> For the complete documentation index, see [llms.txt](https://docs.ur.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ur.app/api-reference/api/kyc-and-kyb/managed-custody-kyc.md).

# Managed Custody KYC

SDK KYC and shared-token KYC reuse for Managed Custody Mode.

## Create the account and onboarding session

> Mints or reuses the user's URID, provisions the UR-managed wallet, and opens an onboarding session. This is the first step of both Managed Custody KYC paths: the SDK path starts in state ConfirmYourCountryOfResidence and the shared-token path starts in state PartnerDataIngestion. Repeated calls with the same X-External-User-Id while a session is active return the existing session with idempotentReplay set to true.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycCreateAccountRequest":{"type":"object","description":"Body for create-account. The user's identity travels in the X-External-User-Id header, not the body. Do not send a Sumsub applicantId on either KYC path; the applicant is assigned or handed off later.","required":["email"],"properties":{"email":{"type":"string","description":"The user's email address. Must be unique across UR partners; reusing the same email under a different partner returns L1_CONFLICT (code 30015)."},"nationality":{"type":"string","description":"ISO 3166-1 alpha-3 country code. Sending it lets UR fail country gates before the URID is minted."},"residency":{"type":"string","description":"ISO 3166-1 alpha-3 country code of residence."},"dob":{"type":"string","description":"Date of birth in YYYY-MM-DD format."},"documentExpiry":{"type":"string","description":"Passport or ID expiry date in YYYY-MM-DD format."}}},"KycCreateAccountResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"sessionId":{"type":"string","description":"UUID for this onboarding attempt. Constant for the lifetime of the attempt; use it in every subsequent KYC endpoint."},"urId":{"type":"integer","format":"int64","description":"The user's URID, equal to the on-chain NFT token id. Stable for the user's lifetime across all retries."},"evmAddress":{"type":"string","description":"The user's UR-managed wallet address. Form A is signed by this address."},"state":{"type":"string","description":"Session entry state: ConfirmYourCountryOfResidence on the SDK path, PartnerDataIngestion on the shared-token path."},"idempotentReplay":{"type":"boolean","description":"True when an active onboarding session already exists for (partnerId, X-External-User-Id). The existing session is returned unchanged so repeated calls cannot mint duplicate URIDs."}}}}}}},"paths":{"/api/fma/v1/create-account":{"post":{"tags":["Managed Custody KYC"],"operationId":"kycMcCreateAccount","summary":"Create the account and onboarding session","description":"Mints or reuses the user's URID, provisions the UR-managed wallet, and opens an onboarding session. This is the first step of both Managed Custody KYC paths: the SDK path starts in state ConfirmYourCountryOfResidence and the shared-token path starts in state PartnerDataIngestion. Repeated calls with the same X-External-User-Id while a session is active return the existing session with idempotentReplay set to true.","parameters":[{"name":"X-External-User-Id","in":"header","required":true,"schema":{"type":"string"},"description":"Your partner-side user id. This endpoint identifies the user by X-External-User-Id only; there is no X-Ur-Id yet."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycCreateAccountRequest"}}}},"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycCreateAccountResponse"}}}}}}}}}
```

## Create a Sumsub access token

> Issues a short-lived Sumsub access token bound to UR's Sumsub tenant, with a fixed 20 minute TTL. Call this after create-account on the SDK path and relay the token to the Sumsub mobile or web SDK on the user device; re-issue a token bound to the same applicant if the user pauses. Requires an active onboarding session on the SDK data channel; X-Ur-Id must be present.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycSumsubAccessTokenRequest":{"type":"object","description":"Empty body by design. UR reads urId from the X-Ur-Id header and resolves the active onboarding session server-side.","additionalProperties":false,"properties":{}},"KycSumsubAccessTokenResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"token":{"type":"string","description":"Sumsub access token with a 20 minute TTL. Pass this string to the Sumsub SDK constructor on the user device."}}}}}}},"paths":{"/api/fma/v1/kyc/sumsub-access-token":{"post":{"tags":["Managed Custody KYC"],"operationId":"kycMcCreateSumsubAccessToken","summary":"Create a Sumsub access token","description":"Issues a short-lived Sumsub access token bound to UR's Sumsub tenant, with a fixed 20 minute TTL. Call this after create-account on the SDK path and relay the token to the Sumsub mobile or web SDK on the user device; re-issue a token bound to the same applicant if the user pauses. Requires an active onboarding session on the SDK data channel; X-Ur-Id must be present.","parameters":[{"name":"X-Ur-Id","in":"header","required":true,"schema":{"type":"string"},"description":"The user's URID. Required for this endpoint; requests without it fail with INVALID_PARAM."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSumsubAccessTokenRequest"}}}},"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSumsubAccessTokenResponse"}}}}}}}}}
```

## Get the current onboarding session

> Returns the current onboarding state for the user. Poll it after the user starts the Sumsub SDK to detect the SignFormA state; the state can lag the SDK UI by a few seconds because Sumsub webhooks drive the transitions. Returns NO\_ACTIVE\_SESSION (code 30031) when the user has no active session, including after the session reaches a terminal outcome.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycSessionCurrentResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"sessionId":{"type":"string","description":"UUID of the active onboarding session."},"flowId":{"type":"integer","format":"int64","description":"Internal flow id for the session."},"retryLevel":{"type":"integer","format":"int64","description":"Increments each time the user starts a fresh onboarding attempt after a previous one terminated."},"state":{"type":"string","description":"Current session state, in onboarding order: ConfirmYourCountryOfResidence, AddressProof, UnderstandRisk, LiabilityWaiver, IdOrPassportOrOtherIdInformationScan, IdAndLiveness, SignFormA, Register. Treat any unlisted value as an in-progress state. Terminal outcomes are reported by the fma.account.result webhook, after which this endpoint returns NO_ACTIVE_SESSION (code 30031)."},"dataChannel":{"type":"string","description":"Onboarding path of the session; sdk for the Sumsub SDK path. Any other value indicates a partner configuration issue for that path."},"createdAt":{"type":"integer","format":"int64","description":"Session creation time, Unix seconds."},"lastUserActivityAt":{"type":"integer","format":"int64","description":"Last user activity time, Unix seconds."}}}}}}},"paths":{"/api/fma/v1/kyc/session/current":{"get":{"tags":["Managed Custody KYC"],"operationId":"kycMcGetCurrentSession","summary":"Get the current onboarding session","description":"Returns the current onboarding state for the user. Poll it after the user starts the Sumsub SDK to detect the SignFormA state; the state can lag the SDK UI by a few seconds because Sumsub webhooks drive the transitions. Returns NO_ACTIVE_SESSION (code 30031) when the user has no active session, including after the session reaches a terminal outcome.","parameters":[{"name":"X-Ur-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The user's URID. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSessionCurrentResponse"}}}}}}}}}
```

## Hand off a Sumsub share token

> Submits a single-use Sumsub share token minted in your tenant for UR's clientId. UR copies the applicant into its own Sumsub tenant, validates the copied data, and returns a conclusive verdict synchronously; UR does not re-run the KYC checks. Requires an onboarding session created by create-account and a GREEN applicant in your tenant. The envelope code is authoritative: passed maps to 0, incomplete to 20004, and terminal verdicts to eligibility codes 30010 to 30012.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycReuseShareTokenRequest":{"type":"object","required":["shareToken"],"properties":{"shareToken":{"type":"string","description":"Single-use Sumsub share token minted in your tenant with forClientId set to UR's clientId, valid for about 20 minutes. Mint a fresh token for every handoff attempt."}}},"KycReuseShareTokenResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"status":{"type":"string","enum":["passed","incomplete","terminal"],"description":"Conclusive verdict for this handoff attempt. The envelope code mirrors it: passed maps to code 0, incomplete to 20004, terminal to a numeric eligibility code (30010, 30011, or 30012)."},"applicantId":{"type":"string","description":"The imported applicant id in UR's Sumsub tenant. Present once the applicant is copied."},"attempt":{"type":"integer","format":"int64","description":"1-indexed handoff attempt within this session."},"missingFields":{"type":"array","items":{"type":"string"},"description":"Present on incomplete; machine paths of the gaps the user must remediate in your workflow."}}}}}}},"paths":{"/api/fma/v1/kyc/reuse-share-token":{"post":{"tags":["Managed Custody KYC"],"operationId":"kycMcReuseShareToken","summary":"Hand off a Sumsub share token","description":"Submits a single-use Sumsub share token minted in your tenant for UR's clientId. UR copies the applicant into its own Sumsub tenant, validates the copied data, and returns a conclusive verdict synchronously; UR does not re-run the KYC checks. Requires an onboarding session created by create-account and a GREEN applicant in your tenant. The envelope code is authoritative: passed maps to 0, incomplete to 20004, and terminal verdicts to eligibility codes 30010 to 30012.","parameters":[{"name":"X-Ur-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The user's URID. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycReuseShareTokenRequest"}}}},"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycReuseShareTokenResponse"}}}}}}}}}
```

## Check KYC data completeness

> Pure read of the last handoff attempt's verdict on the shared-token path; use it as a fallback to the fma.kyc.reuse\_check.result webhook. Poll every 30 seconds to 2 minutes and stop after submit succeeds. An incomplete session returns code 20004 with missingFields.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycCheckRequest":{"type":"object","required":["sessionId"],"properties":{"sessionId":{"type":"string","description":"The onboarding session UUID returned by create-account."}}},"KycCheckResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"state":{"type":"string","description":"Current session state."},"complete":{"type":"boolean","description":"True when the staged KYC data is complete and the session can proceed."},"missingFields":{"type":"array","items":{"type":"string"},"description":"Machine paths of the fields the completeness check found missing. Empty or omitted when the session is complete."}}}}}}},"paths":{"/api/fma/v1/kyc/check":{"post":{"tags":["Managed Custody KYC"],"operationId":"kycMcCheck","summary":"Check KYC data completeness","description":"Pure read of the last handoff attempt's verdict on the shared-token path; use it as a fallback to the fma.kyc.reuse_check.result webhook. Poll every 30 seconds to 2 minutes and stop after submit succeeds. An incomplete session returns code 20004 with missingFields.","parameters":[{"name":"X-Ur-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The user's URID. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycCheckRequest"}}}},"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycCheckResponse"}}}}}}}}}
```

## Get Form A text and hash

> Renders Form A from the verified KYC snapshot. Callable only while the session state is SignFormA. Display data.text to the user exactly as returned, capture explicit consent, and echo textHash unchanged into sign-form so UR can detect tampering between render and sign.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycFormAInfoResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"formAVersion":{"type":"string","description":"Current Form A template version. Pin this in your audit trail."},"text":{"type":"string","description":"The Markdown text the user must consent to. Render exactly as returned."},"textHash":{"type":"string","description":"0x-prefixed Keccak-256 hash of the UTF-8 text bytes. Pass it back unchanged in sign-form so UR can detect tampering between render and sign."}}}}}}},"paths":{"/api/fma/v1/kyc/form-a-info":{"get":{"tags":["Managed Custody KYC"],"operationId":"kycMcGetFormAInfo","summary":"Get Form A text and hash","description":"Renders Form A from the verified KYC snapshot. Callable only while the session state is SignFormA. Display data.text to the user exactly as returned, capture explicit consent, and echo textHash unchanged into sign-form so UR can detect tampering between render and sign.","parameters":[{"name":"X-Ur-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The user's URID. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."},{"name":"sessionId","in":"query","required":true,"schema":{"type":"string"},"description":"The onboarding session UUID returned by create-account."}],"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycFormAInfoResponse"}}}}}}}}}
```

## Sign Form A

> UR's TurnKey wallet signs Form A on the user's behalf after the user consented in your UI; you prove that authorization by echoing the textHash from form-a-info. Returns FORMA\_TEXT\_MISMATCH (code 20005) if the stored text changed since the last render; re-call form-a-info and resign with the new hash. The returned state stays SignFormA; only submit advances the session.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycSignFormRequest":{"type":"object","required":["sessionId","textHash"],"properties":{"sessionId":{"type":"string","description":"The onboarding session UUID returned by create-account."},"textHash":{"type":"string","description":"The textHash from the latest form-a-info render. Must match the current render or the call fails with FORMA_TEXT_MISMATCH (code 20005)."}}},"KycSignFormResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"state":{"type":"string","description":"Still SignFormA after a successful sign; only submit advances the session."},"signature":{"type":"string","description":"Hex signature over the Form A text, produced by the UR-managed wallet."},"signerAddress":{"type":"string","description":"The UR-managed TurnKey wallet that signed. Equals the evmAddress from create-account."}}}}}}},"paths":{"/api/fma/v1/kyc/sign-form":{"post":{"tags":["Managed Custody KYC"],"operationId":"kycMcSignForm","summary":"Sign Form A","description":"UR's TurnKey wallet signs Form A on the user's behalf after the user consented in your UI; you prove that authorization by echoing the textHash from form-a-info. Returns FORMA_TEXT_MISMATCH (code 20005) if the stored text changed since the last render; re-call form-a-info and resign with the new hash. The returned state stays SignFormA; only submit advances the session.","parameters":[{"name":"X-Ur-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The user's URID. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSignFormRequest"}}}},"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSignFormResponse"}}}}}}}}}
```

## Submit the session for activation

> Hands the session off to UR's background activation scheduler after Form A is signed. Returns state Submitting with queued false as the success signal on these KYC paths; UR then activates the bank account asynchronously and delivers the fma.account.result webhook when the URID reaches Live status.

```json
{"openapi":"3.1.0","info":{"title":"UR API","version":"1.0.0"},"tags":[{"name":"Managed Custody KYC","description":"SDK KYC and shared-token KYC reuse for Managed Custody Mode."}],"servers":[{"url":"https://openapi.ur.app","description":"Production (partner API)"},{"url":"https://uropenapi-qa.ur-inc.xyz","description":"Testnet (partner API)"}],"security":[{"partnerAuth":[]},{"userWalletAuth":[]}],"components":{"securitySchemes":{"partnerAuth":{"type":"apiKey","in":"header","name":"X-Api-Signature","description":"Partner Auth: EIP-191 signature by the partner's registered backend key, with X-Api-Deadline and optional X-Api-PublicKey headers. See the Signature and verify guide."},"userWalletAuth":{"type":"apiKey","in":"header","name":"sign","description":"User wallet auth (External Wallet Access Mode): EIP-191 signature by the user's wallet key, with tokenId, network, hash, and deadline headers. See the Signature and verify guide."}},"schemas":{"KycSubmitRequest":{"type":"object","required":["sessionId"],"properties":{"sessionId":{"type":"string","description":"The onboarding session UUID returned by create-account."}}},"KycSubmitResponse":{"type":"object","properties":{"code":{"type":"integer","description":"Business result code. 0 means success; business rejections return HTTP 200 with a non-zero code."},"message":{"type":"string","description":"Human-readable diagnostic for non-zero codes."},"data":{"type":"object","properties":{"state":{"type":"string","description":"Submitting on success; UR's background scheduler now owns the session."},"queued":{"type":"boolean","description":"False is the success signal for these KYC paths; UR can proceed with bank activation immediately without further user action."},"awaitingPenny":{"type":"boolean","description":"False on the documented KYC paths."}}}}}}},"paths":{"/api/fma/v1/kyc/submit":{"post":{"tags":["Managed Custody KYC"],"operationId":"kycMcSubmit","summary":"Submit the session for activation","description":"Hands the session off to UR's background activation scheduler after Form A is signed. Returns state Submitting with queued false as the success signal on these KYC paths; UR then activates the bank account asynchronously and delivers the fma.account.result webhook when the URID reaches Live status.","parameters":[{"name":"X-Ur-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The user's URID. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-External-User-Id","in":"header","required":false,"schema":{"type":"string"},"description":"Your partner-side user id. Send at least one of X-Ur-Id or X-External-User-Id."},{"name":"X-Api-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Partner Auth EIP-191 signature over the canonical message plus the deadline."},{"name":"X-Api-Deadline","in":"header","required":true,"schema":{"type":"string"},"description":"Unix timestamp in seconds after which the signature is no longer valid."},{"name":"X-Api-PublicKey","in":"header","required":true,"schema":{"type":"string"},"description":"Partner public key used to verify the signature."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSubmitRequest"}}}},"responses":{"200":{"description":"Business result envelope. code 0 means success; business rejections return HTTP 200 with a non-zero code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KycSubmitResponse"}}}}}}}}}
```

{% openapi-webhook spec="ur-api" name="kycResult" method="post" %}
[ur-api](https://4401d86825a13bf607936cc3a9f3897a.r2.cloudflarestorage.com/gitbook-x-prod-openapi/raw/d60738e7b09baa14d700294ed8c6bb9ad4161d224c9b03199485a97e657e2fad.json?X-Amz-Algorithm=AWS4-HMAC-SHA256\&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD\&X-Amz-Credential=dce48141f43c0191a2ad043a6888781c%2F20260726%2Fauto%2Fs3%2Faws4_request\&X-Amz-Date=20260726T064604Z\&X-Amz-Expires=172800\&X-Amz-Signature=20c6740b35995eb6a1e937bb03a4da34332a64864a6c43fac2aef397c0c21fe1\&X-Amz-SignedHeaders=host\&x-amz-checksum-mode=ENABLED\&x-id=GetObject)
{% endopenapi-webhook %}

{% openapi-webhook spec="ur-api" name="kycReuseCheckResult" method="post" %}
[ur-api](https://4401d86825a13bf607936cc3a9f3897a.r2.cloudflarestorage.com/gitbook-x-prod-openapi/raw/d60738e7b09baa14d700294ed8c6bb9ad4161d224c9b03199485a97e657e2fad.json?X-Amz-Algorithm=AWS4-HMAC-SHA256\&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD\&X-Amz-Credential=dce48141f43c0191a2ad043a6888781c%2F20260726%2Fauto%2Fs3%2Faws4_request\&X-Amz-Date=20260726T064605Z\&X-Amz-Expires=172800\&X-Amz-Signature=00c71d7f82065c95d9497c2d6a18b8d797b20e1876873b2c0df8ae9a1e9e863a\&X-Amz-SignedHeaders=host\&x-amz-checksum-mode=ENABLED\&x-id=GetObject)
{% endopenapi-webhook %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ur.app/api-reference/api/kyc-and-kyb/managed-custody-kyc.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
