vellTRDocs
GuidesGoing Live

Going Live

Intermediate15 min

KYC onboarding, production keys, webhook hardening, and the pre-launch checklist.


Moving from sandbox to production changes three things: your integration now touches a real bank and real money, your account has to clear KYC before Kvell will process a live payment, and a handful of hardening steps that don't matter in simulation start to matter a lot. This guide is the checklist for that transition.

1. Complete KYC onboarding

Every merchant account is gated on KYC before it can process live payments. Business information β€” sole trader (TCKN) or company (VKN/MERSΔ°S) β€” is submitted through the merchant dashboard onboarding flow; from your integration, you only need to read the resulting status:

Shell
curl https://api.pay.kvell.group/v1/kyc/status \
  -H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
  -H "X-Timestamp: 2026-04-13T10:00:00Z" \
  -H "X-Signature: <signature>"
JSON
{
  "status": "approved",
  "decidedAt": "2026-04-10T09:00:00Z",
  "sessionId": "f1c9a2e6-b4d4-e8a9-c1f2-a7b8c9d0e1f3",
  "subjectId": "sbj_3c4d5e6f7081"
}

status is one of none, pending, approved, rejected, suspended, or expired. Payments are allowed only when it's approved β€” every other value gets a 403 KVELL-GW-4020 on any payment call. If you integrate on behalf of multiple sellers, each submerchant carries its own KYC decision too β€” see Marketplace & Split Payments.

2. Switch to production credentials

SandboxProduction
Base URLapi.pay.sandbox.kvell.groupapi.pay.kvell.group
API key prefixpk_demo_…pk_live_…
Secret key prefixsk_demo_…sk_live_…

Generate a live key pair from the dashboard, point your base URL at production, and stop sending X-Sim-Scenario β€” it's silently ignored there, but a stray header is a good sign an environment got mixed up somewhere. Testing in the Sandbox has the full sandbox-vs-production comparison.

3. Harden your webhook endpoint

Sandbox testing tends to hide three things that matter immediately in production:

  • Serve HTTPS. Kvell will not deliver to a plain-HTTP URL.
  • Verify X-Kvell-Signature on every request before acting on the body β€” never trust an unsigned or mis-signed payload.
  • Dedupe on X-Kvell-Event-Id, not just event type + payment id. A retried delivery gets a new X-Kvell-Delivery value but the same event id.
  • Respond 2xx within 10 seconds. Acknowledge fast and do slow work (emails, ledger writes, fulfillment) asynchronously β€” a slow handler looks identical to a failing one and triggers a retry.
JavaScript
import crypto from 'node:crypto';

function verifyKvellSignature(rawBody, signatureHeader, secret) {
  // signatureHeader looks like "sha256=<hex>"
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected)
  );
}

Register (or rotate) the endpoint from the merchant dashboard β€” endpoint management runs over your dashboard session, not the HMAC-signed public API. Webhooks has the full event list and retry schedule.

4. Go-live checklist

CheckWhy it matters
Every write carries an idempotency fence (Idempotency-Key header, or transactionId for sessions/charges)A retried request after a timeout must not double-charge or double-payout
Amounts are integer kuruş everywhere15000 means 150.00 TRY β€” a float anywhere in the pipeline is a bug
You branch on 4xxx vs 5xxx error codes4xxx won't succeed on retry (fix the request); 5xxx might (retry with backoff)
Refund path is tested end-to-endIncluding a partial refund and reading refundableAmount back
Reconciliation is wired upGET /v1/transactions to list, POST /v1/transactions/exports for a scheduled CSV/PDF pull β€” match both against your own ledger
You know your rate limitDefault tier is 500 requests/sec sustained, 1,000 burst β€” design retries with backoff, not a tight loop

Example: scheduling a reconciliation export

Shell
curl -X POST https://api.pay.kvell.group/v1/transactions/exports \
  -H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
  -H "X-Timestamp: 2026-04-13T10:00:00Z" \
  -H "X-Signature: <signature>" \
  -H "Content-Type: application/json" \
  -d '{
    "format": "csv",
    "filters": { "from": "2026-04-01T00:00:00Z", "to": "2026-04-30T23:59:59Z", "status": "captured" }
  }'
JSON
{
  "exportId": "exp_3c4d5e6f7081",
  "status": "queued"
}

Poll until it's ready, then download it:

Shell
curl https://api.pay.kvell.group/v1/transactions/exports/exp_3c4d5e6f7081 \
  -H "X-Api-Key: pk_live_a1b2c3d4e5f6g7h8" \
  -H "X-Timestamp: 2026-04-13T10:00:05Z" \
  -H "X-Signature: <signature>"
JSON
{
  "exportId": "exp_3c4d5e6f7081",
  "status": "ready",
  "downloadUrl": "/v1/transactions/exports/exp_3c4d5e6f7081/download",
  "rowCount": 128
}

Run this on a schedule β€” nightly or monthly β€” and diff rowCount and totals against your own order database. A persistent mismatch usually means a webhook was missed, not that Kvell's ledger is wrong.

5. Security notes

  • Your secret key signs every request server-side β€” it never belongs in a browser, a mobile app, or a public repo.
  • Rotate keys from the dashboard if one is ever exposed; the old key stops working immediately.
  • Never log a raw card number. In practice you won't have one to log β€” hosted checkout and tokenization mean a PAN reaches Kvell's card vault and nowhere else in your stack.

Next steps

GuideWhat it covers
Testing in the SandboxConfirm every flow passes before you flip the switch
Marketplace & Split PaymentsSelling on behalf of multiple sellers
API referenceEvery endpoint, request, and response shape