Docs
⌘K
Resources / Errors

Errors

Every error returns the same flat envelope. The HTTP status gives the class of problem, code tells you what to do about it, and request_id is what support will ask for. This page is the catalogue, grouped by where you will hit them.

The envelope

{
  "code": "validation_failed",
  "message": "One or more fields are invalid.",
  "status": 422,
  "request_id": "req_7d3c91f4a2",
  "field_errors": [
    {
      "field": "country",
      "code": "invalid_format",
      "message": "Must be an ISO 3166-1 alpha-2 code."
    }
  ]
}
FieldAlways presentNotes
codeYesStable. This is what you branch on.
messageYesHuman-readable and subject to rewording. Never parse it.
statusYesMirrors the HTTP status, so the body is self-contained in logs.
request_idYesLog it. It is the single identifier support needs to trace a failure.
field_errorsNoPresent on validation_failed. An array of {field, code, message}.
detailNoAdditional structured context for some errors.
typeNoPresent on some errors for finer classification.
Branch on the code, show the message, log the request_id

Codes are stable and safe to switch on. Messages are for humans and may be reworded. And when something goes wrong that this page does not explain, request_id is what turns a support conversation into a lookup.

Status classes

StatusClassGeneral handling
400The request itself is wrong — unparseable body, missing body, or nothing to doRead the code — it names the rule. This is not the general field-validation status.
401Authentication failedCheck which credential you sent, and whether it was rotated.
403Authenticated, but not permittedNot retryable as-is. Scope, role, or accreditation forbids it.
404No such object, or not yoursDo not distinguish these in your own UI.
409State conflict — the object is not in a state that allows thisRe-read the object. Retrying the same call will fail identically.
422Validation failed — well-formed but invalidfield_errors[] names the fields. Surface each against its own field.
429Rate limitedBack off with jitter.
5xxNustro-side failureRetry idempotent calls with backoff. Never retry a key-issuing call without an Idempotency-Key.
Nustro enforces

Three statuses, three questions. 400 — was the request itself well formed? 422 — were the values valid? 409 — does the object’s current state allow it? Field validation is 422 validation_failed carrying field_errors[] — never a per-field 400. Build form handling against field_errors generically and you will not need a new case each time a field is added.

409 is not transient

It means your view of the object disagrees with Nustro’s. Retry loops on 409 generate load and never succeed — re-read, then decide. The one exception is idempotency_key_processing, below.

Authentication and access

CodeHTTPMeaningWhat to do
invalid_credentials401The credential is wrong, unknown, or revokedCheck the key; it may have been rotated
certificate_required401A runtime call arrived without an agent certificateAgents authenticate with AEAP-Certificate, never a management key
aeap_verification_failed401The certificate or proof did not verify, or the timestamp is outside the replay windowCheck clock sync; refetch the issuer JWKS if a key rotated
email_not_verified403The account exists but its email is unverifiedComplete email verification
account_suspended403The platform account is suspendedContact support
tier_exceeds_accreditation403The action requires a higher accreditation than the account holdsNot retryable; accreditation must be raised
production_locked409A production action attempted while production is unavailableRe-read your account status (GET /v1/platform)
forbidden403Generic authorisation failure — the caller is not a party to this objectCheck the identifiers in the path

Account and onboarding

CodeHTTPMeaningWhat to do
email_exists409Registration attempted with an address that already has an accountSign in or reset; do not create a second account
email_already_in_use409An email change collided with an existing accountChoose a different address
confirmation_mismatch422A typed confirmation did not match — account closure, for exampleField-level; correctable
current_password_invalid422Password change with the wrong current passwordField-level; correctable
kyb_in_progress409A verification submission is already under reviewWait for the decision rather than resubmitting
verification_already_accredited409Verification submitted for an already accredited accountNo action needed
not_accredited409A production action attempted before accreditationWait for platform.verification_decided
already_promoted409The account is already on productionDo not auto-retry. The key is not returned again; rotate instead
key_not_provisioned409A production key requested before it existsRe-read your account status (GET /v1/platform)
close_blocked_escrow409Closure attempted with funded escrowRelease escrow first
close_blocked_open_disputes409Closure attempted with open disputesCases must resolve before closure

Document upload

CodeHTTPMeaningWhat to do
document_type_invalid422Not a recognised document typeUse a type from the required list
file_too_large422The upload exceeds the size limitCompress or split before retrying
unsupported_file_type422The file format is not acceptedConvert to an accepted format

Principals and agents

CodeHTTPMeaningWhat to do
external_ref_exists409A principal already exists with that external_refDeduplication is by external_ref — re-read rather than creating a duplicate
customer_not_empty409A Customer deleted while it still holds principalsReassign or ungroup them first — a label is never deleted out from under live principals
tenant_principal_field_locked409A field on your auto-created tenant principal is not editable here — including grouping it under a CustomerEdit it in your company profile — one writer, not two
invalid_status409The object is not in a status that permits this action — activating an agent that is not DRAFT, for exampleRe-read the object. The request was valid; the state forbids it
field_not_allowed_for_role422A field was set that does not apply to the agent’s economic roleRemove it — the role determines which pillars apply. Named in field_errors
invalid_market_format422The market identifier is malformedCheck the jurisdiction-currency format. Named in field_errors
role_not_permitted403The agent’s role does not permit this market configurationConsumers have no markets to configure
environment_mismatch409Sandbox and production objects were mixed in one operationCheck which key you sent, and the object’s environment
agent_status_not_mutable409The agent’s lifecycle status does not allow this edit — identity and scope are not editable on a REVOKED or TERMINATED agentRe-read the agent. Not retryable as-is
use_activate_endpoint409DRAFTACTIVE was attempted through the status patchCall POST /v1/agents/{did}/activate — that transition issues a keypair and certificate, so it has its own endpoint
escrow_constrained_cannot_reactivate409Reactivation attempted while escrow is CONSTRAINEDResolve the escrow deficiency first; the agent cannot return to ACTIVE under-funded
no_changes_requested400An identity or scope patch contained no changesSend only when something changed. This is a request problem, not a validation failure — hence 400
catalog_url_not_permitted422catalog_url was set on a CONSUMER agentThe field is PROVIDER/ENTERPRISE only. Do not offer it on consumer agents
cert_tier_role_mismatch422The requested certificate tier does not exist for that economic roleField-level, inside field_errors
authorized_action_role_mismatch422An authorized action does not exist for that economic roleField-level, inside field_errors

Runtime and settlement

CodeHTTPMeaningWhat to do
spend_policy_violation403A ceiling, window, or counterparty floor refused the payment intentNot retryable. The principal must adjust scope
settlement_verification_failed409The on-chain settlement could not be verified against the intent — not found, wrong amount, or wrong networkConfirm the transaction on-chain before facilitating
facilitation_not_found404No facilitation record for this referenceDelivery must follow a successful facilitate call
confirmation_window_closed409Confirmation arrived after the window closed and the task auto-confirmedThe outcome stands; re-read the task

Disputes

CodeHTTPMeaningWhat to do
invalid_reason422The reason is not an accepted valueUse not_delivered, not_as_described, unauthorized, or other
invalid_disputed_amount422The disputed amount is malformed or exceeds the settled amountField-level — the error a user can fix themselves
dispute_window_expired409The dispute window elapsedNot recoverable; do not offer the form again
final_sale409The transaction settled with a dispute window of zeroNot recoverable. Show final sale before the user tries
dispute_exists409A dispute is already open on that transactionRender the existing case instead of the form

Idempotency and rate limits

CodeHTTPMeaningWhat to do
idempotency_key_reused422The same key was sent with a different request bodyUse a fresh key for a genuinely different request
idempotency_key_processing409The original request with this key is still in flightRetry with the same key. Do not generate a new one
rate_limited429Too many requestsBack off with jitter
Nustro enforces

idempotency_key_processing is the one 409 you should retry — with the same key. Generating a new key at that moment is how one timed-out request becomes two objects.

Handling patterns

PatternApplies to
Map field_errors to your formEvery 422. Build it once, generically, rather than adding a case per field.
Re-read, then decideEvery 409 except idempotency_key_processing. Fetch the object, reconcile, then proceed differently or explain.
Do not auto-retryalready_promoted, and any failure on a call that returns a one-time secret. A blind retry destroys the credential rather than recovering it.
Translate for principalsspend_policy_violation, final_sale, dispute_window_expired, tier_exceeds_accreditation. Each has a concrete remedy — or a concrete reason there is none. Say which.
Always log request_idEverything. It is the difference between a support ticket and a lookup.