Errors
Handle validation errors, auth failures, state conflicts, rate limits, retryable failures, request ids, and idempotent writes.
Error handling is part of the API contract. A production integration should branch on machine-readable status and error fields, preserve request ids, retry only when the failure is retryable, and use idempotency keys for state-changing writes.
Error envelope
Examples in the source use a common error shape:
{
"type": "invalid_request",
"code": "invalid_request",
"message": "Example validation or state error using fake data.",
"request_id": "req_test_000000000123",
"doc_url": null,
"statusCode": 400
}Treat code, type, statusCode, and request_id as integration signals. Do not build business logic by parsing the free-form message. Every response also carries an X-Request-Id header that mirrors request_id; capture it from headers even when there is no JSON body to parse.
Status classes
| Status | Meaning | Retry behavior |
|---|---|---|
400 | Request shape, validation, or state input problem. | Do not retry unchanged. |
401 | Missing or invalid authentication. | Do not retry until credentials are fixed. |
402 | Insufficient balance to reserve or settle the write. | Fund the balance, then retry. |
403 | Valid credential but missing permission, entitlement, origin, or trusted status. | Do not retry until access/config changes. |
404 | Object not found or not partner-scoped. | Check ids and ownership; do not expose foreign ids. |
409 | State conflict, duplicate, expired, consumed, or already terminal object. | Retrieve current state and branch. |
429 | Rate limit. | Retry with backoff and respect retry guidance. |
501 | Capability not enabled for public use (for example, an off-ramp transact). | Do not retry; request enablement through account review. |
5xx | Server or upstream failure. | Retry bounded with the same idempotency key for writes. |
Error types
type is a fixed, machine-readable classification of the failure. Branch on it for broad handling, and on code for specific cases.
type | Typical status | Meaning |
|---|---|---|
invalid_request | 400 | Request shape, validation, or state input problem. |
unauthorized | 401 | Missing or invalid authentication. |
forbidden | 403 | Valid credential but missing permission or entitlement. |
not_found | 404 | Object not found or not partner-scoped. |
conflict | 409 | State conflict, duplicate, expired, consumed, or idempotency mismatch. |
rate_limited | 429 | Velocity or throttle limit reached. |
server_error | 5xx | Server or upstream failure. |
Cross-tenant lookups return 404, not 403
A lookup for an object that belongs to another partner returns 404, not 403. The API never confirms the existence of objects outside your own scope. Do not infer ownership or existence from the status, and do not surface foreign ids.
0Pools 403 denial codes
When a 0Pools request is rejected for access reasons, branch on the code field. The message is free-form and may change; the code is stable.
code | Meaning | What to do |
|---|---|---|
pools_not_enabled | 0Pools is not enabled for this partner. | Request enablement through account review. |
pool_access_suspended | The partner's pool access is suspended. | Contact 0Bit; access is operator-provisioned. |
kyc_not_approved | KYC is not in an approved state. | Wait for approval; do not retry on the same state. |
pool_not_allowed | The pool is not in the partner's allowed set. | Use a pool from GET /pools. |
key_mode_mismatch | The key mode does not match the operation (for example, a publishable key on a secret-only route). | Use the correct key for the operation. |
Branch on code, not message
Drive logic from code, never the human-readable message. Messages are for logs and operators; codes are the stable contract. The 0Pools balance and capability responses deliberately omit access and KYC state — those surface only as these 403 denial codes.
Idempotent retry pattern
const idempotencyKey = order.storedIdempotencyKey;
const response = await fetch(`${process.env.OBIT_GATE_BASE_URL}/gate_sessions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.OBIT_GATE_SECRET_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify({
amount: '100.00',
currency: 'EUR',
return_url: 'https://partner.example/complete',
}),
});If the network fails after the server created state, retry with the same key. A new key may create a duplicate logical write.
Failure handling table
| Failure | What to log | What to do |
|---|---|---|
| Validation error | Request id, endpoint, schema field, safe partner reference. | Fix request or user input. |
| Auth error | Request id, key prefix, environment, endpoint. | Rotate or correct credentials; do not log the full key. |
| Permission error | Request id, product, entitlement, origin, environment. | Request access or fix allow-list/config. |
| Conflict | Request id, object id, current known status. | Retrieve current object and branch. |
| Rate limit | Request id, endpoint, key/environment, retry time. | Back off and alert if sustained. |
| Timeout | Idempotency key, endpoint, object reference. | Retry with same key and then read current state. |
| Webhook failure | Event id, delivery id, last response status. | Fix endpoint and replay only after dedupe is in place. |
Operational rules
- Persist request ids in logs.
- Persist idempotency keys for writes.
- Persist event ids for webhooks.
- Use decimal strings for money-like values where the API expects strings.
- Do not depend on undocumented fields.
- Use fake values in docs, tests, and screenshots.
- Keep secrets, PII, provider payloads, and internal runbooks out of public docs and logs.