ARCNM

Building blocks

Errors

Status codes, the error envelope, the stable error-code catalog, and how to debug.

Every ARCNM error returns the same JSON envelope — a stable error.code, a human-readable message, and a request_id for support. Branch on code, never the message:

{
  "error": {
    "code": "insufficient_scope",
    "message": "API key / token missing required scope",
    "details": {},
    "doc_url": "https://api.arcnm.io/…"
  },
  "request_id": "req_9c5f-…"
}
  • code — stable machine-readable identifier. Switch on this, not on message.
  • message — human-readable, English. Safe to log; translate before showing end-users.
  • details — endpoint-specific context (offending field, valid values, limits). Always an object.
  • doc_url — a link to the relevant docs for this code.
  • request_id — a top-level sibling of error, and also the X-Request-ID response header. Quote it in support tickets.

Status codes

Code Meaning
200 OK Success, body returned
201 Created Resource created
202 Accepted Async work enqueued — poll for the result
204 No Content Success, no body
400 Bad Request Payload doesn't match the schema
401 Unauthorized Missing / invalid / expired credential
402 Payment Required Wallet can't cover the call, or plan entitlement exceeded
403 Forbidden Authenticated but lacks scope / role / verification
404 Not Found Resource doesn't exist in this tenant
405 Method Not Allowed Wrong method for the route
409 Conflict Uniqueness or state conflict (e.g. duplicate part_number)
410 Gone Resource permanently removed
412 Precondition Failed Precondition (e.g. If-Match) not met
413 Payload Too Large Request body exceeds the size cap (details.observed_bytes, details.limit_bytes)
415 Unsupported Media Type Wrong Content-Type (e.g. JSON for a CAD upload)
422 Unprocessable Entity Schema valid but a value was rejected
423 Locked Wallet administratively paused
429 Too Many Requests Rate / quota limit hit — see Rate limits
5xx Server / upstream error — retry with backoff

Billed on success only: non-2xx responses cost €0, including 429s.


Error code catalog

These code values are emitted by the platform's typed errors and are stable. Route-level HTTPExceptions that don't map to a typed error get a code derived from the status (400→bad_request, 401→unauthorized, 403→forbidden, 404→not_found, 405→method_not_allowed, 409→conflict, 410→gone, 415→unsupported_media_type, 422→unprocessable_entity, 429→too_many_requests) — with the specific reason in message (e.g. calculation_not_found, max_attempts_exceeded).

Authentication & authorization

Code Status Meaning
unauthorized 401 Missing or invalid credential
invalid_api_key 401 API key not found or revoked
mfa_required 401 Session needs an MFA step
replay_detected 401 Token / key replay — session revoked
forbidden 403 Authenticated but not allowed
insufficient_scope 403 Key/token lacks the required scope
tenant_isolation_violation 403 Tenant context required / mismatch
email_verification_required 403 Caller's email isn't verified
recent_auth_required 403 Action needs fresh credentials

Billing (402 / 423)

Code Status Meaning
insufficient_funds 402 Wallet can't cover the request
entitlement_exceeded 402 Plan limit exceeded
quota_exceeded 402 Plan quota for a feature exhausted
wallet_locked 423 Wallet is paused

State & validation

Code Status Meaning
bad_request 400 Malformed request
not_found 404 Resource not in tenant
conflict 409 Uniqueness or state conflict
no_organization_context 409 User must create/join an org first
idempotency_in_flight 409 Same Idempotency-Key still processing
idempotency_conflict 409 Same key reused with a different body
subscription_not_provisioned 409 Subscription not yet linked to billing
precondition_failed 412 Precondition not met
payload_too_large 413 Request body exceeds the size cap (details.observed_bytes, details.limit_bytes)
unprocessable_entity 422 Value rejected by a business rule

Rate limits & server

Code Status Meaning
rate_limited 429 Per-caller quota exceeded (details.limit, details.window_seconds)
too_many_requests 429 Auth-route limit exceeded
service_unavailable 503 Deploy window or brownout — retry

Debugging a request

  1. Capture X-Request-ID from the response — we log every request with this id.
  2. Read error.details for the offending field / limit.
  3. Check the status page for incidents.
  4. File a ticket at [email protected] with the request id and timestamp (redact secrets).

Retry guidance

Code Safe to retry?
429 rate_limited / too_many_requests Yes — exponential backoff
5xx Yes — exponential backoff (start 1s, cap 30s, ~5 retries)
402 insufficient_funds Only after topping up the wallet
4xx (other) No — fix the request first

Pair idempotency keys with retries so repeated attempts don't create duplicate billable calculations. For ready-to-paste backoff loops (Python / TypeScript / Bash), see Rate limits → Retry strategy.


See also