Errors
Every error response is a ProblemDetails-shaped JSON body with a stable code string — build
your recovery logic against code, not the HTTP status alone (several distinct errors share the
same status). This page covers the errors most integrators actually need to handle; it is not
exhaustive of every possible validation message.
General shape
| HTTP status | General meaning | What to do |
|---|---|---|
400 | Structural/type validation failure (malformed request). | Fix the request shape; never retry unchanged. |
401 | Missing or invalid credential (X-Api-Key or Authorization: Bearer) — see Authentication. | Check which of the 3 principals the route needs; never retry with the same credential. |
403 | Credential is valid, but not authorized for this specific action/resource (e.g. cross-Organization access, or a route that requires a Member JWT and got an API Key instead). | Check scoping/permissions; never retry unchanged. |
404 | The resource doesn't exist, or doesn't belong to the caller's Organization (the two are deliberately indistinguishable to avoid leaking existence across tenants). | Confirm the ID and that it belongs to your Organization. |
409 | A conflict with existing state — usually a "first-registration-wins" rule or an idempotency key reused with a different payload. | See specific codes below; usually means "already done," not "retry." |
422 | The request is well-formed but violates a business rule (insufficient balance, unsupported mode, etc.). | See specific codes below; fix the underlying condition, then retry. |
Idempotency conflicts
| Code | Status | Meaning | What to do |
|---|---|---|---|
IDEMPOTENCY_KEY_CONFLICT | 409 | The Idempotency-Key (or body field, depending on the route) was already used with a different payload. Real replay of the exact same payload with the same key returns the original result instead of erroring. | Never generate a new key and blindly retry — if the original request actually succeeded, retrying with a new key double-executes it. Use a stable, deterministic key per real intent (see Idempotency). |
PAYOUT_BATCH_IDEMPOTENCY_KEY_CONFLICT | 409 | Same rule, specific to CreatePayoutBatch. | Same as above. |
Network Execution
| Code | Status | Meaning | What to do |
|---|---|---|---|
NETWORK_EXECUTION_FEE_INSUFFICIENT_BALANCE | 422 | The NetworkCostPayerAccount doesn't have enough balance to cover the charged network cost. The response body's extensions include payerAccountId, availableBalance, requiredAmount, deficit, billingAsset, and remediation: "DEPOSIT_REQUIRED" — enough to resolve it programmatically without parsing the message text. | Fund the Account (a normal Deposit) for at least deficit more billingAsset, then retry — see Network Execution § First funding. |
CUSTOMER_NETWORK_RESOURCE_INSUFFICIENT | 422 | Under CUSTOMER_RESOURCES mode, your registered ExecutionSource doesn't have enough declared on-chain resource capacity. Fails closed — no silent fallback to ISHTARAN_RESOURCES unless you opted into allowFallbackToIshtaranResources. | Increase your on-chain resources and re-sync via executionSources.syncResourceStake(...), or explicitly opt into fallback. |
CUSTOMER_RESOURCE_STAKE_INVALID | 400 | The values passed to syncResourceStake are structurally invalid (e.g. negative). | Fix the request; never retry unchanged. |
EXECUTION_SOURCE_ALREADY_REGISTERED | 409 | First-registration-wins — an ExecutionSource already exists for that Wallet/AssetNetwork pair. | Fetch the existing registration instead of registering again. |
NETWORK_COST_PAYER_ACCOUNT_ALREADY_REGISTERED | 409 | First-registration-wins — a NetworkCostPayerAccount already exists for that Organization/AssetNetwork pair. | Fetch the existing registration instead of registering again. |
EXECUTION_DESTINATION_ALREADY_REGISTERED | 409 | First-registration-wins — an ExecutionDestination already exists for that Account/AssetNetwork pair. | Fetch the existing registration instead of registering again. |
Payout
| Code | Status | Meaning | What to do |
|---|---|---|---|
PAYOUT_POLICY_MODE_NOT_SUPPORTED | 422 | An attempt to configure PayoutPolicy to THRESHOLD or SCHEDULED — only IMMEDIATE and MANUAL are supported today (see Payout in each SDK's CORE_API.md). | Use IMMEDIATE or MANUAL; don't build against the other two as available capabilities. |
PAYOUT_BATCH_TRIGGER_NOT_SUPPORTED | 422 | The public CreatePayoutBatch route only ever creates a MANUAL-triggered batch — there is no trigger field on the public request. | Call CreatePayoutBatch with no trigger field; THRESHOLD/SCHEDULED automation is not reachable through this public route. |
Validation
| Code | Status | Meaning | What to do |
|---|---|---|---|
VALIDATION_ERROR | 400 | Structural request validation failed (missing/malformed field). The response includes the specific field errors. | Fix the request shape per the returned detail; never retry unchanged. |
Self-Custody
See Self-Custody § Errors for SIGNED_TRANSACTION_MISMATCH —
a signature whose recomputed canonical hash doesn't match is rejected outright, and the leg is
never broadcast.