Skip to main content

📄 Overview

A KYC ticket narrows “has this user passed KYC” down to a single application: how far it got, why it did not pass, and when a card can be issued. Tickets and card applications are two layers — first a PASSED ticket, then a card application created from it. Every state change is pushed through the KYC_TICKET webhook, and you can query detail at any time.
KYC ticket state machineKYC ticket state machine
The diagram shows the longest path. PENDING is not a mandatory state: an API-mode ticket may move straight from INIT or NEED_VERIFY to PASSED / REJECTED when the risk engine reports back. Handle whatever status you receive; do not assume states advance one step at a time.

Two application modes

Existing integrations are unaffected: calling guidance-link (action=KYC_GUIDE) without a ticket still works and DCS reuses or creates an H5 ticket for the user when the link is issued. The ticket ID is not returned by that endpoint, but a newly created ticket triggers a KYC_TICKET webhook carrying its kycTicketId, which you can then use with detail. KYC_GUIDE does not read kycTicketId — a value passed there is ignored; to issue a link for a ticket you created yourself, use KYC_APPLY.

Ticket states

Partner-initiated creation is rejected whenever the user already has any in-flight (non-final) ticket, including the H5 ticket DCS creates automatically when an action=KYC_GUIDE link is issued; that automatic creation only looks for an in-flight H5 ticket and ignores API ones, so a user may hold both a partner-created API ticket and a DCS-created H5 ticket at the same time. The automatic ticket occupies the single in-flight slot and is never expired or reclaimed, so if the user opened a guidance link but never submitted, apply-kyc for that user keeps returning KYC_TICKET_IS_IN_PROCESSING — do not issue a KYC_GUIDE link first if you plan to use API mode. Re-creating with the same kycTicketRef: apply-kyc always returns KYC_TICKET_REF_NOT_UNIQUE (look the ticket up by kycTicketRef if a request timed out); apply-kyc-h5 returns the original ticket while it is still INIT (network-retry case), and from PENDING onwards it is rejected by status (KYC_TICKET_IS_IN_PROCESSING / KYC_TICKET_ALREADY_PASSED / KYC_TICKET_VERIFY_FAIL); a new kycTicketRef while an in-flight ticket exists returns KYC_TICKET_IS_IN_PROCESSING.

Four failure paths

API mode: encrypted career info

encryptedKycCareerInfo = Base64( AES-GCM( key = Base64Decode(SecretKey), iv = Base64Decode(iv), plaintext = the careerInfo JSON ) ) with a 128-bit tag. DCS looks up your SecretKey from the X-DAPI-API-KEY header to decrypt and never logs the plaintext. Send at least one of careerInfo / encryptedKycCareerInfo; the ciphertext wins if both are present.

Issuing a card from a ticket

Once the ticket is PASSED, call POST /card/v1/virtual-card/apply with cardApplyMode=NORMAL and kycTicketId; kycInfo is no longer needed inline. Omitting cardApplyMode means COMPLETE, identical to the previous behavior. Tickets are not bound to a card product: the KYC outcome is per user, so one PASSED ticket can back several cards of any product for that user. The apply and detail responses now include kycTicketId / cardApplyMode. See Issuing cards.
  • The secret in the link is single-use and valid for 300 seconds; opening the page starts a 1-hour session. Request a new link after expiry; the ticket is unaffected.
  • The face page may return PERMISSION_DENIED (link used or expired), SUMSUB_TOKEN_NOT_FOUND (session is not a face session or the ticket does not belong to the user) or KYC_TICKET_STATUS_NOT_LIVENESS (ticket already submitted or final). The first two are returned only by the face page to the browser, never in a partner API response — they are listed here to help you triage user reports. KYC_TICKET_STATUS_NOT_LIVENESS is also returned to you directly when you call guidance-link with action=KYC_LIVENESS and the ticket is already PENDING or final.

Error codes

Codes are returned with the module prefix of the endpoint (e.g. CARD_KYC_TICKET_NOT_FOUND); match on the actual string. For REJECTED, API-mode tickets carry the risk-engine codes, the same as for card applications, see the application data dictionary; H5-mode tickets use KYC_REJECTED, with the specific reason in errorMessage, or TIMEOUT when the user did not finish within the Sumsub time limit — you can prompt them to start again.
  • POST /kyc-ticket/v1/apply-kyc-h5 / POST /kyc-ticket/v1/apply-kyc / GET /kyc-ticket/v1/detail: see the API reference
  • POST /redirect/v2/guidance-link: new kycTicketId, categoryId and action=KYC_APPLY / KYC_LIVENESS, see H5 guidance pages
  • KYC_TICKET webhook: see Webhook & WebSocket