Send an email
Queues one message. A 202 means the message was durably queued, not delivered: follow its outcome with webhooks or by fetching it. Send either subject with html and/or text, or a template_id with variables, never both. Up to 50 recipients in total across to, cc and bcc. Live messages must come from a verified domain in the same project. Recipients on your suppression list are skipped.
Add up to 10 attachments (base64 content or an https path we download, with a content_id to show an image inline; up to 10 MB in total; executables and scripts are refused). Attachments are not available in a batch. Add up to 20 of your own headers (for example List-Unsubscribe); headers SendSend sets itself cannot be overridden.
Send an Idempotency-Key header so a retry can never deliver twice.
Required scope: emails:send
Parameters
| Name | In | Description |
|---|---|---|
Idempotency-Keyoptional | header | Up to 255 printable characters. The same key with the same body returns the original message; the same key with a different body returns 409. Kept for 24 hours. |
Request body
| Field | Type |
|---|---|
fromrequired | string |
tooptional | string[] |
ccoptional | string[] |
bccoptional | string[] |
reply_tooptional | string[] |
subjectoptional | string |
htmloptional | string |
textoptional | string |
template_idoptional | string |
template_versionoptional | integer |
variablesoptional | object |
languageoptional | string |
tagsoptional | object |
scheduled_atoptional | string |
attachmentsoptional | object[] |
headersoptional | object |
Unknown fields are rejected, so a typo never silently does nothing.
Example
curl -X POST 'https://api.sendsend.email/v1/emails' \
-H "Authorization: Bearer $SENDSEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"from": "Acme <hello@mail.acme.com>",
"to": [
"maria@example.com"
],
"subject": "Welcome to Acme",
"html": "<p>Thanks for signing up!</p>",
"text": "Thanks for signing up!",
"tags": {
"application_reference": "signup_123"
}
}'Response 202
{
"id": "msg_8Xk2pQwLr0aVn3Zt",
"object": "email",
"environment": "live",
"status": "queued",
"status_reason": null,
"simulated": false,
"from": "Acme <hello@mail.acme.com>",
"to": [
"maria@example.com"
],
"cc": [],
"bcc": [],
"reply_to": null,
"subject": "Welcome to Acme",
"template_id": null,
"template_version": null,
"tags": {
"application_reference": "signup_123"
},
"provider_message_id": "0100019…",
"request_id": "req_Kd83…",
"attempts": 0,
"recipients": [
{
"address": "maria@example.com",
"kind": "to",
"status": "queued"
}
],
"created_at": "2026-10-06T15:04:05.000Z",
"updated_at": "2026-10-06T15:04:07.000Z"
}Errors
| Status | Code | When |
|---|---|---|
| 400 | validation_error | The request body or parameters are invalid. The message says which field. |
| 401 | unauthorized | The API key is missing, invalid or revoked. |
| 403 | forbidden | The key does not have the scope this endpoint needs. |
| 422 | domain_not_verified | The From domain is not verified in this project. |
| 400 | invalid_variables | A template variable is missing or has the wrong type. |
| 422 | recipient_suppressed | Every recipient is on your suppression list. |
| 409 | idempotency_conflict | The Idempotency-Key was used with a different request body. |
| 429 | quota_exhausted | The monthly allowance is used up and overage is off or capped. |
| 429 | daily_limit_exceeded | This project reached its daily sending limit. |
| 403 | account_not_approved | Live sending waits for account approval. Test keys work meanwhile. |
| 403 | account_suspended | The account is paused. Contact support. |
| 429 | rate_limited | Too many requests. Wait for the number of seconds in the Retry-After header. |
Every error has the same shape. See Errors.