Nylas’s send endpoints accept an Idempotency-Key header so you can safely retry a send without delivering the same message twice. This is the cornerstone for any send pipeline that retries automatically: workflow engines, background queues that redeliver on worker crash, or client code that retries after a network blip.
Supported endpoints
Section titled “Supported endpoints”Both send endpoints accept the same Idempotency-Key header with the same semantics:
- Grant-based send:
POST /v3/grants/{grant_id}/messages/send(API reference) - Transactional send (Beta):
POST /v3/domains/{domain_name}/messages/send(API reference)
Send a request with an Idempotency-Key
Section titled “Send a request with an Idempotency-Key”Generate a unique key per logical send (a UUID v4 is a safe choice) and pass it in the Idempotency-Key header:
curl --request POST \ --url 'https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages/send' \ --header 'Authorization: Bearer <NYLAS_API_KEY>' \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479' \ --data '{ "subject": "Welcome to Nylas", "to": [{ "name": "Leyah Miller", "email": "[email protected]" }], "body": "<p>Thanks for signing up!</p>" }'import { randomUUID } from "node:crypto";
const idempotencyKey = randomUUID();
const response = await fetch( `https://api.us.nylas.com/v3/grants/${grantId}/messages/send`, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", "Idempotency-Key": idempotencyKey, }, body: JSON.stringify({ subject: "Welcome to Nylas", body: "<p>Thanks for signing up!</p>", }), },);import uuidimport requests
idempotency_key = str(uuid.uuid4())
response = requests.post( f"https://api.us.nylas.com/v3/grants/{grant_id}/messages/send", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "Idempotency-Key": idempotency_key, }, json={ "subject": "Welcome to Nylas", "body": "<p>Thanks for signing up!</p>", },)Key requirements
Section titled “Key requirements”- Max length: 256 characters.
- Uniqueness: a key represents one logical send. Generate a fresh key for each new message.
- Format: any string up to 256 characters.
Detect a cached response
Section titled “Detect a cached response”When Nylas returns a cached response, the body and status code are identical to the original response, and the Idempotent-Response header is set to true. Check this header to tell whether the provider was actually contacted on this request:
HTTP/1.1 200 OKIdempotent-Response: trueContent-Type: application/json
{ "data": { ... }, "request_id": "..." }The first request through a key never has this header set; only retries that hit the cache do.
TTL and scope
Section titled “TTL and scope”- TTL: keys are valid for 1 hour after the first request. Once the TTL elapses, the key can be reused freely.
- Grant-based send is scoped per grant. The same key under two different grants creates two independent cache entries.
- Transactional send is scoped per Nylas application.
Idempotency error responses
Section titled “Idempotency error responses”| Status | Error type | When it happens |
|---|---|---|
400 | api.invalid_idempotency_key | The Idempotency-Key header is longer than 256 characters. |
409 | api.invalid_idempotent_request | A previous request used the same key with a different payload. |
409 | api.concurrent_idempotent_request | A request with the same key is currently in flight. |
Error response bodies follow the standard Nylas error shape (request_id, error.type, error.message). For the full error schema, see Sending errors.
When and how to retry
Section titled “When and how to retry”Use this table to decide whether to reuse the original key or generate a new one:
| Response | Retry with | Notes |
|---|---|---|
| Network failure (no response received) | Same key | The cornerstone use case. The first request may or may not have reached Nylas. Retrying with the same key is safe. |
409 api.concurrent_idempotent_request | Same key | Wait a few seconds and try again. Another caller is processing the same logical send. |
429 rate limit | Different key | Nylas caches the 429 against the key, so a retry with the same key gets the cached 429 back and Nylas doesn’t attempt the send. If the response includes a Retry-After header, wait that many seconds. A replayed 429 counts down from the original wait rather than restarting it. Without the header, back off. Then retry with a new key. A 429 on a raw MIME send doesn’t guarantee the message wasn’t delivered, so check the mailbox’s Sent Items first. |
4xx other than api.concurrent_idempotent_request | Different key | The request was rejected (bad payload, auth failure, validation error). Fix the underlying issue, then retry with a fresh key. |
5xx from Nylas (not a provider error) | Usually same key | Retry behavior here isn’t as well-defined — it depends on the error and when it happened, so read the error message before deciding, the status code alone doesn’t tell you which case you’re in. Example: Nylas returns a 504 Gateway Timeout however the provider completed the send but was delayed. Keep retrying with the same key until the real send response is echoed back. While the original request is still in flight you may see 409 api.concurrent_idempotent_request; once it settles, the cached response (success or error) is returned. If instead the error indicates a connect-phase failure, Nylas never reached the destination mail server at all and nothing was sent, retry with a new key instead. See the worked example below. |
5xx provider error | Different key | The provider rejected the message. Address the cause, then retry with a fresh key. Reusing the original key would just return the same error. |
Example: a connect-phase failure
Section titled “Example: a connect-phase failure”Suppose an IMAP send times out because Nylas can’t reach the recipient’s mail server at all (for example, a firewall or DNS issue on their end). Nothing is accepted by the mail server, so there’s no send to protect with the same key:
- You send with
Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479. Nylas attempts to connect to the mail server and gets no response. - If you retry with the same key while that first attempt is still running, you get
409 api.concurrent_idempotent_request. - Nylas eventually gives up and caches a
5xxagainst that key. Retrying with the same key just replays that same5xx, since nothing was ever sent for it to resolve into. - Generate a new key and retry. This reaches the mail server again, giving you a genuine second attempt instead of a repeated failure.
This is the opposite of the delayed-response case above, where the provider already sent the message and the same key eventually settles to a 200. Here, nothing reached the provider, so there’s no result to wait for.
Limitations
Section titled “Limitations”- Idempotency is enforced at the Nylas layer only. Nylas does not propagate the key to downstream providers (Google, Microsoft, Yahoo, IMAP, EWS).
- TTL is fixed at 1 hour so you should implement retries within this time period.