Errors
Drin uses conventional HTTP status codes and returns a consistent JSON envelope on any non-2xx response. Switch on error.type — it's stable and machine-readable; never parse the message.
01 The envelope
Every non-2xx response carries the same shape:
{
"error": {
"type": "validation_error",
"message": "`to` must contain at least one recipient.",
"param": "to"
}
}| Field | Description |
|---|---|
type | Machine-readable category — switch on this. One of the values in the table below. |
message | Human-readable explanation. Safe to log; don't parse it — wording may change. |
code | Optional finer-grained code, when present. |
param | Optional offending request field, when the error is about input. |
02 Error types
Each type maps to a status range. The status tells you how to react; the type tells you why.
type | Status | Meaning |
|---|---|---|
validation_error | 400 / 422 | The request was malformed or failed validation — param points at the field. |
authentication_error | 401 | Missing, malformed, or revoked API key. |
permission_error | 403 | The key is valid but not allowed to do this — wrong project, sandbox restriction, or plan limit. |
not_found | 404 | No such resource for this account. |
conflict | 409 | Conflicts with current state — an idempotency replay with a different body, or deleting a domain with sending history. |
suppressed | 409 | Every recipient is on this project's suppression list, so nothing was sent. |
rate_limited | 429 | Too many requests — back off and retry. See Rate limits. |
internal_error | 5xx | Something went wrong on our side. Safe to retry idempotently. |
03 Request IDs
Every response echoes a request id in the X-Request-Id header. Include it when you contact support — it lets us find the exact request in our logs.
Retrying safelyThe SDK auto-retries
429 and 5xx with exponential backoff and full jitter. A POST is only retried when you pass an Idempotency-Key, so a send is never duplicated. If you build your own client, do the same — never blindly retry a bare POST.Working in TypeScript? Each type is also a typed error subclass (DrinValidationError, DrinRateLimitError, DrinSuppressedError, …), so you can branch with instanceof. See the Errors guide and the TypeScript SDK.