> ## Documentation Index
> Fetch the complete documentation index at: https://v2.docs.conduit.financial/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Codes

> Reference of public Conduit API error codes

All Conduit API errors follow [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) (Problem Details for HTTP APIs). Every error response includes a machine-readable public `type` field that uniquely identifies the error.

```json theme={null}
{
  "type": "VALIDATION_ERROR",
  "title": "Validation Error",
  "status": 400,
  "detail": "The field 'email' is not a valid email address.",
  "resolution": "Check the 'errors' array in the response for specific field-level issues and correct your request payload accordingly.",
  "docs": "https://conduit-v2.mintlify.app/errors#validation-error",
  "instance": "/v2/customers/cus_033NVvWNQgT5Arna7wIHO8",
  "correlationId": "corr_xyz789",
  "timestamp": "2026-01-15T09:30:00.000Z"
}
```

<Note>
  Always match on the `type` field for error handling logic, not on `status` or
  `title`. The `type` code is stable across API versions. The `title` is
  human-readable and may change.
</Note>

## Synchronous vs terminal failure codes

Conduit surfaces failures on two distinct channels. Branch your error-handling code on which channel a code arrives on, not on the code alone.

**Synchronous HTTP responses.** RFC 9457 Problem Details bodies on the same response as the request that caused them. Validation (`VALIDATION_ERROR`, `ONBOARDING_NOT_READY`), authentication (`AUTH_INVALID_API_KEY`), conflicts (`CONFLICT`, `DUPLICATE_CLIENT_REFERENCE_ID`, `IDEMPOTENCY_KEY_CONFLICT`, `RESOURCE_TERMINAL`), preconditions (`SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY`), rate limits (`RATE_LIMITED`), and similar errors arrive here. A `try/catch` around the HTTP call sees these.

**Terminal `failureCode` values.** Codes like `compliance_review_rejected`, `compliance_hold`, `returned_by_sender`, `travel_rule_rejected`, `user_signature_declined`, `roster_changed`, `chain_broadcast_failed` never arrive on an HTTP response. They arrive as the `failureCode` field on:

* `transaction.failed` / `transaction.rejected` events delivered to your registered webhook endpoint, and
* `GET /v2/transactions/{id}` when polled after the transaction reaches a terminal state.

A `try/catch` around `POST /v2/payouts` only sees synchronous codes. Terminal codes arrive later via your webhook handler or the next read of the transaction. See [webhooks](/webhooks) for the webhook payload shape, the [API reference](/api-reference/overview) for the synchronous error shapes per endpoint, and the [transaction failure-code catalog](#transaction-failure-code-catalog) below for terminal codes and their meanings. The "Channel" column on the failure-codes table names where each code is observable.

### Field-level errors (`errors[]`)

`VALIDATION_ERROR`, `ONBOARDING_NOT_READY`, and `DOCUMENT_IDS_NOT_FOUND` carry an `errors` array — one entry per invalid field — alongside the top-level fields above:

```json theme={null}
{
  "type": "ONBOARDING_NOT_READY",
  "title": "Onboarding Not Ready",
  "status": 422,
  "detail": "Validation failed",
  "errors": [
    {
      "pointer": "/businessInfo/taxId",
      "detail": "Tax ID is required",
      "category": "field"
    },
    {
      "pointer": "/documentIds",
      "detail": "At least one document is required",
      "category": "document"
    },
    {
      "pointer": "/ownership/persons",
      "detail": "At least 1 CONTROLLING_PERSON is required",
      "category": "individual"
    }
  ]
}
```

* `pointer` is an [RFC 6901](https://www.rfc-editor.org/rfc/rfc6901) JSON Pointer to the offending field.
* `detail` describes the problem (never echoes the rejected value).
* `category` is optional; when set, it's one of `field`, `document`, or `individual` and lets you group blockers without parsing pointers. `field` = form-field gap. `document` = missing or insufficient document (including per-UBO document slots — the pointer still names the person). `individual` = a required person is missing or has a non-document validation issue. The category is set by the requirements validator (`ONBOARDING_NOT_READY` and per-flow `VALIDATION_ERROR`); it's omitted on top-level schema rejections that don't carry the same context.

## Transaction failure-code catalog

The table below is committed alongside the runtime enum; CI breaks if a code is added without updating it. Each code links to its recovery playbook.

| Code                                  | Terminal state | Channel          | Description                                                                                                                                                                                       | Playbook                                                |
| ------------------------------------- | -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `chain_broadcast_failed`              | failed         | webhook + polled | The signed payout could not be broadcast to the chain before reaching finality; no funds left the wallet                                                                                          | [Playbook](/errors#chain-broadcast-failed)              |
| `compliance_hold`                     | failed         | webhook + polled | Compliance review held the funds; manual remediation required                                                                                                                                     | [Playbook](/errors/compliance-hold)                     |
| `compliance_rejected`                 | failed         | webhook + polled | A compliance reviewer rejected the payout's supporting documentation; reserved funds were returned (fires transaction.rejected, not transaction.failed)                                           | [Playbook](/errors#compliance-rejected)                 |
| `compliance_review_rejected`          | failed         | webhook + polled | Transaction rejected in regulatory compliance review; non-retryable                                                                                                                               | [Playbook](/errors/compliance-review-rejected)          |
| `crypto_wallet_misconfigured`         | failed         | webhook + polled | The source wallet is missing required custody configuration; the payout could not be signed                                                                                                       | [Playbook](/errors/crypto-wallet-misconfigured)         |
| `insufficient_funds`                  | failed         | webhook + polled | Source balance was insufficient to reserve the payout before broadcast; no funds moved                                                                                                            | [Playbook](/errors#insufficient-funds)                  |
| `insufficient_funds_at_settle`        | failed         | webhook + polled | Source funds were insufficient at the settlement attempt                                                                                                                                          | [Playbook](/errors/insufficient-funds-at-settle)        |
| `provider_rejected`                   | failed         | webhook + polled | The crypto outbound provider declined the broadcast request before the transaction was submitted to the chain                                                                                     | [Playbook](/errors/provider-rejected)                   |
| `rail_policy_rejected`                | failed         | webhook + polled | The receiving rail rejected the payment per its policy                                                                                                                                            | [Playbook](/errors/rail-policy-rejected)                |
| `rail_unavailable`                    | failed         | webhook + polled | The chosen rail was temporarily unavailable                                                                                                                                                       | [Playbook](/errors/rail-unavailable)                    |
| `returned_by_sender`                  | failed         | webhook + polled | The inbound transfer was returned by the originating institution before it could be credited                                                                                                      | [Playbook](/errors/returned-by-sender)                  |
| `roster_changed`                      | failed         | webhook + polled | A signer on the customer's roster was removed (or demoted out of the signing pool) while the payout was awaiting signatures; the half-collected stamps were voided so the fintech can re-initiate | [Playbook](/errors#roster-changed)                      |
| `sender_info_timeout`                 | failed         | webhook + polled | Sender-information gate expired before resolution                                                                                                                                                 | [Playbook](/errors/sender-info-timeout)                 |
| `travel_rule_rejected`                | failed         | webhook + polled | Counterparty rejected the Travel Rule request, or Travel Rule validation failed pre-broadcast                                                                                                     | [Playbook](/errors/travel-rule-rejected)                |
| `user_signature_declined`             | failed         | webhook + polled | End user explicitly declined to sign                                                                                                                                                              | [Playbook](/errors/user-signature-declined)             |
| `user_signature_expired`              | failed         | webhook + polled | End user did not sign across the allowed signing windows; the request expired                                                                                                                     | [Playbook](/errors#user-signature-expired)              |
| `user_signature_rejected_by_provider` | failed         | webhook + polled | Custody provider rejected the signature payload                                                                                                                                                   | [Playbook](/errors/user-signature-rejected-by-provider) |
| `user_signature_timeout`              | failed         | webhook + polled | Payout timed out waiting in the wallet's signing queue before it could start collecting signatures                                                                                                | [Playbook](/errors/user-signature-timeout)              |

## Sandbox simulate endpoint errors

These codes are returned as HTTP errors by sandbox simulate endpoints. They are not `failureCode` values on transactions.

| Code                                           | HTTP status | Description                                                                                                                                                                                                                                                                              | Playbook                                                         |
| ---------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `INVALID_ADDRESS_FORMAT`                       | 400         | EVM address must be all-lowercase or a correct EIP-55 checksum                                                                                                                                                                                                                           | [Playbook](/errors/invalid-address-format)                       |
| `RESOURCE_TERMINAL`                            | 409         | Tried to drive a simulate against an already-terminal entity                                                                                                                                                                                                                             | [Playbook](/errors/resource-terminal)                            |
| `SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY` | 422         | Called a sandbox simulate endpoint whose requested outcome is not viable in the transaction's current phase (e.g. simulate/settled or simulate/confirm against a payout parked at the document-review gate, or simulate/terminal with outcome:completed before a fiat route is selected) | [Playbook](/errors/sandbox-transaction-not-force-terminal-ready) |

For HTTP-level error responses (e.g., `400 VALIDATION_ERROR`, `404 NOT_FOUND`, `409 CONFLICT`), see the per-status sections below.

## Error Codes

<h3 id="validation-error">
  Validation Error
</h3>

`VALIDATION_ERROR` — HTTP 400

The request body or query parameters failed validation. One or more fields have invalid values, missing required properties, or incorrect types. Multipart file uploads that fail at the multipart-parser layer (unexpected form-field name, too many parts) carry an extra 'field' member naming the offending form-field.

**Resolution:** Check the 'errors' array in the response for specific field-level issues and correct your request payload accordingly. For multipart uploads, also inspect the optional 'field' member.

<h3 id="malformed-json">
  Malformed JSON Body
</h3>

`MALFORMED_JSON` — HTTP 400

The request body could not be parsed as JSON. Bodies declared as 'application/json' — and bodies with no Content-Type header, which are assumed to be JSON — must contain syntactically valid JSON.

**Resolution:** Fix the JSON syntax in the request body. If you intended to send a different format, declare it in the Content-Type header instead; JSON endpoints only accept 'application/json'.

<h3 id="rate-limited">
  Rate Limited
</h3>

`RATE_LIMITED` — HTTP 429

Too many requests. This error is returned by three independent checks: the per-organization bucket applied to every authenticated API request; the per-IP bucket applied to unauthenticated traffic before an API key is validated; and the per-IP bucket applied when repeated invalid API keys are submitted from the same address. Honor the Retry-After header (also exposed as retryAfterSeconds in the body) before retrying. Current limits and remaining budget are visible in X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset on rate-limited route responses.

**Resolution:** Sleep until Retry-After seconds have elapsed, then retry. For sustained workloads exceeding the per-organization defaults, request a rate-limit increase through your support contact.

<h3 id="invalid-oid-format">
  Invalid Object ID Format
</h3>

`INVALID_OID_FORMAT` — HTTP 400

A path or query parameter expected a valid object identifier but received a value that does not match the expected format.

**Resolution:** Verify that all IDs in the request URL and query parameters are correctly formatted. IDs are typically prefixed strings like 'cus\_...', 'app\_...', or 'doc\_...'.

<h3 id="invalid-cursor">
  Invalid Cursor
</h3>

`INVALID_CURSOR` — HTTP 400

The pagination cursor provided in the request is malformed or has expired.

**Resolution:** Use a cursor value returned from a previous list response. Do not construct cursor values manually. If the cursor has expired, start pagination from the beginning.

<h3 id="api-key-missing">
  API Key Missing
</h3>

`API_KEY_MISSING` — HTTP 401

The request did not include an API key. All API requests must be authenticated.

**Resolution:** Include your API key in the 'x-api-key' header with every request.

<h3 id="api-key-invalid">
  API Key Invalid
</h3>

`API_KEY_INVALID` — HTTP 401

The provided API key is not recognized or has been revoked.

**Resolution:** Verify that your API key is correct and has not been revoked. Generate a new key from the dashboard if needed.

<h3 id="feature-not-enabled">
  Feature Not Enabled
</h3>

`FEATURE_NOT_ENABLED` — HTTP 403

Your account does not have access to this feature. Features are enabled on a per-account basis.

**Resolution:** Contact support to request access to this feature, or check your account settings to verify which features are enabled.

<h3 id="api-key-creation-disabled">
  API Key Creation Disabled
</h3>

`API_KEY_CREATION_DISABLED` — HTTP 403

API key creation is not currently enabled for this organization.

**Resolution:** Contact Conduit to request API access for your organization.

<h3 id="api-key-read-only">
  API Key Is Read-Only
</h3>

`API_KEY_READ_ONLY` — HTTP 403

This API key has read-only access and cannot perform write operations. Read-only keys may make read requests (GET, HEAD, OPTIONS) only.

**Resolution:** Use a read-write API key for this request, or have an organization admin mint one from the dashboard.

<h3 id="api-key-ip-not-allowed">
  API key not allowed from this IP
</h3>

`API_KEY_IP_NOT_ALLOWED` — HTTP 403

The request came from an IP address that is not in this API key's allowlist.

**Resolution:** Add this IP to the key's allowlist in the dashboard, or call from an allowed network.

<h3 id="api-key-write-requires-admin">
  Read-Write Key Requires Admin
</h3>

`API_KEY_WRITE_REQUIRES_ADMIN` — HTTP 403

Only an organization admin can create a read-write API key. Developers may create read-only keys.

**Resolution:** Ask an organization admin to create the read-write key, or create a read-only key instead.

<h3 id="insufficient-role">
  Insufficient Role
</h3>

`INSUFFICIENT_ROLE` — HTTP 403

Your user role does not permit this action. This operation requires a higher-privileged role.

**Resolution:** Ask an organization admin to perform this action or to grant you the required role.

<h3 id="step-up-required">
  Step-Up Verification Required
</h3>

`STEP_UP_REQUIRED` — HTTP 403

This action requires fresh multi-factor (step-up) verification before it can proceed.

**Resolution:** Complete the multi-factor verification prompt and retry the request.

<h3 id="last-org-admin">
  Last Organization Admin
</h3>

`LAST_ORG_ADMIN` — HTTP 409

This user is the organization's only admin. An organization must always have at least one admin.

**Resolution:** Invite or assign another organization admin before removing this user.

<h3 id="cannot-remove-self">
  Cannot Remove Self
</h3>

`CANNOT_REMOVE_SELF` — HTTP 409

You cannot remove your own account from the organization.

**Resolution:** Ask another organization admin to remove your account.

<h3 id="internal-error">
  Internal Error
</h3>

`INTERNAL_ERROR` — HTTP 500

An unexpected error occurred while processing your request.

**Resolution:** Retry the request after a brief delay. If the error persists, contact support and include the correlationId from the error response for investigation.

<h3 id="signer-session-invalid">
  Signer Session Invalid
</h3>

`SIGNER_SESSION_INVALID` — HTTP 401

The signer session token is missing, malformed, has a bad signature, or has expired.

**Resolution:** Re-authenticate with your passkey to obtain a fresh signer session token, then retry with it in the Authorization: Bearer header.

<h3 id="signer-auth-failed">
  Signer Authentication Failed
</h3>

`SIGNER_AUTH_FAILED` — HTTP 401

Passkey authentication failed: the stamp could not be verified, the sub-organization did not match, or no active signer was found for the verified identity.

**Resolution:** Re-authenticate with your passkey against the correct sub-organization and retry.

<h3 id="signature-rejected">
  Signature Rejected
</h3>

`SIGNATURE_REJECTED` — HTTP 422

The signing provider rejected the stamped request. The signer is authenticated, but the signature itself could not be applied: the assertion may target the wrong sub-organization, the activity may have already completed or expired, or a policy denied the vote.

**Resolution:** Do not re-authenticate — the session is still valid. Contact support and include the correlationId from the error response so we can investigate the underlying rejection.

<h3 id="signing-provider-unavailable">
  Signing Provider Unavailable
</h3>

`SIGNING_PROVIDER_UNAVAILABLE` — HTTP 502

The signing provider returned a transient upstream error (network failure, timeout, rate limit, or 5xx) rather than a definitive verdict on the stamped request. The signer is authenticated and the request itself is fine — the provider just couldn't be reached right now.

**Resolution:** Retry the request after a brief delay. Do not re-authenticate — the session is still valid.

<h3 id="bad-request">
  Bad Request
</h3>

`BAD_REQUEST` — HTTP 400

The request could not be understood or was missing required parameters.

**Resolution:** Review the request format and ensure all required parameters are present and correctly typed.

<h3 id="unauthorized">
  Unauthorized
</h3>

`UNAUTHORIZED` — HTTP 401

Authentication is required and has not been provided or is invalid.

**Resolution:** Provide valid authentication credentials. Check that your API key or token has not expired.

<h3 id="forbidden">
  Forbidden
</h3>

`FORBIDDEN` — HTTP 403

You do not have permission to perform this action on the requested resource.

**Resolution:** Verify that your account has the required permissions for this operation. Contact your administrator if you believe this is an error.

<h3 id="not-found">
  Not Found
</h3>

`NOT_FOUND` — HTTP 404

The requested resource does not exist or you do not have access to it.

**Resolution:** Check that the resource ID in the URL is correct. The resource may have been deleted or may belong to a different account.

<h3 id="conflict">
  Conflict
</h3>

`CONFLICT` — HTTP 409

The request conflicts with the current state of the resource. This usually means a duplicate or a state transition that is not allowed.

**Resolution:** Check the current state of the resource before retrying. If creating a resource, verify that a resource with the same unique fields does not already exist.

<h3 id="duplicate-client-reference-id">
  Duplicate Client Reference ID
</h3>

`DUPLICATE_CLIENT_REFERENCE_ID` — HTTP 409

A resource with this client\_reference\_id already exists for your organization. A client\_reference\_id must be unique per resource type within your organization.

**Resolution:** Use a different client\_reference\_id, or fetch the existing resource by that reference. To retry the same request idempotently, reuse the idempotency-key header instead of changing the client\_reference\_id.

<h3 id="gone">
  Gone
</h3>

`GONE` — HTTP 410

The requested resource has been permanently removed and is no longer available.

**Resolution:** This resource has been deleted and cannot be recovered. Remove any references to it in your system.

<h3 id="precondition-failed">
  Precondition Failed
</h3>

`PRECONDITION_FAILED` — HTTP 412

A precondition specified in the request headers was not met by the server.

**Resolution:** Re-fetch the resource to get the current state and retry with the updated precondition values.

<h3 id="unprocessable-entity">
  Unprocessable Entity
</h3>

`UNPROCESSABLE_ENTITY` — HTTP 422

The request was well-formed but could not be processed due to semantic errors or business rule violations.

**Resolution:** Review the error details and adjust your request to comply with the documented business rules for this endpoint.

<h3 id="payload-too-large">
  Payload Too Large
</h3>

`PAYLOAD_TOO_LARGE` — HTTP 413

The request body exceeds the maximum size accepted by the server.

**Resolution:** Reduce the size of the request payload and retry. For file uploads, the response will instead carry the more specific FILE\_TOO\_LARGE code with the limit and offending field.

<h3 id="unsupported-media-type">
  Unsupported Media Type
</h3>

`UNSUPPORTED_MEDIA_TYPE` — HTTP 415

The request carries a body with a Content-Type this endpoint cannot parse. JSON endpoints accept 'application/json'; a body with no Content-Type header at all is assumed to be JSON. File-upload endpoints accept only 'multipart/form-data' — JSON or undeclared bodies are rejected there.

**Resolution:** Send the request body with the 'Content-Type: application/json' header. For file uploads, use 'Content-Type: multipart/form-data' — upload endpoints accept no other body type.

<h3 id="bad-gateway">
  Bad Gateway
</h3>

`BAD_GATEWAY` — HTTP 502

An upstream service returned an invalid response while processing your request.

**Resolution:** Retry the request after a brief delay. If the error persists, an upstream dependency may be experiencing issues.

<h3 id="service-unavailable">
  Service Unavailable
</h3>

`SERVICE_UNAVAILABLE` — HTTP 503

The service is temporarily unable to handle your request due to maintenance or capacity constraints.

**Resolution:** Retry the request using exponential backoff. Check the status page for any ongoing incidents.

<h3 id="policy-evaluation-unavailable">
  Policy Evaluation Unavailable
</h3>

`POLICY_EVALUATION_UNAVAILABLE` — HTTP 503

The documentation policy could not be evaluated because a policy lookup did not complete. No transaction was created.

**Resolution:** Retry the request using exponential backoff. If the error persists, contact support.

<h3 id="customer-not-found">
  Customer Not Found
</h3>

`CUSTOMER_NOT_FOUND` — HTTP 404

No customer exists with the specified ID, or the customer belongs to a different organization.

**Resolution:** Verify the customer ID is correct. Use the list customers endpoint to find valid customer IDs for your organization.

<h3 id="individual-not-found">
  Individual Not Found
</h3>

`INDIVIDUAL_NOT_FOUND` — HTTP 404

No individual exists with the specified ID for this customer, or the customer belongs to a different organization.

**Resolution:** Verify both the customer ID and individual ID are correct, and that the individual belongs to that customer.

<h3 id="customer-not-onboarded">
  Customer Not Onboarded
</h3>

`CUSTOMER_NOT_ONBOARDED` — HTTP 422

This operation requires the customer to have completed onboarding, but the customer has not been fully onboarded yet.

**Resolution:** Complete the customer onboarding process before attempting this operation. See the onboarding guide for the required steps.

**Runbook:** [Customer Not Onboarded](/errors/customer-not-onboarded)

<h3 id="document-ids-not-found">
  Document IDs Not Found
</h3>

`DOCUMENT_IDS_NOT_FOUND` — HTTP 400

One or more document IDs provided in the request do not exist or do not belong to this customer.

**Resolution:** Verify that all document IDs are correct and belong to the customer specified in the request.

<h3 id="application-not-found">
  Application Not Found
</h3>

`APPLICATION_NOT_FOUND` — HTTP 404

No application exists with the specified ID, or the application belongs to a different organization.

**Resolution:** Verify the application ID is correct. Use the list applications endpoint to find valid application IDs for your organization.

<h3 id="application-person-not-found">
  Application Person Not Found
</h3>

`APPLICATION_PERSON_NOT_FOUND` — HTTP 404

No ownership person with the specified reference ID exists on this application.

**Resolution:** Verify the person reference ID from the application detail response.

<h3 id="application-person-field-empty">
  Application Person Field Empty
</h3>

`APPLICATION_PERSON_FIELD_EMPTY` — HTTP 404

The ownership person exists but the requested tier-2 field has no submitted value — e.g. a non-US person carries no tax identifier.

**Resolution:** This is expected when the field does not apply to the person (a non-US person has no US TIN). No value can be revealed.

<h3 id="application-invalid-status">
  Application Invalid Status
</h3>

`APPLICATION_INVALID_STATUS` — HTTP 409

The requested operation cannot be performed because the application is not in the required status.

**Resolution:** Check the application's current status and refer to the documentation for allowed status transitions.

<h3 id="application-already-decided">
  Application Already Decided
</h3>

`APPLICATION_ALREADY_DECIDED` — HTTP 409

The application has already reached a terminal status (approved, rejected, or cancelled) and cannot be decided again.

**Resolution:** A decided application is final. Create a new application instead of re-deciding this one.

<h3 id="application-country-not-supported">
  Application Country Not Supported
</h3>

`APPLICATION_COUNTRY_NOT_SUPPORTED` — HTTP 409

The application cannot be approved because a country it carries — the registered jurisdiction, or a person's country — is either not a recognizable ISO 3166-1 code or not a jurisdiction Conduit supports, so a customer could not be created from it.

**Resolution:** Correct the offending country on the application and retry the decision.

<h3 id="rejection-category-not-applicable">
  Rejection Category Not Applicable
</h3>

`REJECTION_CATEGORY_NOT_APPLICABLE` — HTTP 422

The sandbox application-simulate `category` body field is only meaningful for KYB-pipeline applications. Other application types reject any `category` value.

**Resolution:** Omit the `category` field (the service falls back to a generic sandbox-rejection sentinel) or call simulate/decision with `outcome: "rejected"` against a KYB-pipeline application.

<h3 id="invalid-legal-structure">
  Invalid Legal Structure
</h3>

`INVALID_LEGAL_STRUCTURE` — HTTP 400

The submitted `companyClassification.legalStructure` is not a valid local structure for the registered country. Valid options are surfaced by the discovery endpoint (`/v2/onboarding/requirements?country=...`); the submission must use one of those exact option values.

**Resolution:** Re-fetch the requirements for the registered country and submit one of the listed `companyClassification.legalStructure` option values verbatim.

<h3 id="order-not-found">
  Order Not Found
</h3>

`ORDER_NOT_FOUND` — HTTP 404

No order exists with the specified ID, or the order has expired.

**Resolution:** Verify the order ID is correct. Orders have a limited validity period; create a new order if the previous one has expired.

<h3 id="unsupported-pair">
  Unsupported Pair
</h3>

`UNSUPPORTED_PAIR` — HTTP 422

The requested source and destination asset pair is not supported.

**Resolution:** Check the configured trading pairs and submit a supported source/destination asset combination.

<h3 id="invalid-order-combo">
  Invalid Order Combination
</h3>

`INVALID_ORDER_COMBO` — HTTP 422

The requested order combination is not supported: same-asset same-chain moves, mismatched source and destination resources, or an autoPayout shape that does not match the order direction. OFFRAMP orders require a fiat autoPayout (`{ rail, purpose, documents?, recipient }`); ONRAMP orders require a crypto autoPayout (`{ purpose, documents?, recipient: { rail: 'crypto', chain, address, attestation } }`), self- or third-party-custody. `purpose` is required and drives the documentation / whitelist gate; `documents` are supporting-document ids. `recipient.chain` must match the destination wallet's chain.

**Resolution:** Adjust the source/destination pair to a supported combination, or align autoPayout with the order direction: fiat autoPayout for OFFRAMP, crypto autoPayout for ONRAMP. For ONRAMP, `recipient.chain` must match the destination wallet's chain.

<h3 id="non-custodial-source-not-supported">
  Non-Custodial Source Not Supported
</h3>

`NON_CUSTODIAL_SOURCE_NOT_SUPPORTED` — HTTP 422

Non-custodial source wallets are supported on EVM chains only. On non-EVM chains (Tron, Solana, Stellar, Bitcoin) the order is rejected at creation.

**Resolution:** Use a custodial wallet as the source on non-EVM chains, or a non-custodial wallet on an EVM chain.

<h3 id="no-pricing-configured">
  No Pricing Configured
</h3>

`NO_PRICING_CONFIGURED` — HTTP 404

No active pricing profile contains a base pricing rule for the requested asset pair.

**Resolution:** Configure a base pricing rule for the source/destination pair before requesting an order.

<h3 id="missing-required-fields">
  Missing Required Fields
</h3>

`MISSING_REQUIRED_FIELDS` — HTTP 422

The order request is missing one or more fields required for the destination shape.

**Resolution:** Review the order requirements response for the requested pair, country, rail, and recipient type.

<h3 id="recipient-validation-failed">
  Recipient Validation Failed
</h3>

`RECIPIENT_VALIDATION_FAILED` — HTTP 422

The supplied recipient or destination details failed validation for the chosen rail, chain, or corridor.

**Resolution:** Verify that the recipient or destination fields (e.g., destination address, account number, routing details) are valid for the chosen rail, chain, and country.

<h3 id="amount-out-of-range">
  Amount Out of Range
</h3>

`AMOUNT_OUT_OF_RANGE` — HTTP 422

The requested amount is below the minimum or above the maximum allowed for this order.

**Resolution:** Adjust the amount to fall within the allowed range.

<h3 id="rate-unavailable">
  Rate Unavailable
</h3>

`RATE_UNAVAILABLE` — HTTP 503

A live exchange rate could not be retrieved for the requested currency pair. The rate provider may be temporarily unavailable.

**Resolution:** Retry the request after a short delay. If the error persists, the rate provider for this currency pair may be experiencing an outage.

**Runbook:** [Rate Unavailable](/errors/rate-unavailable)

<h3 id="rate-unavailable-after-hours">
  Rate Unavailable (After Hours)
</h3>

`RATE_UNAVAILABLE_AFTER_HOURS` — HTTP 503

The market for this currency pair is currently closed and no prior open-market rate is available to freeze, so a quote cannot be produced.

**Resolution:** Retry once the market reopens. If the error persists during open hours, the rate provider for this currency pair may be experiencing an outage.

**Runbook:** [Rate Unavailable (After Hours)](/errors/rate-unavailable-after-hours)

<h3 id="order-not-executable">
  Order Not Executable
</h3>

`ORDER_NOT_EXECUTABLE` — HTTP 409

The order cannot be executed because it is not in a pending state. Already-executed or failed orders cannot be re-executed.

**Resolution:** Create a new order. An order can only be executed once and only while it is pending.

<h3 id="order-expired">
  Order Expired
</h3>

`ORDER_EXPIRED` — HTTP 409

The order has expired and can no longer be executed.

**Resolution:** Create a new order to obtain a fresh price lock and execute that one instead.

<h3 id="order-not-cancellable">
  Order Cannot Be Cancelled
</h3>

`ORDER_NOT_CANCELLABLE` — HTTP 409

The order cannot be cancelled. Either it is already in a terminal state (succeeded or failed), or its execution has begun moving funds and the cancel path can no longer safely unwind it.

**Resolution:** Read the order to confirm its current state. If it is already terminal, no action is required. If it is still pending while executing, wait for the order to reach a terminal state and react to that; once execution has started only the server can safely complete or reverse the order.

<h3 id="provider-unavailable">
  Provider unavailable
</h3>

`PROVIDER_UNAVAILABLE` — HTTP 503

The order's conversion provider was unavailable or returned a transient failure during execution. No funds were moved.

**Resolution:** Retry by creating a new order. If the issue persists, contact support.

<h3 id="provider-rejected">
  Provider rejected the conversion
</h3>

`PROVIDER_REJECTED` — HTTP 422

The order's conversion provider rejected the request (rate stale, recipient blocked, or other provider-side policy). No funds were moved.

**Resolution:** Verify the order parameters (rate, recipient, amount) and submit a new order. Contact support if the cause is unclear.

<h3 id="cancelled">
  Conversion cancelled
</h3>

`CANCELLED` — HTTP 422

The order's conversion was cancelled before completion (operator action or upstream condition). No funds were moved.

**Resolution:** Submit a new order to retry.

<h3 id="user-not-found">
  User Not Found
</h3>

`USER_NOT_FOUND` — HTTP 404

No user exists with the specified ID, or the user belongs to a different organization.

**Resolution:** Verify the user ID is correct. Use the list users endpoint to find valid user IDs for your organization.

<h3 id="user-already-exists">
  User Already Exists
</h3>

`USER_ALREADY_EXISTS` — HTTP 409

A user with the same email address already exists in this organization.

**Resolution:** Use the existing user or choose a different email address. Use the list users endpoint to find the existing user.

<h3 id="user-already-belongs-to-org">
  User Already Belongs to Organization
</h3>

`USER_ALREADY_BELONGS_TO_ORG` — HTTP 409

The calling user is already linked to an organization. Each user can belong to one organization at a time; calling `setup` a second time on the same user is rejected.

**Resolution:** Resolve the calling user's existing organization (e.g. via `GET /v2/portal/users/me`) and route the user accordingly instead of retrying setup.

<h3 id="wallet-not-found">
  Wallet Not Found
</h3>

`WALLET_NOT_FOUND` — HTTP 404

No wallet exists with the specified ID, or the wallet belongs to a different organization.

**Resolution:** Verify the wallet ID is correct. Use the list wallets endpoint to find valid wallet IDs for your organization.

<h3 id="cosign-activity-not-found">
  Cosign activity not found
</h3>

`COSIGN_ACTIVITY_NOT_FOUND` — HTTP 404

Sandbox cosign activity could not be located.

**Resolution:** Verify the activityId. Sandbox-only: activity IDs are issued during cosign creation. If you don't have one, use POST /v2/sandbox/payouts/:id/simulate/cosign to resolve the cosign gate keyed by payout id instead.

<h3 id="wallet-not-active">
  Wallet Not Active
</h3>

`WALLET_NOT_ACTIVE` — HTTP 422

The wallet is not in ACTIVE status and cannot be used at this time (e.g. a frozen non-custodial wallet cannot initiate a payout).

**Resolution:** Check the wallet's current status. Only ACTIVE wallets can receive deposits or initiate payouts; a frozen wallet must be cleared by Conduit before it can move funds again.

<h3 id="wallet-rotation-blocked-balance">
  Wallet Rotation Blocked: Balance Above Dust
</h3>

`WALLET_ROTATION_BLOCKED_BALANCE` — HTTP 422

The wallet holds a balance (available, pending, or frozen) above the asset's dust floor and cannot be rotated. Rotation would strand those funds on the retired wallet record. A leftover at or below the dust floor does not block rotation — it is too small to be worth a transfer, so nothing will ever move it.

**Resolution:** Move the funds out of the wallet (e.g. via a payout) and wait for any in-flight deposits to settle, then retry the rotation. Compliance-frozen balances must be released before rotation is possible.

<h3 id="wallet-not-rotatable">
  Wallet Not Rotatable
</h3>

`WALLET_NOT_ROTATABLE` — HTTP 409

Only an active wallet can be rotated. The wallet is no longer active — it may already have been rotated or disabled.

**Resolution:** Fetch the wallet to check its current status. A retired wallet exposes the replacement via `replacedByWalletId`; rotate that one instead.

<h3 id="wallet-rotation-in-progress">
  Wallet Rotation In Progress
</h3>

`WALLET_ROTATION_IN_PROGRESS` — HTTP 409

A rotation for this wallet is already running. Concurrent rotations of the same wallet are rejected until the in-flight one completes.

**Resolution:** Wait for the in-flight rotation to finish (the wallet's address updates on completion), then retry if needed.

<h3 id="wallet-no-address">
  Wallet Has No Deposit Address
</h3>

`WALLET_NO_ADDRESS` — HTTP 422

The wallet does not have a deposit address assigned. Address provisioning may still be in progress.

**Resolution:** Wait for the wallet to be fully provisioned before simulating a deposit. If the issue persists, contact support.

<h3 id="wallet-chain-mismatch">
  Wallet Chain Mismatch
</h3>

`WALLET_CHAIN_MISMATCH` — HTTP 422

The chain specified in the request does not match the chain the wallet is provisioned on.

**Resolution:** Use the correct chain for this wallet. Fetch the wallet details to see which chain it is provisioned on.

<h3 id="virtual-account-not-found">
  Virtual Account Not Found
</h3>

`VIRTUAL_ACCOUNT_NOT_FOUND` — HTTP 404

No virtual account exists for the requested customer and asset, or the virtual account belongs to a different organization.

**Resolution:** Verify the customer has an active virtual account for the requested asset before creating a fiat payout.

<h3 id="no-eligible-provider">
  No eligible banking provider
</h3>

`NO_ELIGIBLE_PROVIDER` — HTTP 422

No banking provider is currently available to service a virtual account for this customer. Every provider that could serve the customer's country and industry is either restricted or has been taken offline.

**Resolution:** The customer cannot be provisioned a virtual account until a banking provider becomes available for their jurisdiction. This is an operational availability limit, not a fixable request error.

<h3 id="virtual-account-not-active">
  Virtual Account Not Active
</h3>

`VIRTUAL_ACCOUNT_NOT_ACTIVE` — HTTP 422

The virtual account is not in active status and cannot accept deposits at this time.

**Resolution:** Check the virtual account's current status. Only active virtual accounts can receive simulated deposits.

<h3 id="virtual-account-asset-mismatch">
  Virtual Account Asset Mismatch
</h3>

`VIRTUAL_ACCOUNT_ASSET_MISMATCH` — HTTP 422

The asset or chain specified in the request does not match the virtual account's configured asset.

**Resolution:** Use the asset code matching the virtual account's configured fiat asset. Fetch the virtual account details to see its asset.

<h3 id="wallet-no-provider-account">
  Wallet Account Not Provisioned
</h3>

`WALLET_NO_PROVIDER_ACCOUNT` — HTTP 422

The customer does not yet have a wallet account on the underlying provider. Wallet addresses cannot be created until the customer claims non-custodial control.

**Resolution:** Submit a non-custodial claim via POST /v2/customers/:id/wallets/claim-non-custodial to provision the wallet account, then retry creating the wallet.

<h3 id="wallet-custody-not-claimed">
  Wallet Custody Not Claimed
</h3>

`WALLET_CUSTODY_NOT_CLAIMED` — HTTP 409

The customer's wallet account is custodial, but custodial wallets are not enabled for this organization. Wallet addresses cannot be issued on a custodial account unless the custodial wallet path is enabled, and a custodial account cannot be claimed as non-custodial.

**Resolution:** If your organization should hold custody, contact your Conduit representative to enable the custodial wallet path; wallet creation then works directly. A custodial account cannot be converted to non-custodial via claim-non-custodial. If custodial wallets are intentionally disabled, this 409 is the expected gate.

<h3 id="deposit-address-unavailable">
  Deposit Address Unavailable
</h3>

`DEPOSIT_ADDRESS_UNAVAILABLE` — HTTP 409

A funding address for this customer is still being provisioned, so the order could not be given one. Nothing was created and no funds were moved.

**Resolution:** Retry the same request shortly. Provisioning is a one-off per customer and chain, so a retry after a moment succeeds.

<h3 id="wallet-provisioning-rejected">
  Wallet Provisioning Rejected
</h3>

`WALLET_PROVISIONING_REJECTED` — HTTP 422

The custody provider rejected the wallet creation; no wallet was created and no funds were moved.

**Resolution:** Retry with a new idempotency key. If the rejection persists, contact support.

<h3 id="wallet-awaiting-admin-approval">
  Wallet Awaiting Admin Approval
</h3>

`WALLET_AWAITING_ADMIN_APPROVAL` — HTTP 409

This wallet is being provisioned through a ceremony that is awaiting admin approval.

**Resolution:** Wait for an admin to approve the ceremony, then check the wallet status again.

<h3 id="registered-address-not-found">
  Registered Address Not Found
</h3>

`REGISTERED_ADDRESS_NOT_FOUND` — HTTP 404

No registered address exists with the specified ID, or the registered address belongs to a different organization.

**Resolution:** Verify the registered address ID is correct. Use the list registered addresses endpoint to find valid IDs for the customer.

<h3 id="transaction-not-found">
  Transaction Not Found
</h3>

`TRANSACTION_NOT_FOUND` — HTTP 404

No transaction exists with the specified ID, or the transaction belongs to a different organization.

**Resolution:** Verify the transaction ID is correct. Use the list transactions endpoint to find valid IDs for your organization.

<h3 id="sandbox-transaction-type-not-simulatable">
  Transaction Type Not Simulatable
</h3>

`SANDBOX_TRANSACTION_TYPE_NOT_SIMULATABLE` — HTTP 422

The sandbox simulate-terminal endpoint does not support this transaction type.

**Resolution:** Supported types: WITHDRAWAL, ONRAMP, OFFRAMP, DEPOSIT. INTERNAL\_TRANSFER is not supported. Check the response body for the current allowlist.

<h3 id="sandbox-transaction-not-force-terminal-ready">
  Transaction Not Force-Terminal Ready
</h3>

`SANDBOX_TRANSACTION_NOT_FORCE_TERMINAL_READY` — HTTP 422

A sandbox simulate endpoint refused the request because the requested outcome is not viable in the transaction's current phase. Common cases: a payout parked at the document-review gate receiving `simulate/settled` or `simulate/confirm` before review approval; a fiat payout with no selected settlement route receiving `simulate/terminal` with `outcome: "completed"`.

**Resolution:** If the payout carries `documents`, call `POST /v2/sandbox/payouts/{id}/simulate-review-approve` (or `simulate-review-reject`) first. For `simulate/terminal` callers, wait for the transaction to advance to the next phase or call with `outcome: "failed"` to terminalize from the current phase.

<h3 id="transaction-not-awaiting-signature">
  Transaction Not Awaiting Signature
</h3>

`TRANSACTION_NOT_AWAITING_SIGNATURE` — HTTP 409

The transaction has not reached signature collection, so there is no signing link to return. Non-custodial payouts are minted a signing link only after they clear compliance and document review; that link is delivered on the passkey\_required variant of the transaction.awaiting\_signature webhook, never by minting one through this endpoint.

**Resolution:** Do not create a transaction\_approval verification for a payout yourself. Wait for the transaction.awaiting\_signature webhook and route your signers to the verificationUrl it carries (passkey\_required mode). Re-fetch the payout to confirm it is still awaiting signature.

<h3 id="signing-link-not-human-signable">
  No Human Signing Link For This Wallet
</h3>

`SIGNING_LINK_NOT_HUMAN_SIGNABLE` — HTTP 409

This payout's wallet signs in a programmatic mode and its roster is machine-only — no human signer can approve it, so there is no verification link to return. The signing handle is delivered as a signingRequestId on the transaction.awaiting\_signature webhook, and your machine integration approves or rejects it through the signing-requests API. A programmatic wallet that has an active passkey signer DOES return the human link here (that signer may approve on the verify page); this error means the roster carries no such signer.

**Resolution:** Read the signingRequestId from the transaction.awaiting\_signature webhook, fetch details with GET /v2/signing-requests/\{id}, then approve via POST /v2/signing-requests/\{id}/approve or reject via POST /v2/signing-requests/\{id}/reject. If you expected a human signer to approve this payout, add an active passkey signer to the wallet's roster.

<h3 id="unregistered-address-not-awaiting">
  Deposit Not Awaiting Sender Information
</h3>

`UNREGISTERED_ADDRESS_NOT_AWAITING` — HTTP 409

The deposit is not currently awaiting sender information. Sender information can only be submitted while the deposit is processing and waiting for those details.

**Resolution:** Re-fetch the deposit to inspect its current public status. If the deposit has already progressed, no further sender-information submission is needed.

<h3 id="sender-info-not-a-deposit">
  Transaction Is Not a Deposit
</h3>

`SENDER_INFO_NOT_A_DEPOSIT` — HTTP 404

Sender information can only be submitted for deposits. The transaction with the specified ID exists but is not a deposit.

**Resolution:** Verify the transaction ID. Sender-information submission applies only to inbound fiat deposits parked on an unregistered address.

<h3 id="sender-info-already-recorded">
  Sender Information Already Recorded
</h3>

`SENDER_INFO_ALREADY_RECORDED` — HTTP 409

Sender information has already been recorded for this deposit. The API accepts one submission per deposit; later submissions are rejected.

**Resolution:** Double-check the originator details before submitting — the API accepts only one submission per deposit. If the recorded information was incorrect, contact Conduit to have it corrected while the deposit is still under review.

<h3 id="sender-info-submission-in-flight">
  Sender Information Submission In Flight
</h3>

`SENDER_INFO_SUBMISSION_IN_FLIGHT` — HTTP 409

Another sender-information submission for this deposit is still being processed.

**Resolution:** Wait for the in-flight submission to resolve, then re-fetch the deposit before retrying.

<h3 id="prefunding-source-not-house-account">
  Prefunding Source Must Be a House Account
</h3>

`PREFUNDING_SOURCE_NOT_HOUSE_ACCOUNT` — HTTP 422

A prefunding transaction must originate from a Conduit house account. The source account for this request is not marked as a house account.

**Resolution:** Send prefunding transactions only from an account marked as a house account, or remove the prefunding purpose.

<h3 id="prefunding-destination-not-found">
  Prefunding Destination Not Found
</h3>

`PREFUNDING_DESTINATION_NOT_FOUND` — HTTP 422

The destination account specified for this prefunding transaction could not be found.

**Resolution:** Verify the destination account identifier and retry.

<h3 id="prefunding-ambiguous-match">
  Prefunding Match Is Ambiguous
</h3>

`PREFUNDING_AMBIGUOUS_MATCH` — HTTP 422

More than one account matches the details given for this prefunding transaction, so Conduit cannot determine a single destination.

**Resolution:** Provide additional identifying details to narrow the match to a single account.

<h3 id="prefunding-unsupported-rail">
  Prefunding Rail Not Supported
</h3>

`PREFUNDING_UNSUPPORTED_RAIL` — HTTP 422

Prefunding is not supported over the rail requested for this transaction.

**Resolution:** Use a supported rail for prefunding transactions, or remove the prefunding purpose.

<h3 id="sandbox-order-not-deposit-funded">
  Order has no funding address to deposit to
</h3>

`SANDBOX_ORDER_NOT_DEPOSIT_FUNDED` — HTTP 409

The order names its own funding source, so it publishes no Conduit-managed funding address. Only a deposit-funded order has one to deposit to.

**Resolution:** Fund the named source directly, or create the order without a `source` so it returns a funding address in `depositInstructions`.

<h3 id="sandbox-order-no-parked-funding">
  Order has no funding transfer awaiting a compliance decision
</h3>

`SANDBOX_ORDER_NO_PARKED_FUNDING` — HTTP 404

No transfer into this order's funding address is currently held for compliance review. It may not have landed yet, it may have cleared on its own, or it may already have reached a terminal state.

**Resolution:** Fund the order with a scenario that holds the transfer for review, then retry. Poll the order until it stops reporting this before counting the decision as applied.

<h3 id="sandbox-sender-info-not-required">
  Deposit is not waiting for sender information
</h3>

`SANDBOX_SENDER_INFO_NOT_REQUIRED` — HTTP 409

The deposit is not currently parked at the sender-information gate. It may have already been resolved, never required sender information, or reached a terminal state.

**Resolution:** Inspect the deposit status. Only deposits that are pending and waiting for sender information accept this simulator.

<h3 id="signer-not-found">
  Signer Not Found
</h3>

`SIGNER_NOT_FOUND` — HTTP 404

No signer exists with the specified ID for this wallet.

**Resolution:** Verify the signer ID is correct and that it belongs to the specified wallet.

<h3 id="claim-not-found">
  Claim Not Found
</h3>

`CLAIM_NOT_FOUND` — HTTP 404

No non-custodial claim exists with the specified ID for this customer.

**Resolution:** Verify the claimId returned by POST /v2/customers/:id/wallets/claim-non-custodial and that it belongs to this customer.

<h3 id="signer-not-enrollable">
  Signer Not Enrollable
</h3>

`SIGNER_NOT_ENROLLABLE` — HTTP 409

The signer is not in a state that admits enrollment, so there is no invite to reissue (it is already active, being removed, removed, or errored).

**Resolution:** Only signers still completing enrollment (pending activation or activating) can have an invite reissued. Check the signer status via GET /v2/customers/:id/wallet-signers.

<h3 id="signer-pending-admin-approval">
  Signer Pending Admin Approval
</h3>

`SIGNER_PENDING_ADMIN_APPROVAL` — HTTP 409

The signer has enrolled a passkey on a live roster and is now parked for a customer admin's co-stamp — the blocker is an admin approval, not a lost enrollment invite. Reissuing the signer invite would send the wrong link.

**Resolution:** Recover the admin approval link with POST /v2/customers/:customerId/wallet-signers/:signerId/reissue-admin-approval, and route it to an admin. Do not reissue the signer invite.

<h3 id="signer-no-pending-admin-approval">
  No Pending Admin Approval
</h3>

`SIGNER_NO_PENDING_ADMIN_APPROVAL` — HTTP 409

The signer has no live admin-approval awaiting a co-stamp, so there is no admin link to recover.

**Resolution:** This endpoint recovers the admin link only while a passkey add is parked for approval. Check the signer status via GET /v2/customers/:customerId/wallet-signers.

<h3 id="verification-type-not-reissuable">
  Verification Type Not Reissuable Here
</h3>

`VERIFICATION_TYPE_NOT_REISSUABLE` — HTTP 422

This verification type cannot be initiated through the generic verifications route because it requires per-type context the route cannot supply.

**Resolution:** Use the dedicated endpoint for this verification type. For wallet signer invites, POST /v2/customers/:customerId/wallet-signers/:signerId/reissue-invite.

<h3 id="signer-not-active">
  Signer Not Active
</h3>

`SIGNER_NOT_ACTIVE` — HTTP 409

The signer is not in an active state and cannot perform the requested operation.

**Resolution:** Check the signer's current status. Only active signers can sign transactions or be modified.

<h3 id="signer-removal-forbidden">
  Signer Removal Forbidden
</h3>

`SIGNER_REMOVAL_FORBIDDEN` — HTTP 403

The signer cannot be removed because it is the last remaining signer on the wallet or has a protected role.

**Resolution:** Add another signer to the wallet before removing this one, or contact support if the signer has a protected role.

<h3 id="signers-not-supported">
  Signers Not Supported
</h3>

`SIGNERS_NOT_SUPPORTED` — HTTP 422

Signers can only be managed on non-custodial wallets. This customer's wallet is custodial, so the backend signs on its behalf.

**Resolution:** Claim the wallet as non-custodial via POST /v2/customers/:id/wallets/claim-non-custodial before adding or removing signers.

<h3 id="roster-below-min-admins">
  Roster Below Minimum Admins
</h3>

`ROSTER_BELOW_MIN_ADMINS` — HTTP 400

A non-custodial roster must contain at least 2 admin signers so no single person can lose access to the wallet.

**Resolution:** Include at least 2 signers with role `admin` in the roster before submitting the claim.

<h3 id="threshold-exceeds-roster">
  Signing Threshold Exceeds Roster
</h3>

`THRESHOLD_EXCEEDS_ROSTER` — HTTP 400

The requested `signingThreshold` is greater than the number of signers in the roster, which would make signing impossible.

**Resolution:** Lower `signingThreshold` to at most the roster size, or add signers to the roster before raising the threshold.

<h3 id="signer-email-duplicate">
  Duplicate Signer Email
</h3>

`SIGNER_EMAIL_DUPLICATE` — HTTP 400

The roster contains two or more signers with the same email address. Each signer must map to a distinct human.

**Resolution:** Remove the duplicate entries so every signer in the roster has a unique email.

<h3 id="api-key-credential-not-supported">
  Machine Signer Credentials Not Supported in This Context
</h3>

`API_KEY_CREDENTIAL_NOT_SUPPORTED` — HTTP 422

The request includes a machine signer (`credentialType: api_key`) in a context where api\_key signers cannot be enrolled. This is reserved for future compatibility and should not be seen in normal operation.

**Resolution:** Use `credentialType: passkey` for this context, or include the api\_key member in the `claim-non-custodial` roster instead.

<h3 id="signing-request-not-found">
  Signing request not found
</h3>

`SIGNING_REQUEST_NOT_FOUND` — HTTP 404

No signing request with this id exists for your account. The same response is returned for an id that belongs to another account, so a 404 never confirms that an id exists elsewhere.

**Resolution:** List your actionable signing requests with GET /v2/signing-requests and use an id from that list. The transaction.awaiting\_signature webhook also delivers the id directly as signingRequestId (programmatic mode).

<h3 id="signing-stamp-attribution-mismatch">
  Signing stamp was not attributed to the resolved signer
</h3>

`SIGNING_STAMP_ATTRIBUTION_MISMATCH` — HTTP 422

The stamp verified locally and resolved to one of your machine signers, but the signing provider did not record a vote for that signer — the vote was attributed elsewhere or not recorded. Conduit fails closed rather than counting a vote toward quorum that it cannot attribute to the signer you submitted.

**Resolution:** Re-fetch the signing request with GET /v2/signing-requests/\{id} and resubmit with the machine signer whose public key is registered on this wallet's roster. If it recurs, the signer's provider registration may be out of sync — contact Conduit.

<h3 id="signing-stamp-invalid">
  Signing stamp is invalid
</h3>

`SIGNING_STAMP_INVALID` — HTTP 422

The submitted stamp could not be verified: it is not a well-formed P-256 stamp, its signature does not match the signed body, or the signed body does not authorize this exact signing request (its activity fingerprint, sub-organization, or request type does not match).

**Resolution:** Re-fetch the signing request's approval material with GET /v2/signing-requests/\{id}, rebuild the approve/reject activity body exactly as returned, and stamp it with your machine signer's P-256 api key.

**Guide:** [Programmatic payout signing](/guides/machine-signer-stamping)

<h3 id="signing-stamp-signer-unknown">
  Signing stamp does not match an active machine signer
</h3>

`SIGNING_STAMP_SIGNER_UNKNOWN` — HTTP 422

The stamp's public key does not resolve to an ACTIVE machine (api\_key) signer on the wallet's roster. The signature verified, but the key is not one of this account's provisioned api-key signers.

**Resolution:** Stamp with the P-256 key you registered for an active api\_key signer on this account's roster, or add the signer to the roster before approving.

**Guide:** [Programmatic payout signing](/guides/machine-signer-stamping)

<h3 id="signing-mode-not-programmatic">
  Wallet is not configured for programmatic signing
</h3>

`SIGNING_MODE_NOT_PROGRAMMATIC` — HTTP 409

This wallet's signing mode requires human passkey approval, so machine (api-key) stamps are not accepted. The signing-requests API only serves accounts whose signing mode is `programmatic` or `programmatic_unattended`.

**Resolution:** Have Conduit enable a programmatic signing mode for this customer, or approve the payout with a passkey on the Conduit-hosted verification page.

**Guide:** [Programmatic payout signing](/guides/machine-signer-stamping)

<h3 id="programmatic-quorum-unreachable">
  Not enough machine signers to reach the signing threshold
</h3>

`PROGRAMMATIC_QUORUM_UNREACHABLE` — HTTP 422

A programmatic (machine-key) roster must contain at least `signingThreshold` api\_key signers so the customer's backend can reach the signing quorum on its own. This roster has fewer api\_key signers than the threshold.

**Resolution:** Add api\_key signers until their count is at least `signingThreshold`, or lower `signingThreshold` to at most the number of api\_key signers.

**Guide:** [Programmatic payout signing](/guides/machine-signer-stamping)

<h3 id="verification-method-not-supported-for-type">
  Verification method not supported for this verification type
</h3>

`VERIFICATION_METHOD_NOT_SUPPORTED_FOR_TYPE` — HTTP 422

The supplied verification method cannot complete this verification type. A machine api-key (`API_KEY`) is an approval-relay credential only: it signs transaction and ceremony approvals, and is never used to enroll a signer or complete an identity verification.

**Resolution:** Use the method the verification type expects — a passkey (or Google OAuth) for signer enrollment / identity, and `API_KEY` only to approve a transaction or ceremony.

<h3 id="signer-credential-scheme-not-admissible">
  Signer approved with a credential that does not match their enrolled type
</h3>

`SIGNER_CREDENTIAL_SCHEME_NOT_ADMISSIBLE` — HTTP 422

A counted signer's approval was stamped with a credential type that does not match the credential that signer is enrolled with: a passkey signer must stamp with their passkey, and a machine (api-key) signer must stamp with its P-256 api key. Substituting one for the other is rejected, as is any credential the wallet's signing mode does not permit at all — passkey-required wallets accept only passkey approvals, and no credential other than an enrolled passkey or provisioned machine api-key is ever admissible.

**Resolution:** Re-stamp the approval with the exact credential that signer is enrolled with — the enrolled passkey for a passkey signer, or the provisioned machine api-key for a machine signer — and make sure that credential type is allowed by the wallet's signing mode.

<h3 id="api-key-public-key-required">
  API Key Public Key Required
</h3>

`API_KEY_PUBLIC_KEY_REQUIRED` — HTTP 400

credentialType=api\_key requires a publicKey (a P-256 compressed public key in hex).

**Resolution:** Include `publicKey` in the request body when `credentialType` is `api_key`.

<h3 id="api-key-public-key-invalid">
  API Key Public Key Invalid
</h3>

`API_KEY_PUBLIC_KEY_INVALID` — HTTP 400

The provided `publicKey` is not a valid P-256 compressed public key. Expected 66 hex characters (33 bytes) with a `02` or `03` prefix.

**Resolution:** Provide a valid P-256 compressed public key: 66 hex characters starting with `02` or `03`.

<h3 id="api-key-public-key-duplicate">
  Duplicate API Key Public Key
</h3>

`API_KEY_PUBLIC_KEY_DUPLICATE` — HTTP 422

Two api\_key (machine) roster members were submitted with the same `publicKey`. Each machine signer must have a distinct P-256 public key.

**Resolution:** Give each api\_key signer its own unique public key, or remove the duplicate roster entry.

<h3 id="api-key-public-key-in-use">
  API Key Public Key Already In Use
</h3>

`API_KEY_PUBLIC_KEY_IN_USE` — HTTP 409

The `publicKey` on this add-signer request is already held by another active api\_key signer on the customer's roster. Each machine signer must have a distinct P-256 public key.

**Resolution:** Add the signer with a public key that is not already registered on the roster.

<h3 id="customer-already-non-custodial">
  Customer Already Non-Custodial
</h3>

`CUSTOMER_ALREADY_NON_CUSTODIAL` — HTTP 409

Non-custodial control has already been claimed for this customer; the claim endpoint provisions only fresh customers.

**Resolution:** Use the roster-management endpoints to add, remove, promote, or demote signers; the customer is already in the target state.

<h3 id="claim-resume-roster-mismatch">
  Claim Resume Roster Mismatch
</h3>

`CLAIM_RESUME_ROSTER_MISMATCH` — HTTP 409

A stranded claim can be retried, but only with the same roster, signing threshold, and chains it was originally submitted with. This retry differs from what was committed, so it was refused — resuming with a different roster would register signers the wallet provider never provisioned.

**Resolution:** Retry with the exact roster, signingThreshold, and chains from the original claim. If they need to change, contact support to reset the claim before submitting a new one.

<h3 id="customer-already-custodial">
  Customer Already Custodial
</h3>

`CUSTOMER_ALREADY_CUSTODIAL` — HTTP 409

This customer already has a custodial wallet account from initial onboarding. The claim endpoint provisions only fresh customers; an existing custodial wallet cannot be claimed as non-custodial.

**Resolution:** Custodial wallets are operated by Conduit on the customer's behalf; no signer roster applies. Claim non-custodial control only for customers that do not already have a custodial wallet.

<h3 id="customer-kyb-incomplete">
  Customer KYB Incomplete
</h3>

`CUSTOMER_KYB_INCOMPLETE` — HTTP 422

Non-custodial control can only be claimed after the customer's KYB application has been approved.

**Resolution:** Complete and submit the customer's KYB application, wait for approval, and retry the claim once the customer is active.

<h3 id="signer-is-root-member">
  Signer Is Admin
</h3>

`SIGNER_IS_ROOT_MEMBER` — HTTP 409

Admins cannot be removed directly. They must be demoted to a regular member first so the demote flow can verify that quorum and minimum-admin invariants are still satisfied.

**Resolution:** Demote the signer to a non-admin role via the demote endpoint, then call remove.

<h3 id="signer-already-admin">
  Signer Already Admin
</h3>

`SIGNER_ALREADY_ADMIN` — HTTP 409

The promotion target already has the `admin` role.

**Resolution:** No action needed; the signer already holds the role you are trying to assign.

<h3 id="signer-passkey-count-too-low">
  Signer Passkey Count Too Low
</h3>

`SIGNER_PASSKEY_COUNT_TOO_LOW` — HTTP 422

Promotion to admin requires a passkey-credential signer to have at least 2 registered passkeys so they cannot lose admin access by losing a single device. Machine (api\_key) admins do not need passkeys and are exempt (allowed only in the fully-automated signing mode).

**Resolution:** Have the passkey signer register a second passkey from a different device, then retry the promotion.

<h3 id="passkey-attach-pending-admin-approval">
  Passkey Attach Pending Admin Approval
</h3>

`PASSKEY_ATTACH_PENDING_ADMIN_APPROVAL` — HTTP 409

The provider parked the passkey attach at CONSENSUS\_NEEDED (post-activation policy). An admin has been notified and must approve the attach before the passkey becomes usable.

**Resolution:** Wait for the admin to approve the pending request; retry the attach if the approval is declined or expires.

<h3 id="signer-not-admin">
  Signer Not Admin
</h3>

`SIGNER_NOT_ADMIN` — HTTP 409

The demotion target is not currently an admin, so there is nothing to demote.

**Resolution:** Verify the signer ID; only signers with role `admin` are eligible for demotion.

<h3 id="would-break-min-admins">
  Would Break Minimum Admins
</h3>

`WOULD_BREAK_MIN_ADMINS` — HTTP 409

Demoting this signer would leave fewer than 2 admins on the roster, violating the minimum-admin invariant. Because the root quorum runs at fixed capacity, a bare promote-then-demote sequence does not work: promoting a replacement admin also needs to unseat someone, so the swap has to happen in one ceremony.

**Resolution:** Call the promote endpoint on the replacement signer with `demoteSignerId` set to this signer to swap them in one ceremony, instead of demoting standalone.

<h3 id="root-at-capacity-swap-required">
  Root Quorum At Capacity
</h3>

`ROOT_AT_CAPACITY_SWAP_REQUIRED` — HTTP 409

The root quorum already holds its full set of customer admin seats, so a new admin can only be seated by unseating an existing one. Promotion to root requires selecting the current root admin to demote in the same ceremony.

**Resolution:** Retry the promotion with a `demoteSignerId` naming the current root admin to unseat.

<h3 id="demote-target-not-in-root">
  Demote Target Not In Root Quorum
</h3>

`DEMOTE_TARGET_NOT_IN_ROOT` — HTTP 409

The signer named to be unseated in the swap is not currently a member of the root quorum, so there is no root seat to free.

**Resolution:** Choose a `demoteSignerId` that is a current root-quorum admin (see the customer's root roster).

<h3 id="swap-would-break-f12">
  Swap Would Break Minimum Admins
</h3>

`SWAP_WOULD_BREAK_F12` — HTTP 409

The requested seat swap would leave fewer than 2 admins on the roster, violating the minimum-admin invariant that keeps both the root path and recovery satisfiable.

**Resolution:** Add or promote another admin so the roster keeps at least 2 admins after the swap, then retry.

<h3 id="roster-threshold-exceeded">
  Roster Threshold Exceeded
</h3>

`ROSTER_THRESHOLD_EXCEEDED` — HTTP 400

The signing threshold exceeds the number of active signers. Removing a signer would make the quorum unsatisfiable.

**Resolution:** Lower the signing threshold or add more signers before removing this one.

<h3 id="signer-is-last-admin">
  Signer Is Last Admin
</h3>

`SIGNER_IS_LAST_ADMIN` — HTTP 409

This is the last admin on the roster. At least one admin must remain for governance.

**Resolution:** Promote another signer to admin before removing or demoting this one.

<h3 id="role-requires-admin-approval">
  Role Requires Admin Approval
</h3>

`ROLE_REQUIRES_ADMIN_APPROVAL` — HTTP 403

This roster change requires approval from an admin. The request was submitted but is pending admin sign-off.

**Resolution:** Ask an admin signer to approve the pending ceremony.

<h3 id="ceremony-in-flight">
  Ceremony In Flight
</h3>

`CEREMONY_IN_FLIGHT` — HTTP 409

A ceremony for this account is already in progress and the request would duplicate it. This happens for a signer roster change (a concurrent promote or demote of the same signer), or for a wallet create when a wallet for the same account is already being created (for example, requesting a second EVM chain while the first EVM chain's wallet is still being set up — the EVM chains share one address). The request is rejected until the in-flight ceremony finishes.

**Resolution:** Wait for the in-flight ceremony to finish, then retry. For a roster change, the wallet\_signer.promoted / wallet\_signer.demoted webhook signals completion; for a wallet create, the wallet.created webhook signals the wallet is ready.

<h3 id="signer-enrollment-not-enabled">
  Signer Enrollment Not Enabled
</h3>

`SIGNER_ENROLLMENT_NOT_ENABLED` — HTTP 409

Adding this passkey needs a customer admin's co-stamp, and that capability is temporarily unavailable. The request is rejected transiently.

**Resolution:** Retry shortly. The condition clears on its own.

<h3 id="provider-account-not-found">
  Provider Account Not Found
</h3>

`PROVIDER_ACCOUNT_NOT_FOUND` — HTTP 404

No provider account exists for the specified customer for the requested operation. Reset-claim reports it when there is no wallet account to reset; signing-quorum and recovery operations report it when the customer has no non-custodial provider account.

**Resolution:** Confirm the customer has an active wallet account before retrying. Signing-quorum and recovery operations require a non-custodial account specifically.

<h3 id="claim-reset-blocked">
  Claim Reset Blocked by Referencing Rows
</h3>

`CLAIM_RESET_BLOCKED` — HTTP 409

The reset-claim endpoint cannot wipe the customer's wallet setup while transactions, deposits, or pending signature approvals still reference the wallets or signers. Hard-deleting them would FK-violate.

**Resolution:** Resolve any pending signature approvals first. Transactions and deposits permanently reference the wallet (their rows are never removed), so a customer that already has transaction or deposit history cannot be reset.

<h3 id="quorum-threshold-exceeds-signers">
  Quorum Threshold Exceeds Eligible Signers
</h3>

`QUORUM_THRESHOLD_EXCEEDS_SIGNERS` — HTTP 409

The requested signing-quorum threshold is greater than the number of active signers eligible to approve. A threshold can never exceed the signer count, or transactions would become impossible to sign.

**Resolution:** Lower the requested threshold to at most the number of active signers, or add more signers before raising the threshold.

<h3 id="quorum-wallet-override-cannot-raise">
  Wallet Quorum Override Cannot Raise Threshold
</h3>

`QUORUM_WALLET_OVERRIDE_CANNOT_RAISE` — HTTP 422

A per-wallet signing-quorum override may not set a threshold higher than the customer-level threshold. The underlying signing policy cannot yet enforce a raised per-wallet threshold, so allowing it would leave the wallet under-protected.

**Resolution:** Set the per-wallet override at or below the customer-level threshold, or raise the customer-level threshold instead.

<h3 id="wallet-threshold-change-pending">
  Wallet Threshold Change Already Pending
</h3>

`WALLET_THRESHOLD_CHANGE_PENDING` — HTTP 409

A signing-quorum threshold change ceremony is already in progress for this provider account. Concurrent threshold changes are rejected until the in-flight ceremony completes or fails.

**Resolution:** Wait for the in-flight ceremony to terminate, then retry.

<h3 id="document-not-found">
  Document Not Found
</h3>

`DOCUMENT_NOT_FOUND` — HTTP 404

No document exists with the specified ID, or the document belongs to a different organization.

**Resolution:** Verify the document ID is correct. Use `GET /v2/documents` to find valid document IDs.

<h3 id="unsupported-file-type">
  Unsupported File Type
</h3>

`UNSUPPORTED_FILE_TYPE` — HTTP 400

The uploaded file's content does not match an allowed type. File type is determined by inspecting the file's contents (magic bytes), not the filename extension or the Content-Type header. Allowed types: PDF, JPEG, PNG.

**Resolution:** Re-upload using one of the supported formats. Renaming a file or changing its Content-Type header does not change its content type — convert the file to PDF, JPEG, or PNG instead.

<h3 id="file-too-large">
  File Too Large
</h3>

`FILE_TOO_LARGE` — HTTP 413

The uploaded file exceeds the maximum allowed size (10 MB). The response carries an extra 'field' member naming the form-field that exceeded the limit (typically 'file').

**Resolution:** Reduce the file size and re-upload. Multi-page PDFs that exceed the limit should be split into smaller files. High-resolution images may be downscaled or re-encoded with stronger compression.

<h3 id="invalid-file-name">
  Invalid File Name
</h3>

`INVALID_FILE_NAME` — HTTP 400

The uploaded file's name contains characters that are not allowed (path separators, control characters, NUL bytes, or path traversal sequences).

**Resolution:** Rename the file using only printable characters and re-upload. Do not include directory separators (`/`, `\`), `..`, or control characters in the filename.

<h3 id="verification-not-found">
  Verification Not Found
</h3>

`VERIFICATION_NOT_FOUND` — HTTP 404

No verification exists with the specified ID, or the verification belongs to a different organization.

**Resolution:** Verify the verification ID is correct. Use the list verifications endpoint to find valid verification IDs.

<h3 id="verification-not-declinable">
  Verification Not Declinable
</h3>

`VERIFICATION_NOT_DECLINABLE` — HTTP 409

The verification type does not support the decline operation. Only TRANSACTION\_APPROVAL and CEREMONY\_APPROVAL verifications can be declined; other types are resolved by abandonment or expiry.

**Resolution:** Do not call the decline endpoint for this verification type. WALLET\_\* verifications expire automatically via their TTL.

<h3 id="verification-decline-failed">
  Verification Decline Failed
</h3>

`VERIFICATION_DECLINE_FAILED` — HTTP 409

The reject stamp did not land as a rejection on the underlying signing activity. The decline could not be recorded.

**Resolution:** Re-fetch the verification context and retry the decline with a freshly stamped reject credential.

<h3 id="verification-invalid-status">
  Verification Invalid Status
</h3>

`VERIFICATION_INVALID_STATUS` — HTTP 409

The requested operation cannot be performed because the verification is not in the expected status.

**Resolution:** Check the verification's current status. Verifications can only be completed while in pending status.

<h3 id="verification-token-invalid">
  Verification Token Invalid
</h3>

`VERIFICATION_TOKEN_INVALID` — HTTP 400

The verification token is malformed, expired, or does not match any pending verification.

**Resolution:** Request a new verification link. Tokens are single-use and expire after a short window.

<h3 id="feature-already-exists">
  Feature Already Exists
</h3>

`FEATURE_ALREADY_EXISTS` — HTTP 409

A feature with the same identifier already exists for this account.

**Resolution:** Use the existing feature or choose a different identifier.

<h3 id="customer-update-already-pending">
  Customer Update Already Pending
</h3>

`CUSTOMER_UPDATE_ALREADY_PENDING` — HTTP 409

An update application for this customer is already pending or processing. Only one update can be in flight per customer.

**Resolution:** Wait for the in-flight update application to reach a terminal status (approved, rejected, or cancelled) before submitting a new one. List the customer's applications to find it.

<h3 id="onboarding-not-ready">
  Onboarding Not Ready
</h3>

`ONBOARDING_NOT_READY` — HTTP 422

The onboarding submission cannot be completed because one or more required fields or documents are still missing, or a submitted value is not one of the accepted options for a closed-set field (e.g. a country-specific field like `companyClassification.legalStructure`). Each blocker is reported per field in `errors[]` with a `category` and, for closed-set fields, the accepted `allowedValues`.

**Resolution:** Use the onboarding requirements endpoint to check which fields and documents are required and which values each field accepts, then submit valid values before retrying.

<h3 id="onboarding-already-submitted">
  Onboarding Already Submitted
</h3>

`ONBOARDING_ALREADY_SUBMITTED` — HTTP 409

The onboarding application has already been submitted and cannot be submitted again.

**Resolution:** The onboarding is already in review. Check the application status for updates on the review progress.

<h3 id="customer-already-onboarded">
  Customer Already Onboarded
</h3>

`CUSTOMER_ALREADY_ONBOARDED` — HTTP 409

An approved customer already exists for this organization with the same tax identifier.

**Resolution:** Use the existing customer (returned as details.customerId) rather than creating a new one. To replace it, decommission the existing customer first.

<h3 id="webhook-endpoint-not-found">
  Webhook Endpoint Not Found
</h3>

`WEBHOOK_ENDPOINT_NOT_FOUND` — HTTP 404

No webhook endpoint exists with the specified ID, or it belongs to a different organization.

**Resolution:** Verify the endpoint ID is correct. Use the list webhook endpoints endpoint to find valid IDs for your organization.

<h3 id="webhook-delivery-not-found">
  Webhook Delivery Not Found
</h3>

`WEBHOOK_DELIVERY_NOT_FOUND` — HTTP 404

No webhook delivery record exists with the specified ID.

**Resolution:** Verify the delivery ID is correct. Use the list deliveries endpoint to find valid delivery IDs.

<h3 id="webhook-delivery-not-retryable">
  Webhook Delivery Not Retryable
</h3>

`WEBHOOK_DELIVERY_NOT_RETRYABLE` — HTTP 422

The webhook delivery cannot be retried because it is not in a failed state or has exceeded the maximum retry attempts.

**Resolution:** Only failed deliveries can be retried. Check the delivery status before attempting a retry.

<h3 id="unsupported-asset">
  Unsupported Asset
</h3>

`UNSUPPORTED_ASSET` — HTTP 400

The requested asset and chain combination is not supported for this operation.

**Resolution:** Check the list of supported assets for the target chain and retry with a valid combination.

<h3 id="invitation-invalid">
  Invalid or Expired Invitation
</h3>

`INVITATION_INVALID` — HTTP 400

The invitation token is invalid, has already been used, or has expired.

**Resolution:** Request a new invitation from your organization administrator.

<h3 id="invitation-email-mismatch">
  Invitation Addressed to a Different Email
</h3>

`INVITATION_EMAIL_MISMATCH` — HTTP 403

This invitation was sent to a different email address than the signed-in account.

**Resolution:** Sign in with the email the invitation was sent to, or ask an admin to re-send it to your address.

<h3 id="invitation-resend-rate-limited">
  Invitation Resend Rate Limited
</h3>

`INVITATION_RESEND_RATE_LIMITED` — HTTP 429

This invitation was re-sent too recently. Resends are throttled per invitation with a short cooldown so the invitee isn't email-bombed by a double-clicked button or a repeated admin action.

**Resolution:** Wait for the value in the `Retry-After` response header (also in the `retryAfterSeconds` body field) before resending.

<h3 id="rate-alert-not-found">
  Rate Alert Not Found
</h3>

`RATE_ALERT_NOT_FOUND` — HTTP 404

No rate alert exists with the specified ID.

**Resolution:** Check the alert ID and try again.

<h3 id="invalid-address-format">
  Invalid Address Format
</h3>

`INVALID_ADDRESS_FORMAT` — HTTP 400

The provided blockchain address does not match the expected format for the specified chain.

**Resolution:** Verify the address is a valid address for the specified chain and retry.

<h3 id="registered-address-suspended">
  Registered Address Suspended
</h3>

`REGISTERED_ADDRESS_SUSPENDED` — HTTP 409

This address is currently suspended for the customer and cannot be re-registered.

**Resolution:** Unsuspend the existing registration via the internal portal before re-registering.

<h3 id="registered-address-compliance-rejected">
  Registered Address Declined
</h3>

`REGISTERED_ADDRESS_COMPLIANCE_REJECTED` — HTTP 409

This address could not be registered for the customer following compliance screening.

**Resolution:** This address was declined in accordance with our standard compliance and risk management protocols. Contact support if you believe this is in error.

<h3 id="registered-address-invalid-transition">
  Registered Address Invalid Status Transition
</h3>

`REGISTERED_ADDRESS_INVALID_TRANSITION` — HTTP 409

The requested status transition is not allowed for the registered address in its current state.

**Resolution:** Check the registered address's current status before performing status-change operations.

<h3 id="invalid-tax-id-type">
  Invalid Tax ID Type
</h3>

`INVALID_TAX_ID_TYPE` — HTTP 400

The tax ID type provided is not valid for the required TIN submission.

**Resolution:** Use a supported tax ID type for TIN submissions. Refer to the documentation for the list of supported types.

<h3 id="invalid-phone-format">
  Invalid Phone Format
</h3>

`INVALID_PHONE_FORMAT` — HTTP 400

The contact phone number is not in the required E.164 format (e.g., +14155551234).

**Resolution:** Update the business record with a phone number in E.164 format and retry.

<h3 id="payout-not-found">
  Payout Not Found
</h3>

`PAYOUT_NOT_FOUND` — HTTP 404

No payout exists with the specified ID, or the payout belongs to a different organization.

**Resolution:** Verify the payout ID is correct. Use the list payouts endpoint to find valid payout IDs for your organization.

<h3 id="not-in-awaiting-signature">
  Not Awaiting Signature
</h3>

`NOT_IN_AWAITING_SIGNATURE` — HTTP 404

The payout — or the order's source transfer — is not currently parked at the signature-collection gate. Either the signing quorum has already been resolved, or this transfer never required signatures.

**Resolution:** Read the payout or order state. Only a non-custodial transfer with an active signing quorum accepts stamps.

<h3 id="payout-not-cancellable">
  Payout Cannot Be Cancelled
</h3>

`PAYOUT_NOT_CANCELLABLE` — HTTP 409

The payout cannot be cancelled. Either it is already in a terminal state (completed, failed, or cancelled; re-cancelling a `cancelled` payout returns 200 idempotently, every other terminal returns 409), or its on-chain broadcast has begun and the cancel path can no longer safely unwind it.

**Resolution:** Read the payout to confirm its current state. If it is already terminal, no action is required. If broadcast has begun, wait for the payout to reach a terminal state and react to that.

<h3 id="payout-queue-full">
  Payout Queue Full
</h3>

`PAYOUT_QUEUE_FULL` — HTTP 422

This wallet and chain already has the maximum number of payouts waiting to collect signatures. Only one payout collects signatures at a time per wallet and chain; the rest wait in line.

**Resolution:** Wait for an in-flight payout on this wallet and chain to finish signing, then retry. The cap is configurable by Conduit.

<h3 id="idempotency-key-required">
  Idempotency-Key header required
</h3>

`IDEMPOTENCY_KEY_REQUIRED` — HTTP 400

This endpoint requires an Idempotency-Key header to prevent duplicate processing. Generate a unique key per logical request and resend the request.

**Resolution:** Add an Idempotency-Key header with a UUID or other unique value scoped to the request.

<h3 id="idempotency-key-conflict">
  Idempotency Key Conflict
</h3>

`IDEMPOTENCY_KEY_CONFLICT` — HTTP 409

The idempotency key was previously used with a different request body. Idempotency keys are bound to the exact request shape — replays must match the original.

**Resolution:** Use a fresh idempotency key for the new request, or replay the original request unchanged.

<h3 id="idempotency-body-too-nested">
  Idempotency body too deeply nested
</h3>

`IDEMPOTENCY_BODY_TOO_NESTED` — HTTP 400

The request body exceeds the maximum nesting depth allowed by the idempotency fingerprint hasher. Deeply-nested arrays or objects are rejected as a malformed payload.

**Resolution:** Flatten the request body to a reasonable nesting depth (no more than 64 levels). If you believe your payload is legitimately deeper, contact support.

<h3 id="idempotency-key-invalid">
  Idempotency-Key header invalid
</h3>

`IDEMPOTENCY_KEY_INVALID` — HTTP 400

The Idempotency-Key header value did not match the required shape (1-128 characters, letters / digits / underscore / dot / colon / hyphen).

**Resolution:** Resend the request with an Idempotency-Key matching `^[A-Za-z0-9_.:-]{1,128}$` — for example, a UUID.

<h3 id="idempotency-key-request-in-progress">
  Idempotency Key Request In Progress
</h3>

`IDEMPOTENCY_KEY_REQUEST_IN_PROGRESS` — HTTP 409

A request with this idempotency key is already being processed and has not yet completed. Concurrent requests with the same key are rejected to prevent duplicate execution.

**Resolution:** Wait for the original request to complete, then retry the identical request to replay its result. Retry with exponential backoff is safe.

<h3 id="insufficient-funds">
  Insufficient Funds
</h3>

`INSUFFICIENT_FUNDS` — HTTP 422

The customer's available balance for the order's source resource is below the requested amount plus fee.

**Resolution:** Verify the customer's available balance on the order's source resource and retry with a smaller amount, or top up the source.

<h3 id="travel-rule-rejected">
  Travel Rule counterparty rejected
</h3>

`TRAVEL_RULE_REJECTED` — HTTP 422

The counterparty VASP rejected the Travel Rule transfer before the on-chain broadcast. The payout did not broadcast and no funds were moved.

**Resolution:** Confirm beneficiary details with the recipient. Submit a new payout once the underlying counterparty issue has been addressed.

<h3 id="user-signature-timeout">
  User signature timeout
</h3>

`USER_SIGNATURE_TIMEOUT` — HTTP 422

The payout waited too long in the wallet's signing queue (behind other in-flight payouts on the same wallet) and timed out before it could start collecting signatures. No funds were moved.

**Resolution:** Submit a new payout once the wallet's earlier payouts have finished signing.

<h3 id="user-signature-expired">
  User signature expired
</h3>

`USER_SIGNATURE_EXPIRED` — HTTP 422

The customer did not approve the payout across the allowed signing windows (the request is re-offered with a fresh link each time and only fails after the final window), so the request expired. No funds were moved.

**Resolution:** Submit a new payout when the customer is ready to sign.

<h3 id="user-signature-declined">
  User signature declined
</h3>

`USER_SIGNATURE_DECLINED` — HTTP 422

The customer declined the payout from the approval page. No funds were moved.

**Resolution:** Submit a new payout if the decline was unintentional.

<h3 id="user-signature-rejected-by-provider">
  Customer signature could not be accepted
</h3>

`USER_SIGNATURE_REJECTED_BY_PROVIDER` — HTTP 422

The customer's passkey approval could not be accepted. No funds were moved.

**Resolution:** Submit a new payout. If the same customer or wallet hits this repeatedly, contact support.

<h3 id="roster-changed">
  Signer roster changed
</h3>

`ROSTER_CHANGED` — HTTP 422

A signer was removed (or moved out of the signing pool) while their stamp was on this in-flight payout. No funds were moved.

**Resolution:** Re-initiate the payout; the new attempt collects approvals from the current roster.

<h3 id="chain-broadcast-failed">
  On-chain broadcast failed
</h3>

`CHAIN_BROADCAST_FAILED` — HTTP 422

The payout could not be signed or broadcast to the blockchain before reaching finality. No funds left the wallet.

**Resolution:** Submit a new payout. If the same wallet hits this repeatedly, contact support.

<h3 id="crypto-wallet-misconfigured">
  Crypto wallet misconfigured
</h3>

`CRYPTO_WALLET_MISCONFIGURED` — HTTP 422

The wallet's configuration prevents Conduit from moving funds from it. No funds were moved.

**Resolution:** Conduit is investigating automatically. Contact support if the wallet is needed for a time-sensitive payout.

<h3 id="crypto-feature-not-approved">
  Crypto wallet feature not approved
</h3>

`CRYPTO_FEATURE_NOT_APPROVED` — HTTP 422

Non-custodial control can only be claimed after the customer's CRYPTO\_WALLET feature application has been approved. Either no feature application exists yet, or it is still pending review.

**Resolution:** Submit POST /v2/customers/:id/features \{ type: crypto\_wallet } and wait for the application to reach status: approved before retrying. In sandbox the default is auto-approve; in live the application may need ops review unless the org has opted out of the review gate.

<h3 id="signing-mode-roster-invalid">
  Roster does not match the customer's signing mode
</h3>

`SIGNING_MODE_ROSTER_INVALID` — HTTP 422

The submitted signer roster is not valid for this customer's configured signing mode. In PASSKEY\_REQUIRED mode all signers must be passkeys; in PROGRAMMATIC mode machine (api\_key) signers must have role=signer and governance admins must be passkeys.

**Resolution:** Adjust the roster to the customer's signing mode, or ask your Conduit representative to change the mode. To use machine signers at all, the customer must be in PROGRAMMATIC or PROGRAMMATIC\_UNATTENDED; machine admins require PROGRAMMATIC\_UNATTENDED.

**Guide:** [Programmatic payout signing](/guides/machine-signer-stamping)

<h3 id="crypto-not-available-in-jurisdiction">
  Crypto not available in this jurisdiction
</h3>

`CRYPTO_NOT_AVAILABLE_IN_JURISDICTION` — HTTP 422

Crypto wallet provisioning is not available for customers in this jurisdiction. The customer's registered country is on Conduit's restricted-country list for crypto products.

**Resolution:** Crypto rails are not legally available in the customer's country. KYB customers in this country may still use other rails (e.g., fiat virtual accounts) but cannot enable the CRYPTO\_WALLET feature.

<h3 id="crypto-wallet-consensus-required">
  Crypto wallet operation requires approval
</h3>

`CRYPTO_WALLET_CONSENSUS_REQUIRED` — HTTP 422

This wallet operation needs a customer admin's approval before it can complete; it could not be finished synchronously. No funds were moved and no wallet was created.

**Resolution:** Do not retry blindly — the operation only completes once a customer admin approves it. Complete the pending approval, then re-check the wallet state.

<h3 id="compliance-hold">
  Transaction held for compliance review
</h3>

`COMPLIANCE_HOLD` — HTTP 422

This transaction is held pending a regulatory compliance review and could not be completed. The transaction's funds are held, not returned, pending the review.

**Resolution:** Contact support. This cannot be retried without a compliance review.

<h3 id="compliance-review-rejected">
  Transaction declined — compliance review required
</h3>

`COMPLIANCE_REVIEW_REJECTED` — HTTP 422

This transaction could not be completed due to a regulatory compliance review. No funds were moved.

**Resolution:** Contact support. This transaction cannot be retried without a compliance review.

<h3 id="compliance-rejected">
  Payout rejected in compliance review
</h3>

`COMPLIANCE_REJECTED` — HTTP 422

A compliance reviewer rejected the payout's supporting documentation. No funds were moved; the reserved amount was returned to the available balance.

**Resolution:** Upload an acceptable supporting document via POST /v2/documents and submit a new payout with a fresh idempotency key. The transaction.rejected event lists the accepted document types.

<h3 id="returned-by-sender">
  Returned by sender
</h3>

`RETURNED_BY_SENDER` — HTTP 422

The inbound transfer was returned by the sender's institution, or compliance marked the deposit as returned before credit. The deposit was not credited.

**Resolution:** Contact the sender's bank or Conduit support for the return reason. The customer can attempt the transfer again from the source after the issue is resolved.

<h3 id="rail-policy-rejected">
  Payment rail rejected the transaction
</h3>

`RAIL_POLICY_REJECTED` — HTTP 422

The payment rail's policy rejected the transaction (for example, amount limit, frequency cap, or recipient restriction).

**Resolution:** Adjust the amount, recipient, or wait period and submit a new transaction. Contact support if the cause is unclear.

<h3 id="insufficient-funds-at-settle">
  Insufficient funds at settlement
</h3>

`INSUFFICIENT_FUNDS_AT_SETTLE` — HTTP 422

Funds were available at reservation but not at settlement. No money was moved.

**Resolution:** Top up the funding source and submit a new transaction.

<h3 id="rail-unavailable">
  Payment rail unavailable
</h3>

`RAIL_UNAVAILABLE` — HTTP 503

No viable payment rail was available for the requested corridor. No funds were moved.

**Resolution:** Retry later. If the corridor is persistently unavailable, contact support.

<h3 id="sender-info-timeout">
  Sender information deadline expired
</h3>

`SENDER_INFO_TIMEOUT` — HTTP 422

The sender-information gate timed out before the required Travel Rule details were provided. The deposit could not be completed.

**Resolution:** Submit the deposit again with the sender details included up front.

<h3 id="sandbox-not-provisioned">
  Sandbox Not Provisioned
</h3>

`SANDBOX_NOT_PROVISIONED` — HTTP 409

The API key is valid, but the sandbox organization it belongs to has not been provisioned yet. Sandbox access is provisioned from the live organization shortly after sign-up; this state means that provisioning has not completed (or previously failed). The key itself does not need to be rotated.

**Resolution:** Wait a few moments and retry — provisioning is retried automatically. If the error persists, contact support to re-provision your sandbox environment.

<h3 id="kyc-inquiry-not-found">
  KYC Inquiry Not Found
</h3>

`KYC_INQUIRY_NOT_FOUND` — HTTP 404

No KYC inquiry id is persisted for the requested application + person index. Either the application did not spawn a KYC inquiry (e.g. mock sandbox path, kybRelianceEnabled=true), or the inquiry.created webhook from the KYC provider has not yet enriched the field verification row with the native inquiry id.

**Resolution:** Wait a few seconds for the inquiry.created webhook to land, then retry. If the application was created in sandbox mode or with KYB reliance, no KYC inquiry exists for it.

<h3 id="kyc-inquiry-link-unavailable">
  KYC Inquiry Link Unavailable
</h3>

`KYC_INQUIRY_LINK_UNAVAILABLE` — HTTP 409

The KYC provider refused to generate a new one-time link for the inquiry, typically because the inquiry has expired, has already been completed, or has been resolved (approved/declined). This is a permanent state for that inquiry.

**Resolution:** Check the inquiry status with the KYC provider. If the inquiry is expired or completed, no further hosted-flow link can be issued; start a new inquiry if a re-share is still needed.

<h3 id="kyc-upstream-unavailable">
  KYC Upstream Unavailable
</h3>

`KYC_UPSTREAM_UNAVAILABLE` — HTTP 502

The KYC provider returned a server error or the request did not reach it at all (network failure, timeout). The inquiry itself is fine; the provider just couldn't be contacted right now.

**Resolution:** Retry the request after a brief delay. If the failure persists, check the KYC provider's status page before assuming the inquiry is broken.

<h3 id="kyc-inquiry-link-rate-limited">
  KYC Inquiry Link Rate Limited
</h3>

`KYC_INQUIRY_LINK_RATE_LIMITED` — HTTP 429

A regenerate-link request is already in flight for this UBO. The endpoint serialises requests per UBO with a short lock so the KYC provider isn't hammered with duplicate one-time-link requests from a double-click or a stuck retry.

**Resolution:** Wait for the value in the `Retry-After` response header (also in the `retryAfterSeconds` body field) before retrying.

<h3 id="verified-individual-locked">
  Verified Individual Cannot Be Edited
</h3>

`VERIFIED_INDIVIDUAL_LOCKED` — HTTP 409

An individual whose identity verification has cleared cannot have their identity fields (firstName, lastName, email) changed in place — the verification was tied to those specific values. Organizational fields (roles, ownership percent, shares allocated) remain editable, and the individual can still be removed entirely.

**Resolution:** Remove the individual from the application and add them again to change their identity, then have them complete identity verification under the new identity. Roles and ownership percent can be edited without removing the individual.

<h3 id="whitelist-recipient-not-found">
  Whitelist Recipient Not Found
</h3>

`WHITELIST_RECIPIENT_NOT_FOUND` — HTTP 404

No whitelist recipient with this id exists for your organization.

**Resolution:** Check the id; list entries via GET /v2/customers/\{customerId}/whitelist-recipients.

<h3 id="whitelist-invalid-transition">
  Invalid Whitelist Transition
</h3>

`WHITELIST_INVALID_TRANSITION` — HTTP 409

The whitelist entry is not in a status that allows this action.

**Resolution:** Fetch the entry to inspect its current status.

<h3 id="whitelist-recipient-conflict">
  Whitelist Recipient Conflict
</h3>

`WHITELIST_RECIPIENT_CONFLICT` — HTTP 409

An active whitelist entry already exists for these bank details with different attributes.

**Resolution:** Fetch the existing entry; revoke it first if you need to change attributes, or resubmit with identical details.

<h3 id="documentation-required">
  Documentation Required
</h3>

`DOCUMENTATION_REQUIRED` — HTTP 422

This payout purpose requires a supporting document and none was attached.

**Resolution:** Upload a document via POST /v2/documents with purpose=transaction\_support, attach its id in `documents`, and resubmit with a new idempotency key. `acceptedDocumentTypes` lists the kinds of evidence that satisfy review — any supported upload type is accepted at submission.

<h3 id="recipient-not-whitelisted">
  Recipient Not Whitelisted
</h3>

`RECIPIENT_NOT_WHITELISTED` — HTTP 422

purpose=intercompany requires the recipient to be a registered intercompany whitelist entry for this customer.

**Resolution:** For bank recipients, register via POST /v2/customers/\{customerId}/whitelist-recipients and wait for the whitelist\_recipient.registered webhook. For crypto destinations, register the wallet address via POST /v2/customers/\{customerId}/wallets/registered-addresses (synchronous). Then resubmit under the same idempotency key.

<h3 id="transaction-blocked">
  Transaction Blocked
</h3>

`TRANSACTION_BLOCKED` — HTTP 422

The transaction is blocked by policy. This decision is terminal.

**Resolution:** This transaction cannot be processed. Contact support if you believe this is an error.

<h3 id="capability-suspended">
  Capability Suspended
</h3>

`CAPABILITY_SUSPENDED` — HTTP 403

This account is currently restricted from initiating this type of money movement.

**Resolution:** Contact support if you believe this is an error.

<h3 id="ceremony-not-found">
  Ceremony Not Found
</h3>

`CEREMONY_NOT_FOUND` — HTTP 404

No recovery ceremony matching the given ID was found.

**Resolution:** Verify the ceremony ID.

<h3 id="ceremony-not-cancellable">
  Ceremony Not Cancellable
</h3>

`CEREMONY_NOT_CANCELLABLE` — HTTP 409

The ceremony is not in a PENDING state and cannot be cancelled.

**Resolution:** Only PENDING ceremonies can be cancelled.

<h3 id="recovery-replacement-not-enrolled">
  Replacement Admin Not Enrolled
</h3>

`RECOVERY_REPLACEMENT_NOT_ENROLLED` — HTTP 409

The replacement admin for an admin-recovery must have completed enrollment (be ACTIVE with a provider credential) before being seated into the root quorum.

**Resolution:** Complete the replacement admin's enrollment before initiating admin recovery.

<h3 id="rfi-not-found">
  RFI Not Found
</h3>

`RFI_NOT_FOUND` — HTTP 404

No request-for-information exists with the specified ID, the RFI belongs to a different organization, or (on a client-facing surface) the RFI is still a draft and has never been published.

**Resolution:** Verify the RFI ID is correct. Draft RFIs are never visible on client-facing surfaces — wait until the RFI is published.

<h3 id="rfi-not-open-for-response">
  RFI Not Open For Response
</h3>

`RFI_NOT_OPEN_FOR_RESPONSE` — HTTP 409

A response can only be submitted while the RFI is open. This RFI is not open — the current round may already be answered, or the RFI has been resolved or cancelled.

**Resolution:** Re-fetch the RFI to check its current status. If it is awaiting internal review (responded) or already closed, no further response can be submitted.

<h3 id="rfi-invalid-status-transition">
  RFI Invalid Status Transition
</h3>

`RFI_INVALID_STATUS_TRANSITION` — HTTP 409

The requested action cannot be performed from the RFI's current status. Each RFI action (publish, request-more-info, resolve, cancel, patch) is only valid from a specific set of prior statuses.

**Resolution:** Re-fetch the RFI to check its current status and refer to the documented lifecycle transitions before retrying.
