API/Errors and refusals
v1.0.0-beta.mdGet API keys
Core rules

Errors and refusals

Two shapes only: a refusal is HTTP 200 with allowed:false and a machine-stable reason; a real error is an HTTP status with { error, code }. Branch on reason.

Shape one — a refusal

HTTP 200, allowed: false, and three message layers:

LayerAudienceRule
reasonYour codeMachine-stable, never translated, never in a locale file
developer_messageYour logsAlways English
user_messageThe personLocalised by accept-language (en, pt-BR, es); absent on integration errors, by design

The same refusal, three times, one per Accept-Language value. Only user_message changes; reason never does.

Accept-Language: en
{
  "allowed": false,
  "reason": "ceiling_exceeded",
  "developer_message": "Requested 18900 exceeds the remaining daily ceiling of 12000.",
  "user_message": "This is above the daily limit you set.",
  "step_up_available": true
}
Accept-Language: pt-BR
{
  "allowed": false,
  "reason": "ceiling_exceeded",
  "developer_message": "Requested 18900 exceeds the remaining daily ceiling of 12000.",
  "user_message": "Isso está acima do limite diário que você definiu.",
  "step_up_available": true
}
Accept-Language: es
{
  "allowed": false,
  "reason": "ceiling_exceeded",
  "developer_message": "Requested 18900 exceeds the remaining daily ceiling of 12000.",
  "user_message": "Esto supera el límite diario que definiste.",
  "step_up_available": true
}

Refusal reasons

reasonWhen it firesstep_up_availableWhat to do
ceiling_exceededThe amount or count would pass the ceiling for the periodpresentOffer a step-up mandate, or act within remaining
scope_mismatchThe requested scope is not in the mandatepresentAsk the person for a mandate that carries the scope
mandate_expiredexpires_at has passedIssue a new mandate from a fresh capture
mandate_revokedThe mandate was revokedStop; do not retry
constraint_violatedThe merchant, category or country is outside the constraintsRoute the action elsewhere or ask for a wider mandate
liveness_requiredThe act needs a fresh live captureSend the person to a capture surface
not_foundUnknown mandate, or one the caller may not readDo not probe; unknown and unauthorised answer identically
rate_limitedToo many callsBack off and retry
idempotency_key_requiredmandate-consume without Idempotency-KeyAdd the header and retry

liveness_required is a refusal in a 200 body. It is not a 422.

Shape two — a real HTTP error

{ "error": "Amount must be an integer in the minor unit", "code": "AMOUNT_INVALID" }
StatusCodes
400INVALID_REQUEST, INVALID_INPUT (carries fields), AMOUNT_INVALID, UNKNOWN_SCOPE, WILDCARD_SCOPE_FORBIDDEN, AMOUNT_CEILING_NOT_APPLICABLE, CONSTRAINTS_REQUIRED_FOR_SCOPE, AGENT_DISPLAY_NAME_TOO_LONG, AGENT_DESCRIPTOR_TOO_LONG, AGENT_DISPLAY_NAME_EMPTY, AGENT_DESCRIPTOR_EMPTY
401API_KEY_REQUIRED, INVALID_API_KEY, AUTH_REQUIRED (a pk_ on a server endpoint)
403DOCUMENT_REQUIRED, CONSENT_REQUIRED, BIOMETRIC_LIVENESS_FAILED, IDENTITY_MISMATCH, FACE_MATCH_BELOW_THRESHOLD, COERCION_DETECTED, UNAUTHORIZED_DEVICE, ACCESS_REVOKED
404not_found — unknown and unauthorised answer identically, on purpose
405METHOD_NOT_ALLOWED
409Replay or nonce reuse; SESSION_PRESENT, IDENTITY_UNREACHABLE
422FACE_TEMPLATE_REQUIRED
429RATE_LIMITED
500Carries trace_id; IDENTITY_CREATE_FAILED, TEMPLATE_STORE_FAILED, SESSION_MINT_FAILED
503SLO_BREACH_PAUSED, TEMPLATE_STORE_UNAVAILABLE
Sidaxis is the human authorisation layer. It never moves money, holds funds or settles. The machine-readable contract is openapi.yaml.
Sidaxis API documentationQuickstartAuthenticationHosted or headlessSandbox and productionIdempotencyRate limitsErrors and refusalsField namesWebhooksAssuranceThe document ruleAlias availabilityMerchant themeMCP serverCompatibilityStatusRoadmapChangelogIdentityEnroll an identityRecognise a personDisclose an attributeMandatesThe eight scopesIssue a mandateCheck an actionConsume a mandateRead a mandateRevoke a mandateReceiptsRead a merchant's receiptsVerify a receiptHosted sessionsCreate a hosted sessionConsume a hosted sessionOne Face for agents