Errors and request ids
Every error has the same flat body:
{ "statusCode": 422, "name": "validation_error", "message": "subject: Required", "code": "validation_failed", "request_id": "req_..."}statusCode,nameandmessageare Resend’s fields, so code that branches onerror.namekeeps working.codeis Duta’s, and finer grained. Branch on it in new code.messageis written for a developer. Log it, but do not show it to your users.detail, when present, carries more, such as every failure in a batch.request_ididentifies the request.
Find the request
Section titled “Find the request”Every response carries an X-Request-Id header, and every error body the same
value as request_id. Paste it into the search on the Logs screen to see the
request, the response and the key that made it. Quote it when you ask for
help.
Error codes
Section titled “Error codes”| code | HTTP | name | Meaning |
|---|---|---|---|
unauthorized | 401 | invalid_api_key | The API key is missing, wrong or revoked. |
forbidden | 403 | invalid_access | The key cannot do this. A sending key tried to manage the account, or the action is not allowed. |
not_found | 404 | not_found | Nothing with that id exists in this account. |
validation_failed | 422 | validation_error | The request body or a parameter is invalid. message names the field. |
domain_not_verified | 403 | validation_error | The from address is on a domain that has not verified yet. |
from_domain_not_allowed | 403 | invalid_from_address | The from address is on a domain this account cannot send from. |
recipient_suppressed | 422 | validation_error | The recipient is on the suppression list. |
recipient_not_confirmed | 422 | validation_error | A sandbox send to an address that has not confirmed it wants this account's test mail. |
rate_limited | 429 | rate_limit_exceeded | Too many requests. Wait for the Retry-After header and try again. |
quota_exceeded | 429 | monthly_quota_exceeded | A plan limit was reached, such as the daily limit, the overage cap or the number of domains. |
conflict | 409 | validation_error | The request clashes with the current state, such as a plan change already in progress. |
payment_required | 402 | application_error | The account needs a paid plan for this. |
account_paused | 403 | invalid_access | Sending is paused on this account. The dashboard says why. |
content_rejected | 422 | security_error | The message was refused by content screening. |
platform_halted | 503 | application_error | Sending is halted across Duta. Retry later. |
payload_too_large | 413 | validation_error | The request body is over 14 MB. |
length_required | 411 | validation_error | The request body has no Content-Length header. |
idempotency_conflict | 409 | invalid_idempotent_request | The Idempotency-Key was used with a different body, or its first request is still running. |
provider_error | 502 | application_error | The upstream mail provider failed or timed out. Safe to retry. |
internal_error | 500 | internal_server_error | Something failed inside Duta. Quote the request_id to support. |
Which errors to retry
Section titled “Which errors to retry”Retry rate_limited, platform_halted, provider_error and internal_error
after a wait, with backoff. Also retry concurrent_idempotent_requests (code
idempotency_conflict) after waiting for the in-flight request to finish.
Send an Idempotency-Key so a retry cannot send twice.
The rest mean the request needs to change first, and repeating it will fail the
same way.