POST
Stable
Send a transactional email
/v1/send
Use this endpoint for password resets, verification emails, app notifications, billing messages, and other transactional events. A successful response means the message has been accepted and queued, not that SES has already delivered it.
Request
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
Authorization |
string | Yes | Bearer API key for the EmailsDone environment. | Bearer ed_... |
Idempotency-Key |
string | No | Optional retry key. Reusing the same key returns the original accepted message for 24 hours. Max 120 characters |
password-reset-user-123-2026-05-26 |
templateId |
string | Yes | Template identifier, for example password-reset or verify-email. | password-reset |
to |
Yes | Recipient email address. | user@example.com |
|
templateVersion |
string | No | Optional template version such as v1. If omitted, EmailsDone uses the latest published version for the template. | v1 |
from |
No | Optional From address. For custom sending domains, the domain must match the effective project or environment sending domain. If omitted, EmailsDone uses the environment From Email setting, then the project From Email setting, then notifications@domain. | notifications@example.com |
|
fromName |
string | No | Optional display name for the From header. If omitted, EmailsDone uses the environment setting, then the project setting. Max 120 characters |
Acme App |
replyTo |
No | Optional Reply To email address. If omitted, EmailsDone uses the environment setting, then the project setting. | support@example.com |
|
data |
object | Yes | Template data. Required and optional fields are derived from the selected template blocks. | {"actionButton":{"label":"Reset password","url":"https://app.example.com/reset/token"}} |
curl
curl -X POST 'https://api.emailsdone.dev/v1/send' \
-H 'Authorization: Bearer ed_...' \
-H 'Content-Type: application/json' \
-d '{
"templateId": "password-reset",
"templateVersion": "v1",
"to": "user@example.com",
"from": "notifications@example.com",
"fromName": "Acme App",
"replyTo": "support@example.com",
"data": {
"actionButton": {
"label": "Reset password",
"url": "https://app.example.com/reset/token"
}
}
}'
Response
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
ok |
boolean | Yes | True when the request was accepted. | true |
status |
string | Yes | Message acceptance state. | accepted |
messageId |
string | Yes | Message record identifier for later operational lookup. | msg_abc123 |
idempotent |
boolean | Yes | True when the response is for an earlier request with the same Idempotency-Key. | false |
{
"ok": true,
"status": "accepted",
"messageId": "msg_abc123",
"idempotent": false
}
Errors
| Status | Code | Message |
|---|---|---|
| 400 | invalid_request |
Expected templateId, to, and data. |
| 400 | invalid_from_address |
From address failed validation. |
| 400 | invalid_from_name |
From name failed validation. |
| 400 | invalid_reply_to |
Reply To failed validation. |
| 400 | invalid_template_data |
Template data failed validation. |
| 401 | missing_api_key |
Authorization bearer token is missing. |
| 401 | invalid_api_key |
API key is invalid. |
| 402 | trial_expired |
The trial can no longer send email. |
| 403 | api_key_revoked |
API key has been revoked. |
| 403 | monthly_quota_exceeded |
Tenant has exhausted the monthly quota. |
| 403 | app_paused |
Sending is paused for the app. |
| 403 | app_blocked |
Sending is blocked for the app. |
| 403 | recipient_complained |
Recipient has complained. |
| 403 | recipient_unsubscribed |
Recipient is unsubscribed from notifications. |
| 403 | sending_domain_not_ready |
The configured sending domain is not verified yet. |
| 403 | from_domain_not_allowed |
From address does not match the configured sending domain. |
| 404 | project_not_found |
Project not found for API key. |
| 404 | environment_not_found |
Environment not found for API key. |
| 404 | template_not_found |
Template was not found. |
| 404 | template_version_not_found |
Requested template version was not found. |
| 405 | method_not_allowed |
Only POST is allowed. |
| 500 | internal |
Unexpected server error. |
Notes
- Use Idempotency-Key for password resets, billing emails, and other retry-prone workflows.
- Do not send HTML. Choose a templateId and pass the required template data.
- Treat accepted as queued. Delivery and bounce state are recorded asynchronously.