Send Email
Send transactional emails to one or more recipients. Supports HTML and plain text content, CC and BCC, attachments, tags, custom headers and idempotency keys. Perfect for welcome emails, password resets, notifications, and more.
to + cc + bcc combined. Going over returns 400 TOO_MANY_RECIPIENTS — use the batch endpoint for larger sends.Select Language
curl -X POST \
https://api.sendcomms.com/api/v1/email/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"cc": ["manager@example.com"],
"subject": "Welcome to our platform!",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"from": "Your App <hello@yourdomain.com>",
"replyTo": "support@yourdomain.com",
"headers": {
"List-Unsubscribe": "<https://yourdomain.com/unsubscribe?u=12345>"
},
"tags": [{ "name": "campaign", "value": "welcome" }],
"idempotency_key": "welcome-user-12345"
}'Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| to | string | string[] | Required | Recipient email address(es). Counts towards the 50-recipient cap shared with cc and bcc |
| subject | string | Required | Email subject line |
| html | string | Conditional | HTML content (required if no text) |
| text | string | Conditional | Plain text (required if no html) |
| from | string | Optional | Sender address. Must be on a domain verified for your account, otherwise the request fails with 400 UNVERIFIED_SENDER_DOMAIN. Display names are supported: "Acme <hello@acme.com>". If omitted, your primary verified domain is used |
| replyTo | string | Optional | Reply-To address. Not restricted to your verified domains |
| cc | string | string[] | Optional | CC recipients. Counts towards the 50-recipient cap |
| bcc | string | string[] | Optional | BCC recipients — delivered on the envelope only, never shown in the message headers. Counts towards the 50-recipient cap |
| attachments | object[] | Optional | Files to attach, each { filename, content } with base64-encoded content. Up to 20MB total per message |
| tags | object[] | Optional | Labels for your own reporting, each { name, value }. Returned on the message record and searchable in your dashboard |
| headers | object | Optional | Custom message headers as string key/value pairs. Commonly used for List-Unsubscribe on bulk sends |
| idempotency_key | string | Optional | Your own unique key for safe retries. Retrying with the same key returns the original result instead of sending again |
Response Codes
Common Errors
| Code | Status | When it happens |
|---|---|---|
| TOO_MANY_RECIPIENTS | 400 | More than 50 recipients across to + cc + bcc |
| UNVERIFIED_SENDER_DOMAIN | 400 | The from address is not on a domain verified for your account |
| MISSING_CONTENT | 400 | Neither html nor text was supplied |
| INVALID_EMAIL | 400 | A to, cc or bcc address is not a valid email address |
| REQUEST_IN_PROGRESS | 409 | An earlier request with the same idempotency_key is still being processed |
Sender Verification
The from address must use a domain you have verified for your account. We never silently substitute a different sender, so an unverified domain is rejected outright:
{
"success": false,
"error": {
"code": "UNVERIFIED_SENDER_DOMAIN",
"message": "The \"from\" domain \"acme.com\" is not verified for your account. Verify it under Domains, or omit \"from\" to use your default sender.",
"details": {
"provided_from": "Acme <hello@acme.com>",
"provided_domain": "acme.com"
}
}
}- Display names are supported on every address field —
Acme <hello@acme.com>. - Omit
fromto send from your primary verified domain. - Add and verify domains from the Domains endpoint.
Idempotency
Pass your own idempotency_key to make retries safe. If a request with that key already succeeded, we return the original result instead of sending a second email, and the replay is marked with an X-Idempotent-Replay: true response header.
- Keys are scoped to your account and cached for 24 hours.
- While the first request is still in flight, a retry with the same key returns
409 REQUEST_IN_PROGRESS— wait and retry. - If the send fails, the key is released so you can retry it cleanly.
- Use a value that is unique to the action, e.g.
welcome-user-12345.
Response
Success Response
When an email is successfully queued for delivery, you'll receive a response containing the transaction ID and email details. Keep transaction_id and email_id — both are echoed back on every webhook event for this message.
{
"success": true,
"data": {
"transaction_id": "email_mjgc0ejr_3ca715bfb7a0",
"email_id": "msg_8f21c0d4a97b",
"status": "sent",
"to": ["user@example.com"],
"from": "Your App <hello@yourdomain.com>",
"subject": "Welcome to our platform!",
"recipients": 1,
"price": { "amount": 0.01, "currency": "USD" },
"remaining": 499,
"quota": { "used": 1, "remaining": 499 },
"created_at": "2026-08-23T10:30:00.000000+00:00"
}
}