Error Handling
Cord uses conventional HTTP codes and a predictable error structure to indicate success or failure.
HTTP Status Codes
The Cord API always returns standard HTTP status codes. As a general rule:
2xxindicates success.4xxindicates an error originating from the provided information (e.g., missing parameters, invalid token).5xxindicates an error on Cord’s servers (these are very rare).
Code Summary
| Code | Description |
|---|---|
| 200 OK | Everything worked as expected. |
| 400 Bad Request | The request was unacceptable. Usually means invalid_request (missing fields) or invalid_json. |
| 401 Unauthorized | Your API Key is missing, incorrect, or was revoked. |
| 402 Payment Required | The operation requires a higher plan (subscription_required) or exceeds a limit on your current plan (plan_limit_reached, e.g. number of clients or products). |
| 403 Forbidden | You have insufficient permissions (e.g., trying to write with a read-only key, or a Publishable Key attempting an action outside its scope). |
| 404 Not Found | The resource (quote, client, invoice) does not exist. |
| 409 Conflict | An invalid state transition for the resource (invalid_state) — for example, approving a quote that is still a draft, voiding an invoice that requires a credit note instead, or trying to delete test data (/api/test-mode/reset) without being in a Sandbox organization. Also returned while another request with the same Idempotency-Key is still running (idempotency_in_progress). |
| 422 Unprocessable Entity | You reused an Idempotency-Key with a different request (idempotency_key_reused). |
| 429 Too Many Requests | You exceeded your key’s requests-per-minute limit (rate_limited) or your plan’s monthly quota (api_quota_exceeded). Respect the Retry-After header. |
| 503 Service Unavailable | A dependency the operation needs didn’t respond — for example, an exchange rate could not be obtained (fx_unavailable) or the tax catalog (tax_catalog_unavailable). Cord never invents a rate: if it can’t prove one, the operation fails closed. Retry later. |
Error Object Structure
When a 4xx or 5xx error occurs, Cord always responds with a flat (not nested) JSON object with two fields: error (a human-readable message you can show or log) and code (a stable string for your handling logic). Some errors add extra fields depending on the case (see examples below).
Error Response Example:
{
"error": "The company name is required",
"code": "invalid_request"
}
Example with extra fields (plan limit, 402):
{
"error": "Your plan allows 50 clients. Free up space or upgrade your plan to continue.",
"code": "plan_limit_reached",
"resource": "clients",
"limit": 50,
"plan": "starter"
}
Don’t rely on the nested error.code / error.message shape to parse the response: error is always the message string, and code is a sibling field at the same level, not a property of error.
Common Error Codes (code)
-
invalid_json: YourPOSTrequest body is not valid JSON, orContent-Type: application/jsonwasn’t sent. -
invalid_request: A business validation failed (missing field, discount out of range, a quote without a valid client). Theerrorfield explains the exact reason. -
missing_key/invalid_key: TheAuthorization: Bearerheader is missing, or the key doesn’t exist / was revoked. -
insufficient_scope: Your key doesn’t have the required scope (a read-only key tried to write, or a Publishable Key tried a route outside its allowed list). -
not_found: The requested resource doesn’t exist in your organization. -
subscription_required: The capability requires a higher plan. The response includesplan(your current one) andplan_required. -
plan_limit_reached: You hit a hard limit on your plan (clients, products, API keys). The response includesresource,limit, andplan. -
integration_limit_reached(403): your integrations already created the 100 webhook subscriptions allowed per organization. Delete the ones you no longer use. -
rate_limited/api_quota_exceeded: Requests-per-minute or monthly API quota limit. Retry while respectingRetry-After. -
fx_unavailable/tax_catalog_unavailable: A third-party data point (exchange rate or tax catalog) could not be obtained with certainty. The operation did not complete — don’t assume it did. -
invalid_state: The resource is not in a status that allows the operation, or it changed while we were processing it. Nothing was applied. -
invalid_url: A webhook URL is nothttps://or points to an internal host. -
invalid_idempotency_key/idempotency_key_reused/idempotency_in_progress/idempotency_unavailable: See the next section.
Safe retries with Idempotency-Key
If a request that creates or changes something (POST, PATCH, DELETE) is cut off by the network or a timeout, you do not know whether Cord applied it. Send the Idempotency-Key header with a unique value per operation (for example, a UUID you generate) and retry with the same key and the same body:
curl -X POST "https://cordhq.app/api/v1/clientes" \
-H "Authorization: Bearer sk_live_tU..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5d9c2b7e-6f1a-4c3d-9e8b-2a1f0c7d6e5b" \
-d '{ "empresa": "ACME Corp" }'
- Retry with the same key and body: Cord returns the original response without running the operation again or counting it against your quota. The repeated response includes the
Idempotent-Replayed: trueheader. - Same key with a different body, method, or route:
422 idempotency_key_reused. Use a new key for each different operation. - The first request has not finished:
409 idempotency_in_progress. Wait and retry. 5xxand429errors: are not stored, so you can retry with the same key.- The key is scoped to your API key, accepts 1 to 255 visible characters (
invalid_idempotency_keyotherwise), and expires after 24 hours. - If Cord cannot reserve the key, it returns
503 idempotency_unavailablewithout running the operation.
The header is optional: without it, every request runs as it arrives.