Errors
Every error returns the same shape: an RFC 9457 problem document served as application/problem+json. A client can branch on the HTTP status, on the machine-readable code, or show the human detail, without parsing prose.
The request below runs in CI against an in-process core on FakeChain, so a response that stops matching its # expect line fails the build. The {{KEY}} placeholder works as in the quickstart.
The problem document
This POST omits the required Idempotency-Key header, so it is rejected with idempotency_key_missing before any work starts.
POST /v1/counterparties
authorization: Bearer {{KEY}}
content-type: application/json
{
"type": "bank_account",
"beneficiary_name": "Ada Lovelace",
"bank_name": "Analytical Bank",
"account_number": "123456789",
"routing_code": "021000021",
"currency": "USD"
}
# expect 400
The body is a problem document:
{
"type": "https://api.issuer.example/problems/idempotency_key_missing",
"title": "Bad Request",
"status": 400,
"detail": "Idempotency-Key header is required on POST",
"code": "idempotency_key_missing",
"request_id": "req_7f3c..."
}
| Field | Meaning |
|---|---|
type | A URI that identifies the problem kind; it is stable for a given code. |
title | A short summary of the HTTP status, such as Bad Request. |
status | The HTTP status code, repeated in the body. |
detail | Why this request failed, in human words. |
code | The machine-readable code to branch on. |
request_id | The id of the request; quote it when you contact the operator. |
errors | Optional. Per-field problems, each with a path and a message. |
A validation failure adds errors, one entry per bad field:
{
"type": "https://api.issuer.example/problems/invalid_request",
"title": "Bad Request",
"status": 400,
"detail": "invalid request",
"code": "invalid_request",
"request_id": "req_2a91...",
"errors": [{ "path": "/amount", "message": "must be greater than zero" }]
}
Error codes
| Code | Status | Meaning |
|---|---|---|
invalid_request | 400 | The request is malformed, or names a field the endpoint does not accept. |
invalid_amount_precision | 400 | An amount string has more fractional digits than the asset's scale. |
unsupported_asset | 400 | The named asset or currency is not configured. |
idempotency_key_missing | 400 | A POST arrived without an Idempotency-Key. |
ownership_proof_invalid | 400 | The signed ownership challenge did not verify. |
ownership_challenge_expired | 400 | The ownership challenge was signed too late. |
envelope_mismatch | 400 | The submitted envelope does not match the offer it signs. |
holder_signature_missing | 400 | The wallet's signature is absent from the envelope. |
sponsor_signature_missing | 400 | The sponsor's signature is absent from the envelope. |
enrolment_token_invalid | 400 | The enrolment token is unknown, already used, or expired. |
invitation_invalid | 400 | The invitation is unknown, already used, or revoked. |
invitation_expired | 400 | The invitation's time window has passed. |
webauthn_verification_failed | 400 | The passkey assertion did not verify. |
reason_required | 400 | An operator write arrived without its reason. |
invalid_credentials | 401 | A console login failed its TOTP or passkey check. |
invalid_api_key | 401 | The bearer token is unknown or malformed. |
authentication_required | 401 | The request carried no credentials. |
insufficient_scope | 403 | The API key lacks the scope this operation needs. |
capability_missing | 403 | The organisation has not unlocked this capability. |
step_up_required | 403 | The session has not been re-verified in the last five minutes. |
sandbox_disabled | 403 | A sandbox tool was called off Testnet. |
not_found | 404 | The object does not exist, or belongs to another organisation. |
idempotency_request_in_progress | 409 | A request with this key is still running. |
state_conflict | 409 | The object is not in a state that allows this move. |
mints_paused | 409 | Minting is paused for this asset. |
wallet_already_registered | 409 | A wallet with this address is already registered. |
sponsorship_not_offered | 409 | No sponsored-trustline offer is open for this wallet. |
email_taken | 409 | A user with this email already exists. |
idempotency_key_reused | 422 | The key was already used for a different request. |
quote_expired | 422 | The quoted amount is past its expiry. |
insufficient_funds | 422 | The balance is too low for this transfer. |
insufficient_reserve_headroom | 422 | The reserve rule does not allow more supply. |
destination_no_trustline | 422 | The destination wallet cannot hold the asset. |
destination_frozen | 422 | The destination wallet's trustline is frozen. |
offer_expired | 422 | The sponsored-trustline offer has expired. |
stellar_tx_failed | 422 | The submitted transaction failed on chain. |
rate_limited | 429 | The request exceeds a rate limit; see Rate limits. |
internal_error | 500 | An unexpected server fault; retry, and quote the request_id if it persists. |
upstream_unavailable | 503 | A dependency is unavailable; retry with backoff. |
The catalogue is generated into the OpenAPI document; the same codes appear on the Problem schema as the code enum.