Errors and job states
Updated
Every non-2xx answer is one envelope. The code says what happened. retryable says whether the same call can be tried again. correlation_id is what to quote when you write to us.
{"error": {"code": "insufficient_balance", "message": "the wallet holds $0.80 and the quote is $1.17", "retryable": false, "correlation_id": "req_..."}} Codes
| Code | Status | What to do |
|---|---|---|
invalid_token | 401 | the key is missing, revoked or malformed. Get a key from the dashboard |
scope_missing | 403 | the API key lacks the scope. The message names it |
agent_forbidden | 403 | an agent key is not allowed on this route. A person does this |
not_found | 404 | no such resource in your organization. A foreign id looks the same |
invalid_request | 400 or 422 | the body failed validation. details lists the fields |
conflict | 409 | a state that forbids the call, or a different body under the same Idempotency-Key. |
encrypted_pdf, unsupported_format, page_limit_exceeded | 422 | fix the document and create it again. Nothing was stored or charged |
conversion_failed, image_too_large | 422 | the HTML took longer than 45 seconds to render, or an image frame is over 50 million pixels. Make it smaller and create it again |
render_unavailable | 503 | the HTML renderer is down. Retryable |
live_provider_unavailable | 503 | live sending is not enabled on this server yet, so live quotes, sends and number orders are refused. Test keys keep working. Not retryable |
unsupported_destination | 422 | no published route, or excluded by policy. Not retryable |
quote_expired | 409 | quote again. Quotes last ten minutes |
insufficient_balance | 402 | top up the wallet. The provider was not called |
budget_exceeded, agent_budget_exceeded | 403 | the cap for the period is reached. A person raises it or waits |
approval_required | 403 | a live send with no approval and no allowing policy. Ask a person |
approval_* | 409 | see the approvals page. Quote and ask again |
sandbox_only, environment_not_billable | 403 | the route is for the other kind of key. |
payload_too_large | 413 | 25 MiB upload cap |
rate_limited | 429 | 600 requests a minute per key. Retryable after RateLimit-Reset |
billing_unavailable, temporarily_unavailable, provider_timeout | 502 or 503 | retryable with the same Idempotency-Key. |
internal_error | 500 | retryable. Quote the correlation id if it persists |
Why is a failed fax not an HTTP error?
A send answers 201 with a job in queued. What happens after is a state on the job, carried by events and webhooks, not an HTTP error.
| State | Do |
|---|---|
| queued, active | wait for the webhook or poll GET /v1/faxes/{id}. A fax takes a minute or more on the line. Busy and no answer are retried up to three times by the service |
| delivered | read the receipt and store the provider reference |
| partial | read pages_sent. Decide whether to send the missing pages as a new fax. The service never retransmits on its own |
| failed | read failure_code: busy, no_answer, invalid_number, render_failed, provider_error, canceled. Nothing was charged |
| unknown | wait. The hold stays and the reconcile tick keeps asking the provider. Do not send again from your side: a duplicate fax is the one outcome worse than a late one. If it stays unknown, write to us with the job id |
How do I avoid sending a fax twice?
Send every mutating call with an Idempotency-Key, derived from your own record such as the order id. A timeout on your side then becomes a retry that returns the same job. Never create a new quote and send after a timeout without first reading the job list for your key.