Every Delivrie API error, what causes it and how to fix it, with the JSON error format and which errors are safe to retry.
Errors use standard HTTP status codes and a JSON body:
{
"statusCode": 422,
"name": "validation_error",
"message": "The from domain mail.acme.com is not verified yet (status: pending)."
}
Branch on name in code and show message to people: it's written to say exactly what went wrong. Every request, including failed ones, appears in Logs with its request and response bodies.
| Status | name | Cause | Fix |
|---|---|---|---|
| 401 | missing_api_key | No Authorization header | Add Authorization: Bearer dlv_... |
| 401 | invalid_api_key | The key doesn't exist or was deleted | Check the key; create a new one in API keys |
| 403 | restricted_api_key | A sending-only key called another endpoint, or sent from a domain it isn't allowed to use | Use a full access key, or send from the key's domain |
| 403 | plan_limit_reached | You've reached your plan's domain or webhook limit | Remove one, or upgrade in Billing |
| 403 | sending_paused | Sending is paused because your bounce or complaint rate went over the limit (see Deliverability) | Fix the cause, then email support@delivrie.io to resume |
| 403 | insufficient_scope | An MCP or OAuth connection wasn't granted this scope | Reconnect and approve the scope |
| 404 | not_found | Nothing with that id in this team | Check the id, and that the key belongs to the right team |
| 409 | domain_exists | The domain is already in this team | Use the existing domain |
| 409 | domain_in_use | Another team owns the domain | Claim it from the Domains page if it's yours |
| 422 | validation_error | A field is missing or invalid, or the from domain isn't verified | Read message; it names the field |
| 429 | daily_quota_exceeded | Free plan: 133 emails today | Wait until midnight UTC, buy credits or upgrade |
| 429 | monthly_quota_exceeded | Free plan: 4,000 emails this month | Buy credits or upgrade |
| 429 | spending_cap_reached | This send would take overage past your spending cap | Raise or remove the cap in Billing |
| 500 | application_error | Something failed on our side | Retry with the same idempotency key |
| 503 | sending_not_configured | Sending is temporarily unavailable | Retry later; check status |
validation_error messages| Message | What to do |
|---|---|
| Request body must be valid JSON | Send a JSON body with Content-Type: application/json |
| Missing from / to / subject field | Add the field |
| Provide html or text | Add a body, or send a template |
| The from domain X is not verified | Add and verify the domain in Domains, or fix a typo in from |
| X can only send to members of your team | You're using onboarding@delivrie.io. Verify your own domain to email anyone |
| to can have at most 50 addresses | Split the send, or use batch sending |
| Attachments are limited to 40 MB per email | Send a link to the file instead |
| Attachment path isn't supported yet | Send base64 content |
| scheduled_at must be an ISO 8601 date or a phrase... | Use 2026-12-01T09:00:00Z or a phrase like in 1 hour |
| Template X has no published version | Publish the template |
| Tag names and values can only contain... | Use letters, numbers, _ and - only |
Retry 5xx errors and network failures with the same Idempotency-Key. Don't retry 4xx errors, including quota 429s; they won't succeed until something changes. See Idempotency and retries.
The Node.js SDK returns errors instead of throwing: const { data, error } = await delivrie.emails.send(...). error has the same statusCode, name and message. Problems that never reach the API have statusCode: 0 and the name network_error, timeout or aborted.