Shared-token KYC reuse
Onboard users whose KYC your platform already completed in its own Sumsub tenant, by sharing the verified applicant with UR through a Sumsub share token. UR copies the applicant, validates the data, a
This page documents the shared-token onboarding path: your platform runs a clone of UR's KYC workflow in your own Sumsub tenant, and once a user is approved you share that applicant with UR using a Sumsub share token. UR imports the applicant into UR's Sumsub tenant, validates the shared data, and takes the user to a Live UR Account.
Use this path when you already operate a Sumsub tenant and want to avoid making the user redo KYC. If you do not run a Sumsub tenant, use Managed Custody SDK KYC instead (UR hosts the Sumsub relationship and the SDK runs KYC on the user's device).
Shared-token reuse requires your partnerId to be provisioned for shared-token mode and paired with UR's Sumsub tenant. Coordinate with your dedicated integration channel before pointing production traffic at it. See §8 Prerequisites.
This supersedes the share-token section of API reference: Managed Custody Mode §3. The current implementation uses a dedicated handoff endpoint (/kyc/reuse-share-token), not an applicantId on /create-account, and UR does not re-run the KYC checks; see §1.
1. How it works
The compliance-grade KYC (including NFC document authentication) happens in your Sumsub tenant, running a workflow that UR clones from its own KYC level definitions during onboarding. That is the point at which the regulatory checks are performed.
When you hand the approved applicant to UR:
Your backend mints a single-use share token scoped to UR's Sumsub
clientId, valid for a short TTL (about 20 minutes).Your backend calls UR's handoff endpoint with that token.
UR copies the applicant into UR's own Sumsub tenant (Sumsub "Copy Applicant") and performs data-level validation on the copied profile: completeness (all required fields present), document images and NFC data present, and eligibility (nationality / residency / age / document expiry). UR does not re-run AML/PEP or re-verify the documents; your cloned workflow already did that.
UR returns a conclusive verdict for the attempt:
passed,incomplete(remediable; the user must supply what's missing), or a non-remediable terminal rejection.On
passed, the user signs Form A and your backend submits; UR provisions the account and activates it. Onincomplete, the user completes the missing level in your Sumsub workflow and you hand off again.
Shared-token reuse is available for both account models:
Signing
Partner Auth (EIP-191), identity in X-Ur-Id header
Partner Auth (EIP-191), urId in the request body
Handoff endpoint
POST /api/fma/v1/kyc/reuse-share-token
POST /api/v1/sumsub/reuse-share-token
Poll / submit
/api/fma/v1/kyc/check, /kyc/form-a-info, /kyc/sign-form, /kyc/submit
/api/v1/sumsub/kyc-check, /api/v1/sumsub/kyc-submit
Form A signer
UR-managed custodial wallet
The user's own wallet
The rest of this page describes the Managed Custody flow. See §7 External Wallet variant for the differences.
2. End-to-end sequence
3. Integration walkthrough
3.1 Complete KYC in your Sumsub workflow
Run the user through the UR-cloned workflow in your Sumsub tenant. Present the required data-sharing declaration before identity verification and record the user's agreement; this is the user-facing legal basis for sharing the applicant with UR (see the required KYC disclosure). Only hand off applicants whose review answer is GREEN.
3.2 Create the UR Account
Call POST /api/fma/v1/create-account with X-External-User-Id and the user's email. UR mints (or reuses) the URID, provisions the UR-managed wallet, and returns sessionId, urId, evmAddress, and state = PartnerDataIngestion. You do not pass a Sumsub applicantId here; the applicant is delivered later through the handoff endpoint.
3.3 Mint a share token
After the applicant is GREEN, mint a single-use Sumsub share token scoped to UR's clientId. Mint a fresh token for every handoff attempt; Sumsub invalidates a token once it is used.
3.4 Hand off the applicant
Call POST /api/fma/v1/kyc/reuse-share-token with the shareToken in the body and X-Ur-Id set to the returned urId. UR previews the token (does not consume it), copies the applicant, validates the data, and returns a conclusive verdict synchronously:
status = passed: proceed to Form A.status = incompletewithmissingFields: the user must complete the missing data in your workflow, then hand off again with a new token.A terminal rejection (e.g.
AGE_UNDER_18,NATIONALITY_RESTRICTED): the session cannot proceed; this is non-remediable.
You may either read this synchronous response or consume the fma.kyc.reuse_check.result webhook (see §6.1); both carry the same verdict.
3.5 Poll completeness (optional)
POST /api/fma/v1/kyc/check with sessionId returns the last handoff attempt's verdict as a pure read (complete, state, missingFields). Use it as a fallback to the webhook. Poll every 30 seconds to 2 minutes; stop after /kyc/submit succeeds.
3.6 Render Form A and capture consent
GET /api/fma/v1/kyc/form-a-info?sessionId=… returns the exact Form A text, its formAVersion, and a textHash. Display the text to the user and capture consent. If missingFields is non-empty, do not proceed.
3.7 Sign Form A
POST /api/fma/v1/kyc/sign-form with sessionId and the textHash from the previous step. UR signs Form A with the user's UR-managed custodial wallet and advances the session toward Register.
3.8 Submit
POST /api/fma/v1/kyc/submit with sessionId. UR runs the final register against the banking core asynchronously. When the account activates, UR sends the fma.account.result webhook with status = activated, and the UR Account reaches Live.
4. API reference
All endpoints use Partner Auth (EIP-191) as described in Managed Custody Mode §2.2. User-scoped endpoints identify the user with X-Ur-Id (or X-External-User-Id).
4.1 Create account
POST /api/fma/v1/create-account
email
Yes
User email.
nationality
No
ISO-3166 alpha-3.
residency
No
ISO-3166 alpha-3.
dob
No
YYYY-MM-DD.
documentExpiry
No
YYYY-MM-DD.
Only email (plus the X-External-User-Id header) is required at this endpoint. nationality / residency / dob / documentExpiry are optional here; partners may submit them later via /kyc/sync-data, and the eligibility gates are deferred to /kyc/check, which re-validates over the merged staging snapshot before /kyc/submit.
Identity: send X-External-User-Id. Response data: sessionId, urId, evmAddress, state (PartnerDataIngestion for shared-token).
4.2 Hand off share token
POST /api/fma/v1/kyc/reuse-share-token · identity: X-Ur-Id
shareToken
Yes
Single-use Sumsub share token, forClientId = UR's clientId.
Response data:
status
passed, incomplete, or terminal.
applicantId
The imported applicant ID in UR's tenant (present once copied).
attempt
1-indexed handoff attempt within this session.
missingFields
Present on incomplete; machine paths of the gaps.
The top-level envelope code is the authoritative outcome; data.status mirrors it: passed → code = 0; incomplete → code = 20004; terminal (non-remediable) → a numeric eligibility code (30010 / 30011 / 30012, see §9). On both incomplete and terminal the envelope carries the non-zero business code and data.status set to the matching discriminator; branch on whichever your integration prefers.
4.3 Check
POST /api/fma/v1/kyc/check · identity: X-Ur-Id · body { "sessionId": "…" }
Pure read of the last handoff verdict. Response data: state, complete (bool), missingFields.
4.4 Get Form A
GET /api/fma/v1/kyc/form-a-info?sessionId=… · identity: X-Ur-Id
Response data: formAVersion, text, textHash (echo text/textHash into sign-form). This endpoint returns only these three fields.
4.5 Sign Form A
POST /api/fma/v1/kyc/sign-form · identity: X-Ur-Id · body { "sessionId": "…", "textHash": "0x…" }
Response data: state, signature, signerAddress (UR-managed wallet).
4.6 Submit
POST /api/fma/v1/kyc/submit · identity: X-Ur-Id · body { "sessionId": "…" }
Response data: state, queued. Register proceeds asynchronously; wait for the fma.account.result webhook.
5. Verdict & session state model
One handoff call = one attempt on one share token. Remediable failures are attempts within a living session; only non-remediable verdicts end the session.
passed
Advances to SignFormA
Sign Form A, submit
incomplete
Unchanged; attempt recorded
Complete the missing level in your workflow, mint a new token, hand off again
terminal (non-remediable)
Ends (subsequent handoffs return NO_ACTIVE_SESSION)
Nothing; the user is not eligible
The incomplete loop above is a retry within one onboarding session, driven by you. It is a different mechanism from the operations-initiated retry on Retry KYC, which applies to a user who is already onboarded and starts with POST /api/fma/v1/kyc/session/create after an fma.additional_kyc.required webhook.
6. Webhook contract
Webhooks are delivered per Webhooks (envelope {event, data, timestamp}, EIP-191 signed, X-Webhook-Request-Id idempotency). Subscribe your partnerId to both event types below. Two further events, fma.additional_kyc.required and fma.additional_kyc.completed, cover operations-initiated retries after the user is live; subscribe to those as well and see Retry KYC.
6.1 fma.kyc.reuse_check.result
The async mirror of a handoff attempt's verdict. Sent for passed and incomplete (terminal verdicts are not webhooked in v1). Idempotency business key: {urId}:kyc:reuse_check:{attempt}.
On status = passed, missingFields / requiredLevels are omitted. Partners that only act on remediation may ignore passed.
6.2 fma.account.result
Sent when the account activates after submit. Payload:
7. External Wallet variant
For External Wallet Access, the user holds their own wallet. The flow is the same, with these differences:
Auth / identity: Partner Auth signs the raw body; the
urIdtravels in the request body, not theX-Ur-Idheader.Handoff:
POST /api/v1/sumsub/reuse-share-tokenwith{ shareToken, urId }. The first handoff lazily creates the onboarding session.Poll:
POST /api/v1/sumsub/kyc-check.Submit / Form A:
POST /api/v1/sumsub/kyc-submit; the user signs Form A with their own wallet (there is no custodial signer).
The fma.kyc.reuse_check.result and fma.account.result webhooks are identical.
8. Prerequisites
One-time setup, coordinated with your UR integration channel:
Partner pairing. Your platform and UR are configured as Donor / Recipient in Sumsub so your share tokens are accepted by UR's tenant.
Cloned workflow. UR shares its KYC level definitions and Sumsub clones UR's KYC workflow (GPS / questionnaire / passport + NFC / liveness) into your tenant. Run users through this workflow so the shared applicant carries every field UR validates.
Provisioning. Your
partnerIdis set tofmaMode = shared-tokenwith a per-partnersourceKey, your signer address is registered, and your webhook subscriptions includefma.kyc.reuse_check.resultandfma.account.result.Declaration. Your KYC flow presents the required data-sharing declaration and records the user's agreement before identity verification.
Sandbox. Validate end to end against the testnet base URL before production.
9. Error codes
Business errors are returned with HTTP 200 and a non-zero code in the response envelope.
20003
Invalid / missing parameter (e.g. shareToken, X-Ur-Id)
Fix the request.
20004
Session incomplete
Read missingFields; remediate and hand off again.
20011
Share token invalid
Mint a fresh single-use token (tokens invalidate on use).
30031
No active KYC session
Call /create-account first, or the session already ended (terminal).
30032
Session on the wrong channel
Confirm the partner is in shared-token mode.
30008
Identity already active in another session
The same identity is active in another session; resolve the existing one before reusing the identity.
30010
Nationality restricted (message: NATIONALITY_RESTRICTED)
Non-remediable eligibility rejection; the user is not eligible.
30011
Residency unsupported (message: RESIDENCY_UNSUPPORTED)
Non-remediable eligibility rejection; the user is not eligible.
30012
Age under 18 (message: AGE_UNDER_18)
Non-remediable eligibility rejection; the user is not eligible.
The eligibility codes are numeric in the envelope
code; the string labels (NATIONALITY_RESTRICTEDetc.) appear only in the human-readablemessage.
Last updated