XKOVA Docs

Errors and Correlation IDs

XKOVA returns a single, stable error envelope on every failure. This catalog lists the 155 live error codes by HTTP status. It is generated from the contract, so it stays in step with the API.

Every error response carries the same envelope: a stable code, a human readable message, an optional details object, and a correlation_id to quote when contacting support. The retry column states whether retrying can change the outcome.

Retry semantics

ValueMeaning
noneNot retryable. Retrying will not change the outcome.
immediateRetry right away, for example after a transient network blip.
headerWait the seconds given in the Retry-After response header.
detailsWait the seconds given in details.retry_after_seconds.

400 4

CodeRetryWhen it occurs
faucet_chain_not_supportednoOn-ramp under the faucet profile targeted a non-Fuji chain. Faucet is testnet-only by construction.
ramp_profile_not_allowednoCaller passed x-ramp-profile header naming a profile not in the tenant's ramp_profiles_allowed list. Production tenants reject any non-default value here.
stripe_request_errornothe billing provider API rejected caller-supplied input deterministically (unknown price id, malformed customer params, invalid resource state). Retrying with the same payload will not help; fix the input.
webhook_malformednoAn inbound provider webhook signature envelope or body is structurally malformed, an un-splittable t=…,v1=… header, a non-numeric timestamp, a body that is not parseable JSON, or a payload missing contract-required fields. Deterministic; retrying the same bytes will not help.

401 13

CodeRetryWhen it occurs
active_scope_unavailableyes (immediate)JWT verifies and the operator has staff rows, but the X-Active-Scope header points at a scope the operator cannot act in. Typically a stale localStorage pointer from a recent account switch, clients should clear their cached scope and re-pick from /v1/me/scopes before retrying.
member_auth_invalid_credentialsnoPOST /v1/member-auth/sign-in with email + password that XKOVA IDP rejected.
member_auth_mfa_challenge_invalidnoThe mfa_token supplied to POST /v1/member-auth/sign-in/mfa/verify is unknown, expired, already used, or out of attempts. Start the sign-in over.
member_auth_mfa_code_invalidnoThe TOTP code supplied to POST /v1/member-auth/sign-in/mfa/verify was rejected. The challenge is still open, the member can retry the code until the attempt cap is reached.
member_auth_refresh_replay_detectednoPOST /v1/member-auth/refresh received a refresh token whose hash matches a member_sessions previous_refresh_token_hash, token was already rotated. The whole refresh-token family is revoked at the same time.
member_auth_session_revokednoA sensitive endpoint (sign-out / profile mutation / refresh) loaded a member_sessions row that is revoked. Caller must start a fresh sign-in.
member_auth_token_invalidnoEmail-link token (signup / magic-link / password-reset / refresh) does not exist or was already consumed.
request_signature_invalidnoThe api key has require_signed_requests enabled (opt-in #14) but the request's X-Signature HMAC header was missing/malformed, outside the ±5-minute replay window, or did not match (tampered body/path/method, or wrong signing secret). The same body+signature will never pass, fix the signature, don't retry verbatim. Implemented by RequestSignatureInvalidError.
stripe_webhook_signature_invalidnothe billing provider webhook signature header failed verification. Check that on the receiver matches the signing secret of the registered webhook endpoint in the billing provider.
unauthorizedyes (immediate)No valid authentication credential on the request.
unauthorized_account_holdernoSSO assertion arrived with no matching federated_identity row and JIT is disabled for this tenant.
webhook_signature_invalidnoAn inbound provider webhook (banking / coinbase / conversion / engine / screening / XKOVA IDP hook) arrived with a missing, stale (clock drift outside the replay window), or tampered HMAC signature. The caller failed to authenticate; the same body+signature will never pass, so the provider should stop retrying.
workspace_scope_unavailableyes (immediate)The operator scope resolved, but the X-Workspace-Id header points at a workspace that is inactive/deleted or belongs to a different tenant. Typically a stale localStorage pointer from a same-user account switch, clients should clear their cached workspace id and retry (the api then defaults to the tenant's sandbox workspace). A cross-tenant workspace id is always rejected here, so this is never a data leak.

403 25

CodeRetryWhen it occurs
activation_code_tenant_mismatchnoActivation code was issued with a scoped_tenant_id that doesn't match the caller's tenant. Distinct from _invalid so support can tell apart "you typed the wrong code" from "this code was issued to someone else."
authorization_deniednoThe value movement was denied by the workspace authorization policy (the TAP firewall), a tenant-configured rule, not a sanctions/screening block. Reason code in details. Fail-closed: a missing/uncommitted policy denies everything.
bootstrap_requires_staff_jwtnoThe first admin role grant on a tenant must be authorized via staff JWT auth (recorded as bootstrap_via='staff_jwt'). A request authenticating via an api-key-role grant attempting to bootstrap the first admin gets this 403.
compliance_blockednoPre-sign compliance pipeline hard-blocked the tx. Reason code in details.
contacts_disablednoTenant has disabled the recipient directory (contacts) feature via tenants.contacts_disabled=true. Writes return this error; reads may still proceed with cached results.
csrf_missing_or_invalidyes (immediate)Double-submit CSRF cookie/header pair missing or mismatched on a cookie-authed request. Cleared by reloading the page so the client can pick up a fresh CSRF token.
demo_login_disablednoPOST /v1/demo-login was called but the demo-login feature is off in this environment. The effective gate is =true OR xkovaEnv ∈ {local,test,demo}; staging/prod return this unless the public demo deployment explicitly opts in.
feature_not_enablednoTenant lacks the entitlement required to invoke this surface. details.feature carries the feature key (e.g. 'treasury'); the workspace-console renders this as a "redeem an activation code to unlock <X>" prompt.
first_use_requiredyes (immediate)Account holder has not completed first-use consent for all required tenant policies; outbound ops blocked until accepted.
member_auth_account_holder_inactivenoSign-in succeeded against XKOVA IDP but the bound account_holder is in a non-active state (held / closed).
member_auth_email_not_confirmednoSign-in attempted before the member clicked the confirmation link in their email.
member_auth_mfa_requirednoA member-scoped request presented an identity token that has not passed a second factor, for a member who has a verified MFA factor. Complete the sign-in MFA challenge (POST /v1/member-auth/sign-in/mfa/verify) and use the token it returns.
member_auth_origin_not_allowlistednoBrowser-side caller of /v1/member-auth/* presented a public api key but the request Origin header is not in tenant_member_auth_allowed_origins.
not_a_treasury_signernoCaller has a treasury role but is not a member of this specific Safe wallet, so cannot draft, sign, or execute on it.
partner_scope_deniednoPartner-scoped API key attempted an action that requires tenant-scoped authorization (sensitive actions cannot be performed from a partner key).
platform_partner_requirednoA partner-scoped admin caller attempted a cross-tenant PLATFORM operation, the production-access admin review queue, but its partner is not the XKOVA platform partner (partners.is_platform = false). Review is XKOVA-central; a non-platform partner cannot list, approve, or reject another partner's tenants' requests.
role_deniednoStaff role does not permit this action (per §21.6 override matrix). Audit event written.
rwa_document_not_entitlednoA GET /v1/rwa/tokens/{id}/documents/{docId} for a GATED document named a grantee with no ACTIVE document-entitlement grant on the token. Gated documents are reachable only by a recipient the issuer granted via POST.../document-entitlements. Grant the recipient, or request a public document.
self_approval_forbiddennoSeparation of duties: the staff member who INITIATED an approval request attempted to also attest it toward the same quorum, while the clause is satisfiable WITHOUT them (the group has enough other active members to meet the threshold). A solo-owner clause that cannot resolve without the initiator is still allowed.
step_up_requiredyes (immediate)Action requires step-up re-auth. Two distinct trigger contexts: (1) Operator JWT path, staff session is AAL1 with TOTP enrolled, sensitive actions (API-key rotation, HMAC rotation, staff-role change, fee-schedule commit, custody config, merchant-role approval above threshold, high-value tx, treasury mutations) require a fresh AAL2 challenge. (2) API-key path, an api-key was minted without mfa_elevated_at_creation: true and is now hitting an AAL2-gated route (notably treasury mutations). Resolution: rotate the key under an AAL2-elevated operator session so the replacement carries the MFA-elevation flag for its lifetime.
system_key_revoke_deniednoRole-gated revocation refused: the target api key is system: true (XKOVA-internal demo / dev seeder). Operator JWTs can revoke; api-key-role auth cannot.
tenant_read_onlynoTenant is in read-only mode (billing past due T+30 per §10.1 dunning). Inbound receive + staff ops still work.
tokenization_not_authorizednoA tokenization mutation (e.g. POST /v1/tokenization/erc20) was called by a correctly-scoped tenant key that lacks the tokenize_admin role. Distinct from partner_scope_denied (wrong scope), this is a role gap inside the right tenant. Grant the key the tokenize_admin role and retry.
treasury_role_deniednoCaller is authenticated but lacks the treasury role required for this operation (treasury_admin / treasury_signer / treasury_viewer).
verification_requirednoThe account holder must complete identity verification (KYC/KYB) before a signing session is issued; the account verification_status is not cleared (unverified / in_progress / awaiting_approval / rejected / expired).

404 8

CodeRetryWhen it occurs
email_domain_not_foundnoNo sending domain with the given id exists for the caller's tenant (never created, already removed, or belongs to another tenant, RLS-scoped).
not_foundnoReferenced resource does not exist, or is not visible to the caller under RLS.
revenue_treasury_change_not_foundnoThe revenue-treasury change request does not exist (or is not in the caller's tenant).
tokenization_access_registry_not_setnoA POST /v1/tokenization/tokens/{id}/access-list/{add,remove} request targeted a token that has no access registry, it was deployed without accessControl, so transfers are open and there is no allow/deny list to manage. Attach a registry first via POST /v1/tokenization/tokens/{id}/access-registry (or deploy a token with accessControl: allowlist|denylist), then manage membership here.
tokenization_authority_not_foundnoA token-authority route (/v1/tokenization/authorities/{id}*) named an authority id that does not exist in the caller's workspace, a genuine miss or a cross-workspace id (RLS + the workspace filter make the two indistinguishable by design). Register the authority, or use an id from GET /v1/tokenization/authorities.
treasury_counterparty_not_foundnoNo counterparty exists for the given id under the active tenant scope.
treasury_transfer_not_foundnoNo treasury transfer exists for the given id under the active tenant scope.
treasury_wallet_not_foundnoNo treasury wallet exists for the given id under the active tenant scope.

409 36

CodeRetryWhen it occurs
activation_code_already_redeemednoActivation code was previously redeemed by some tenant. Codes are one-time-use; issue a new code.
asset_schema_fork_conflictnoA fork of a curated platform template into a workspace draft named a new_schema_key that already exists as a schema in the target workspace (the workspaceKeyUniq partial unique on asset_schemas). Supply a distinct key to fork the same template a second time.
attester_already_registerednoRegistering an attester collided with an already-ACTIVE registration for the same (workspace, scope, address). Revoke the existing registration first, or reuse it. details.attester_address names the address.
attester_revokednoAn attested append-log entry named an attester whose registration has been revoked, or a revoke targeted an already-revoked registration. A revoked party can no longer attest. Register a fresh attester.
authorization_requires_approvalnoThe value movement matched a require_approval policy rule and cannot proceed until its compound approver quorum signs off.
break_glass_requirednoA governed action requires quorum but the configured approval clause is UNSATISFIABLE (the approver group has fewer active members than the clause threshold), so the request would strand and never resolve. The action cannot proceed normally; an owner must arm a break-glass session (the time-boxed emergency-access safety valve) to bypass it.
compliance_case_already_opennoThe payment already has an OPEN compliance case. Flagging again would stack a second case on one subject and split the review trail across two records; act on the existing case instead.
compliance_case_openyes (immediate)An operator-visible compliance case (state open/investigating/escalated) is targeting this transaction. Signing URL issuance is blocked until the case is transitioned to closed or sar_filed.
conflict_statenoRequested state transition is not valid from the resource's current state.
earned_beneficiary_unsetnoThe fee resolver found a committed earned rule with no (or zero) on-chain beneficiary address. Earned rules are stamped to the revenue treasury at commit, so this indicates a schedule committed without a revenue treasury; the resolver refuses to burn fees to the zero address.
email_domain_conflictnoThe sending domain is already registered for this tenant. Re-adding an existing domain is a no-op conflict; verify or remove the existing one.
force_cancel_blockednoThe operator force-cancel of a post-sign payment was refused by the on-chain reality check. The transfer either executed successfully on-chain (money moved, never cancellable) or its outcome cannot be resolved yet (still in flight / RPC lag). Only a reverted or never-submitted transfer can be force-cancelled; the operator retries once the chain settles.
idempotency_conflictnoSame Idempotency-Key replayed with a different request body (within 24h window).
insufficient_fundsnoCore-banking declined a debit because the source bank account lacks the funds. Distinct from insufficient_balance (the on-chain wallet check).
invalid_statenoA service rejected the requested transition: the source row exists but is not in a state from which the requested action is allowed (e.g. PATCH state=closed → state=active on an account_holder; confirmAvatar with an object_key whose tenant prefix does not match the holder; jitProvision into a tenant whose oidc_provider_kind is misconfigured for the field passed). The error message names the precondition that failed.
rebind_invalid_statenoA schema-rebind ceremony stage was invoked from the wrong lifecycle state, e.g. attest before a passing dry-run, or commit before attest. The ceremony advances proposed -> dry_run_passed -> attested -> committed. details.{current,required} name the states. Run the missing prior stage.
revenue_treasury_already_designatednoThe workspace already has a different designated revenue treasury. Re-pointing revenue to another Safe requires the quorum-gated revenue-treasury-change flow, not a second designation.
revenue_treasury_change_in_progressnoA non-terminal revenue-treasury change request already exists for the workspace; concurrent re-links are blocked. Cancel or complete it first.
revenue_treasury_change_state_invalidnoThe change request is in a state incompatible with the requested action (signing a non-open request, applying one below threshold, or acting on an applied/cancelled request).
revenue_treasury_in_usenoTried to decommission a Safe that is the active revenue treasury of a workspace with a committed earned-fee schedule routing to it. Re-link revenue to another Safe first (guards against orphaning earned-fee routing to a dead Safe).
revenue_treasury_requirednoTried to commit a fee schedule carrying earned-fee rules (institution / external / issuer) but the workspace has no active revenue treasury to route them to. Establish + designate a treasury Safe first, revenue cannot be enabled without a treasury (the hard gate).
signer_mpc_not_provisionedyes (details)One or more requested signers have not yet provisioned their Thirdweb in-app MPC wallet (state != active). Prompt the staff member(s) to set up their treasury signing key first. details.staff_ids carries the unprovisioned ids.
token_already_boundnoBinding a published asset schema to a token (POST /v1/rwa/tokens/{id}/schema-binding) was rejected because the token is already bound to a DIFFERENT asset-schema version. A token binds one schema version, set once; changing it is the quorum-gated schema-rebind ceremony, not a re-bind. Re-binding to the SAME version is idempotent.
token_transfer_frozennoA platform-relayed NFT transfer was rejected because the token is under an ACTIVE transfer freeze (e.g. a corporate action in flight). The freeze is a hard gate checked before the graded door. Retry after the freeze is lifted.
tokenization_access_registry_existsnoA POST /v1/tokenization/tokens/{id}/access-registry request targeted a token that ALREADY carries an access registry (access_registry_proxy_address is set, at deploy with accessControl, or by a prior enable). A token has exactly one registry; the attach is a one-time act. Manage membership via /access-list/{add,remove}; the registry itself is not replaceable through this surface. details.access_registry_proxy_address names the existing registry.
tokenization_authority_already_verifiednoA proof-of-possession was submitted for an authority that is already verified (verified_at set). A server_key is provable exactly once, its first successful proof is permanent; a safe authority is verified at registration. Returned instead of silently re-stamping verified_at, so a replayed proof is observably a no-op conflict.
tokenization_invalid_state_transitionnoA lifecycle transition was requested from a state that does not allow it, e.g. POST /v1/tokenization/tokens/{id}/deprecate on a token that is not active. Only an active, rails-registered token can be deprecated; a token still deploying or in a failed state has no rails entry to retire. The details.current_state field names the state the token was actually in.
tokenization_operation_already_relayednoA signature (or a duplicate relay) was submitted for a tokenization operation that has already collected its Safe threshold and been relayed to Engine, its Safe.execTransaction is in flight or terminal. Returned instead of burning relayer gas on a guaranteed-revert resubmission; the on-chain Safe nonce is the backstop. Poll the operation for its terminal status.
tokenization_operation_pendingnoA token deploy was requested while a prior deploy operation for the same workspace is still in flight (a tokenization_operations row, kind=deploy, in a non-terminal state). Returned instead of enqueuing a second on-chain deploy that would burn relayer gas. Replaying the SAME request body returns the original operation via the idempotency cache; a DIFFERENT body is rejected here until the in-flight deploy resolves.
treasury_asset_already_registerednoAn asset for this (chain, token_address) is already in the registry.
treasury_counterparty_address_collisionnoThe address is already mapped to a different counterparty on this chain. UNIQUE(chain_id, address) on treasury_counterparty_addresses prevents ambiguous tax aggregation.
treasury_safe_nonce_exhaustednoSafe nonce reservation collided with on-chain state in a way the API cannot resolve automatically (rare; indicates the Safe was used outside XKOVA's API). Operator must reconcile manually before drafting more transfers.
treasury_signature_already_submittednoThis signer has already submitted a signature for this transfer.
treasury_threshold_not_metyes (none)Execute called but signature count is below the Safe's threshold. details.signature_count and details.threshold are populated.
treasury_transfer_nonce_blockednoRefused execTransaction submission because on-chain Safe.nonce() doesn't match this transfer's safe_nonce. Safe consumes nonces in strict order, submitting at a higher nonce reverts on chain with GS026. details.transfer_safe_nonce, details.on_chain_safe_nonce, and details.blocking_transfer_ids are populated; the operator must execute or cancel the blocking transfers before this one can run.
treasury_transfer_state_invalidnoThe transfer is not in the state the requested operation requires (e.g., execute called on a transfer not in ready_to_execute, or signature submitted on a transfer in submitted/confirmed). details.current_state and details.required_states are populated.

410 1

CodeRetryWhen it occurs
member_auth_token_expirednoEmail-link token is past its TTL. Caller should request a fresh link.

422 47

CodeRetryWhen it occurs
account_frozennoCore-banking declined a mutating call because the account is frozen, locked, or closed. Surfaced on ramp deposit/withdrawal so the failure is actionable rather than a generic 500. Mirrors banking-core's account_frozen.
activation_code_expirednoActivation code carries an expires_at in the past.
activation_code_invalidnoActivation code didn't match any known row (sha256 lookup miss). The plaintext code may be mistyped.
attester_scope_mismatchnoA registered attester pinned to a SPECIFIC token was used to attest a fact on a different token. A token-pinned attester signs only for its token; register the attester workspace-wide (no token) or for the right token.
attester_signature_mismatchnoAn attested append-log entry carried a signature that, recomputed server-side over the canonical EIP-712 fact digest, recovered to a DIFFERENT address than the named registered attester. The entry is NOT this attester's; no append-log row and no provenance version are written. details.{recovered,expected} name the addresses. Sign the exact {tokenRef, fieldPath, payloadDigest} digest with the registered key.
authorization_invalidnoThe submission layer could not confirm the signed authorization for this value movement (missing, expired, already consumed, or the rebuilt intent no longer binds the signed digest). The relay is refused.
billing_tier_not_chargeablenoTenant plan has no the billing provider subscription path. Either the plan is base (free tier, no subscription is created by design) or a paid plan is missing its STRIPE_PRICE_*_* env config. Operator must either upgrade the tenant to a chargeable plan or fix the deploy config.
chain_not_allowednoThe optional per-request chain_ref override named a chain this resource cannot use (it does not match the workspace's pinned chain / the referenced wallet or token's chain). Omit chain_ref to use the resolved chain.
email_domain_invalidnoThe submitted sending domain was rejected by the provider as invalid (malformed domain name, or a domain the provider will not accept). Deterministic; the same domain will not pass on retry.
member_auth_invalid_emailnoEmail field on sign-up / sign-in / etc. is not a parseable address.
member_auth_redirect_url_not_allowlistednoredirect_to URL on a sign-up / magic-link / password-reset request is not in the tenant's tenant_member_auth_redirect_urls allowlist.
member_auth_weak_passwordnoPassword supplied to sign-up / password-reset/confirm / profile (PATCH) does not meet XKOVA IDP policy.
nft_not_owned_by_safenoA treasury-Safe NFT transfer was drafted for an NFT the Safe does not own, the on-chain ownerOf (ERC-721) / balanceOf (ERC-1155) read at draft shows the Safe is not the holder, so no op is created. Draft a transfer only for an NFT the Safe currently custodies (see GET /v1/treasury/wallets/{id}/nft-holdings).
off_ramp_burn_unverifiednoThe client-supplied off-ramp burn transaction could not be verified on-chain, so the fiat conversion was refused. The burn must be a successful transfer of the exact stablecoin amount from the source wallet to the partner redeem address, not previously consumed by another off-ramp. Reason in details. Terminal (a mined receipt will not change), build + submit a correct burn.
rebind_attestation_invalidnoA schema-rebind commit re-verified the stored EIP-712 attestation against the recomputed proposal digest and the signature recovered to a DIFFERENT address than the one recorded at attest, the proposal (transform, token set, or dry-run result) was tampered with after attestation. The commit is refused and the ceremony is poisoned to failed. Open a fresh ceremony.
rebind_not_forward_onlynoA schema-rebind ceremony was proposed against a target asset_schema_version that is not a strictly-LATER published version of the SAME asset schema, or against a token not currently bound to the source version, a binding can only move forward. details.reason names the failing leg (different_schema, target_not_published, source_not_published, not_forward). Pick a later published version of the same schema.
revenue_treasury_change_target_invalidnoThe proposed re-link target is invalid: the workspace has no current revenue treasury to change from, or the target Safe is not active / not in the same workspace / is the Safe already designated.
revenue_treasury_not_activenoTried to designate a treasury Safe as the revenue treasury, but the Safe is not active (still deploying or decommissioned). Only an active Safe can hold pushed earned fees.
role_not_allowed_for_service_accountnoA POST /v1/tokenization/tokens/{id}/roles/grant named a target authority whose purpose is service_account. A service account is an integrator-held automation EOA that may hold ONLY a policy-capped MINTER, granted via POST /v1/tokenization/tokens/{id}/policies (the Zodiac scoped role). The raw grantRole path produces an UNCAPPED on-chain role, so it is forbidden for a service account regardless of which role was named (even ). Grant the capped MINTER via the policies endpoint instead. 422.
rwa_document_hash_mismatchnoA POST /v1/rwa/tokens/{id}/documents/confirm declared an expected content hash that does not match the SHA-256 the server recomputed from the uploaded object bytes. The integrity anchor is server-derived; a mismatch means the bytes are not what the caller claims, so no rwa_media_objects row is written and no metadata commit occurs. Re-upload and confirm with the correct hash.
rwa_relationship_cyclenoCommitting a token→token relationship field where the target token is already reachable from the source token, or the edge is a self-reference. token→token edges must stay acyclic so the resolver can compose linked assets without infinite expansion. Remove the edge that would close the loop.
rwa_relationship_external_ref_invalidnoAn external relationship reference was malformed: its system is not in the field's allowed-system enum, or its id failed the field's RE2 id pattern. Use an allowed system + a well-formed id.
rwa_relationship_target_not_foundnoA relationship field referenced a target that does not resolve: a token target that is not a live customer token in your workspace (missing, cross-workspace, or deprecated/terminal), or an identity target that is not an existing in-tenant subject (account-holder / token-authority). Reference a live in-workspace target.
rwa_schema_unsafenoPublishing an RWA asset_schema_version (palette-compiled OR raw-upload) failed the publish gate: the schema embeds a construct that could DoS validation, exfiltrate via an external reference, or smuggle an unbounded structure. details.reason names the failing class (meta_schema, external_ref, unbounded_string, unbounded_array, open_object, disallowed_format, redos_pattern, combinator_breadth, node_count, depth, precompile). Fix the schema (the palette path is safe-by-construction) and re-publish.
token_deploy_failednoAn on-chain token-deploy transaction reverted, Engine reported the tx mined but onChainTxStatus == 0, or the chain receipt status is reverted. The customer_tokens row is left in a non-active state for operator review; the operation row records the failure. Deterministic for the same inputs, so it is not auto-retried.
tokenization_authority_kind_mismatchnoA proof endpoint (/proof-challenge or /proof) was called on an authority whose kind does not support it, the proof-of-possession flow applies ONLY to a server_key authority. A safe authority's control is its on-chain N-of-M owner set (verified at registration), so it has no PoP step. details.kind names the authority's actual kind.
tokenization_authority_not_verifiednoA POST /v1/tokenization/tokens/{id}/policies request named a server_key authority that has not completed proof-of-possession (verified_at is null). A server key must prove control of its EOA before it can hold a scoped mint role, otherwise an unattended capped-mint authority could be granted to an address the customer does not control. Verify the authority via POST /v1/tokenization/authorities/{id}/proof first, then re-issue.
tokenization_operation_not_signablenoA signature submitted to POST /v1/tokenization/tokens/{id}/operations/{opId}/signatures was rejected: the recovered signer is not an active owner of the token's admin Safe (checked against on-chain Safe.getOwners()), the supplied signer address does not match the ecrecover result, or the signature is malformed. Only the admin Safe's N-of-M owners can authorize a mint. Re-sign with an owner key.
tokenization_policy_exceedednoA POST /v1/tokenization/tokens/{id}/policy-mint requested an amount greater than the confirmed on-chain per-call cap for the (token, authority) mint policy. Rejected BEFORE relaying, the Roles Modifier would revert it on-chain anyway; this fail-fast avoids the wasted relay. The cap is read from the DB mirror (defense-in-depth; the on-chain revert is authoritative).
tokenization_policy_not_setnoA POST /v1/tokenization/tokens/{id}/policy-mint request named a (token, authority) pair with no CONFIRMED mint-cap policy, none was set, or the set_mint_policy operation has not yet confirmed on-chain. Without the on-chain Roles scope the relayed mint would revert NotAuthorized, so this fails fast. Set a policy via POST /v1/tokenization/tokens/{id}/policies and wait for it to confirm before minting.
tokenization_proof_invalidnoA proof-of-possession submitted to POST /v1/tokenization/authorities/{id}/proof was rejected: the challenge token's HMAC failed verification (tampered / forged / minted with a rotated secret), the challenge expired, it was minted for a different authority or workspace, or the signature did not recover to the authority's registered address. Control of the server-key EOA is the gate on verifying it. Re-issue a fresh challenge and sign the returned message with the authority's key.
tokenization_raw_call_invalidnoA POST /v1/tokenization/tokens/{id}/raw-safe-tx (the advanced governance escape hatch) named a contract outside the token's own three control contracts (token / access_registry / roles_modifier), a function not in the resolved contract's committed ABI, or args whose count/shape does not match the function inputs. The escape hatch is scoped to ONE of the token's own contracts (server-resolved address; an arbitrary address is impossible) and the inner calldata is encoded from the committed ABI, so a call that cannot encode against a real function is rejected before any SafeTx is built. Re-issue with a contract from the enum + a function in its ABI + the correct arg count.
tokenization_recover_target_activenoA POST /v1/tokenization/tokens/{id}/recover request targeted an account that still has access to the token (accessRegistry.hasAccess(account) is true). recoverTokens is a compliance claw-back that applies ONLY to a blocked account, the on-chain pre-condition is hasAccess == false, else it reverts RecoveryOnActiveAccount. Returned as a pre-flight so the request fails fast rather than relaying a guaranteed-revert SafeTx. Block the account first (denylist: /access-list/add; allowlist: /access-list/remove), then recover. details.account names the target.
tokenization_relay_expirednoA POST /v1/tokenization/tokens/{id}/gasless/relay request carried a forwardRequest whose deadline has already passed. The on-chain forwarder rejects an expired request (ERC2771ForwarderExpiredRequest), so this fails fast rather than relaying a guaranteed revert. Build a fresh transfer-request (which stamps a new deadline + current nonce) and re-sign.
tokenization_relay_invalid_signaturenoA POST /v1/tokenization/tokens/{id}/gasless/relay request carried a forwardRequest + signature whose EIP-712 signature did not recover to forwardRequest.from (tampered request, wrong signer, or malformed signature). The holder's signature is the sole authorization for moving their balance; the on-chain forwarder rejects a mismatch (ERC2771ForwarderInvalidSigner), so this fails fast before relaying. Build a fresh transfer-request and re-sign it verbatim.
tokenization_relay_wrong_targetnoA POST /v1/tokenization/tokens/{id}/gasless/relay request carried a forwardRequest whose inner-call target (to) is not this token's proxy. The relay surface is scoped to the single token in the path; a request targeting a different contract is rejected before relay so the integrator's relay key cannot sponsor an arbitrary call through the shared forwarder. Build the transfer-request via this token's.../gasless/transfer-request endpoint and re-sign.
tokenization_unknown_rolenoA POST /v1/tokenization/tokens/{id}/roles/{grant,revoke} request named a role that is not in the closed ERC20X role set (,,,,,, ). Rejected before any SafeTx is built, a grantRole of an unknown role id would silently create a new, never-checked role on-chain rather than revert. Re-issue with a known role name.
tokenization_unsupported_decimalsnoPOST /v1/tokenization/erc20 requested a decimals other than 18. ERC20X does not override OpenZeppelin default decimals, so every deployed token is 18-decimal on-chain; a non-18 request would silently produce an 18-decimal token. Rejected up-front. Re-issue with decimals: 18.
transfer_eligibility_deniednoA platform-relayed NFT transfer (holder-gasless OR treasury-Safe) was rejected by the graded transfer door before any Engine relay/execute: a declarative eligibility rule on the asset schema denied it. details.reason_code names the failing rule (holder_cap_exceeded, jurisdiction_not_allowed, jurisdiction_denied, accreditation_required, lockup_active). Satisfy the rule (e.g. attest the recipient's accreditation via the holder-eligibility store, or wait out the lockup) and retry.
treasury_asset_not_registerednoThe asset_token_address is not in treasury_supported_assets for this chain. A treasury_admin must register the ERC-20 first via POST /v1/treasury/assets.
treasury_chain_unsupportednoThe requested chain_id has no Safe ProxyFactory + Mastercopy entries in supported-chains.ts. Treasury is not supported on this chain yet.
treasury_replicate_target_invalidnoPOST /v1/treasury/wallets/:id/replicate target list contains the source chain_id, an unsupported chain, or a chain that already has a sibling treasury wallet for this tenant.
treasury_signature_invalidnoecrecover of the submitted signature does not match the signer staff member's known MPC address. Either the signature is malformed, or the staff member's MPC wallet has not been provisioned to the address recorded in staff_mpc_wallets.
treasury_signer_count_below_minimumnoWallet creation or signer-removal would leave signer_count < threshold + 2 (lost-signer policy). details.required_minimum and details.current_signer_count are populated.
treasury_threshold_invalidnoRequested threshold is outside [1, signer_count - 2]. details.attempted_threshold and details.signer_count are populated.
unknown_eventnoA webhook endpoint was created or updated subscribing to an event name that is not in the webhook event-type registry (GET /v1/webhook-event-types). Subscribe only to registered events.
validation_failednoRequest body or query failed schema validation (ValidationPipe).

429 2

CodeRetryWhen it occurs
faucet_daily_cap_exceedednoFaucet daily cap exceeded for the destination wallet. Default $1,000 USD per UTC-day per wallet; configurable via.
member_auth_rate_limitedyes (header)Per-tuple rate limit on a sign-in / sign-up / magic-link-request / password-reset-request endpoint tripped.

500 2

CodeRetryWhen it occurs
configuration_errornoA startup-time invariant between env / registry / chain state disagrees, e.g., an RPC-reported chainId differs from the entry (drift-defense §0.1), a deployed router address does not match the registry (§0.2), on-chain token decimals disagree with the token registry (§0.4), or the router EIP-712 domain separator disagrees with the static domain (§0.5). Startup fails closed rather than serving traffic in a misconfigured state. details.kind discriminates which invariant tripped (chain_id_mismatch, router_provenance_mismatch, token_decimals_mismatch, eip712_domain_mismatch, rpc_unreachable).
internal_errornoUnhandled exception; represents a bug. correlation_id is essential for support.

501 1

CodeRetryWhen it occurs
not_implementednoEndpoint or capability not implemented yet (Phase 1.5+ feature).

502 5

CodeRetryWhen it occurs
coinbase_session_creation_failedyes (header)Coinbase Onramp/Offramp session creation failed (non-2xx response or rejected request shape). Retry per Retry-After header.
email_domain_provider_erroryes (immediate)The email provider (Resend) returned an unexpected error while managing a sending domain (transient outage or an unclassified failure). The raw provider error is wrapped so it never escapes the adapter.
integration_gateway_erroryes (immediate)An outbound call to a tenant's Integration Gateway failed, a non-2xx response or a transport error (connection refused, TLS failure, timeout). details.operation names the contract op; details.upstream_status carries the gateway HTTP status (or null for a transport failure). The on-ramp/off-ramp orchestration decides retry vs compensation.
member_auth_upstream_erroryes (header)XKOVA IDP returned an unexpected error class with no specific mapping. Internal infra failure; transient retries may help.
stripe_upstream_erroryes (immediate)the billing provider API returned a 5xx, 429, or network/decode error. Transient; idempotency keys make retries safe.

503 10

CodeRetryWhen it occurs
billing_not_configurednothe billing provider billing surface invoked but is unset (degraded mode). Local-dev OK; staging/prod must set the key before any paying tenant onboards.
core_banking_outageyes (header)Core-banking provider is unreachable (outage). Retry after the indicated interval.
demo_persona_unavailablenoPOST /v1/demo-login is enabled but the fixed demo tenant has not been seeded, so the requested demo persona (operator/member) does not exist. Run make seed-demo. Distinct from demo_login_disabled (feature on, data missing).
email_domain_provider_unconfiguredyes (immediate)A tenant tried to manage a custom sending domain but the domains-capable Resend key () is not configured. The send key is least-privilege and cannot manage domains. Not the caller's fault; retryable once ops provisions the key.
partner_suspendednoPartner is suspended (operator incident-response action). All tenants under this partner also return 503.
service_unavailableyes (header)Platform-level outage; check status page.
tenant_suspendednoTenant is suspended (billing past due T+60, or operator incident-response action).
treasury_engine_unavailableyes (header)Engine relay returned an unrecoverable error or is unreachable when the API attempted to submit a deploy / owner-mgmt / execute transaction. Existing transfer state is preserved; client may retry.
verification_not_configuredyes (immediate)A production workspace started an identity-verification session but its verification policy carries no hosted_flow_url_template, so there is no vendor flow to send the account holder to. Sandbox workspaces fall back to the demonstration verifier instead; production deliberately does not, because routing a real customer through it would clear them without a real check. Set the template on the workspace's verification policy.
webhook_not_configuredyes (immediate)An inbound provider webhook receiver cannot verify the callback because of a server-side misconfiguration, the signing secret is unset, or raw request body capture is disabled (main.ts rawBody:true). Not the caller's fault; the provider's natural redelivery succeeds once ops fixes the config.

504 1

CodeRetryWhen it occurs
core_banking_timeoutyes (header)Core-banking provider did not respond in time. Retryable.