Retry-After and the Idempotency-Key header.
Error response format
Every error is a JSON object with the same shape.error_code identifies the category of failure, message is a human-readable description, and errors (present on validation failures only) maps each invalid field to its problems:
Common error codes
Validation errors
A422 means the request was well-formed but carried invalid data: a missing required field, a malformed recipient address, or a subject over the length limit. The errors object names the field, so a handler can report the specific problem:
An unverified or misconfigured sending domain is not a validation error. It returns
400 with
invalid_domain or unconfigured_domain, so a handler scoped to 422 misses the most common
first-send failure. SDKs that map only 422 to a validation exception (lettr-php raises a
generic ApiException here) will not route it to a validation catch block either. See
Domain errors below.Domain errors
Thefrom address must use a verified domain with its DNS records in place. These are the most common errors during initial setup, and they are not retryable until the domain is fixed:
Retryable and non-retryable errors
Retrying a validation error or an unauthorized request produces the same result every time, so the retry path should only see server failures, rate limits, and an idempotent request that is still in flight. Two409 codes exist only for requests that carry an Idempotency-Key; the Idempotency guide covers the header itself.
Retry logic
A retry helper needs three things: a stableIdempotency-Key so a retried send cannot deliver twice, a check that rejects non-retryable errors before backing off, and a delay that honours Retry-After when the response carries one. Without that header the pattern below sleeps a random interval under a capped exponential ceiling, so clients recovering from the same outage do not retry in lockstep, and it never sleeps after the final attempt:
Idempotency-Replayed: true; if it did not, the retry sends the email.