Send transactional emails, batch messages, and check delivery status. All email endpoints support idempotency via the Idempotency-Key header.
Send Email
/email/sendSend a transactional email to one or more recipients
| Name | Type | Required | Description |
|---|---|---|---|
| to | string | string[] | Required | Recipient email address, or an array of up to 100 email addresses |
| subject | string | Required | Email subject line (max 998 characters) |
| html | string | Optional | HTML body content. Required if text and template_id are not provided. |
| text | string | Optional | Plain text body content. Required if html and template_id are not provided. |
| template_id | string (UUID) | Optional | ID of a saved template to use. Required if html and text are not provided. |
| template_data | Record<string, string> | Optional | Key-value pairs for template variable substitution using {{variable}} syntax |
| from_email | string | Optional | Sender email address. Falls back to workspace default. Used as the Reply-To address. Emails are sent from your workspace's shared sending domain unless you have a verified custom domain. |
| from_name | string | Optional | Sender display name (max 100 characters). Defaults to workspace setting or "TopMail". |
| reply_to | string | Optional | Reply-to email address |
| track_opens | boolean | Optional | Enable open tracking. Defaults to true. |
| track_clicks | boolean | Optional | Enable click tracking. Defaults to true. |
| send_at | string (ISO 8601) | Optional | Schedule delivery for a future time. Must be within 72 hours. |
curl -X POST https://api.topmail.so/api/v1/email/send \-H "Authorization: Bearer tm_live_abc123" \-H "Content-Type: application/json" \-d '{"to": "user@example.com","subject": "Welcome to TopMail","html": "<h1>Hello {{first_name}}</h1><p>Thanks for signing up!</p>","from_email": "hello@yourdomain.com","from_name": "Your App","reply_to": "support@yourdomain.com","track_opens": true,"track_clicks": true}'
{"data": {"sent": 1,"failed": 0,"skipped": 0,"total": 1,"results": [{"to": "user@example.com","message_id": "ses-message-id-12345","success": true}]}}
Multiple Recipients
Pass an array of up to 100 email addresses in the to field. Each recipient is processed independently -- suppressed or invalid addresses are skipped without failing the entire request. The response includes per-recipient results.
Shared Sending Domain
No domain setup is needed to start sending. Every workspace includes a shared sending domain. Emails are sent from your shared domain (e.g., nick@abc123.send.topmail-manage.com), and from_email is set as Reply-To so replies go back to you. To send directly from your own domain, verify a custom domain in Settings > Domains.
Scheduled Sends
Include a send_at parameter with an ISO 8601 datetime to schedule an email for future delivery. The schedule must be within 72 hours from the current time.
curl -X POST https://api.topmail.so/api/v1/email/send \-H "Authorization: Bearer tm_live_abc123" \-H "Content-Type: application/json" \-d '{"to": "user@example.com","subject": "Your weekly digest","html": "<h1>Weekly Digest</h1><p>Here are your updates...</p>","send_at": "2025-01-15T09:00:00Z"}'
Sandbox Mode
When using a sandbox API key (prefixed with tm_test_), emails are not actually sent. The API returns mock success responses with simulated message IDs, allowing you to test your integration without sending real emails or consuming your email quota.
Email Compliance
TopMail automatically ensures every email includes the compliance elements required by CAN-SPAM and other email regulations. Before sending, each HTML email is checked for two things: a working unsubscribe link (using TopMail's merge tags) and a physical mailing address.
Has {{unsubscribe_url}} + physical address
TopMail uses your footer as-is and only adds subtle TopMail branding.
Has {{unsubscribe_url}} but no address
TopMail keeps your unsubscribe link and appends your company name, address (from Settings > Brand), and branding.
Missing unsubscribe merge tag
TopMail appends a full compliant footer with unsubscribe link, preference center, company address, and branding. External unsubscribe links are not recognized — you must use TopMail's merge tags.
Set up your brand details
Go to Settings > Brand and fill in your company name and physical address. The auto-generated footer uses these values, so keeping them accurate ensures your emails look professional and stay compliant.
Building your own footer?
Use {{unsubscribe_url}} and {{preferences_url}} in your HTML. These are replaced with working TopMail-managed links at send time. Include a physical address alongside them to fully control your footer.
Batch Send
/email/batchSend up to 1,000 emails in a single request. Each message can have unique content, recipients, and settings.
Each item in the messages array accepts the same fields as the single send endpoint (except send_at), but with a single to address per message.
| Name | Type | Required | Description |
|---|---|---|---|
| messages | array | Required | Array of 1-1,000 message objects |
| messages[].to | string | Required | Recipient email address |
| messages[].subject | string | Required | Email subject line |
| messages[].html | string | Optional | HTML body content |
| messages[].text | string | Optional | Plain text body content |
| messages[].template_id | string (UUID) | Optional | Template ID to use |
| messages[].template_data | Record<string, string> | Optional | Template variable substitution |
| messages[].from_email | string | Optional | Sender email (defaults to workspace setting) |
| messages[].from_name | string | Optional | Sender display name |
| messages[].reply_to | string | Optional | Reply-to address |
| messages[].track_opens | boolean | Optional | Enable open tracking (default: true) |
| messages[].track_clicks | boolean | Optional | Enable click tracking (default: true) |
curl -X POST https://api.topmail.so/api/v1/email/batch \-H "Authorization: Bearer tm_live_abc123" \-H "Content-Type: application/json" \-d '{"messages": [{"to": "alice@example.com","subject": "Your order shipped!","html": "<p>Hi Alice, your order #1234 is on the way.</p>"},{"to": "bob@example.com","subject": "Your order shipped!","html": "<p>Hi Bob, your order #5678 is on the way.</p>"}]}'
{"data": {"batch_id": "batch-uuid-12345","status": "completed","total": 2,"sent": 2,"failed": 0}}
Batch Status
/email/batch/:batchIdCheck the status of a batch send
| Name | Type | Required | Description |
|---|---|---|---|
| batchId | string | Required | The batch ID returned from the batch send endpoint (URL parameter) |
curl https://api.topmail.so/api/v1/email/batch/batch-uuid-12345 \-H "Authorization: Bearer tm_live_abc123"
{"data": {"batch_id": "batch-uuid-12345","status": "completed","total": 100,"sent": 98,"failed": 2,"created_at": "2025-01-15T08:30:00.000Z","completed_at": "2025-01-15T08:30:45.000Z"}}
Message Status
/email/:messageId/statusLook up the delivery status of a sent message
| Name | Type | Required | Description |
|---|---|---|---|
| messageId | string | Required | The SES message ID returned from the send endpoint (URL parameter) |
curl https://api.topmail.so/api/v1/email/ses-message-id-12345/status \-H "Authorization: Bearer tm_live_abc123"
{"data": {"message_id": "ses-message-id-12345","status": "delivered","to": "user@example.com","sent_at": "2025-01-15T08:30:00.000Z","delivered_at": "2025-01-15T08:30:02.000Z","opened_at": "2025-01-15T09:15:30.000Z","clicked_at": "2025-01-15T09:16:00.000Z","bounced_at": null,"bounce_type": null}}
Idempotency
The send and batch endpoints support idempotent requests to prevent duplicate sends caused by network retries. Include an Idempotency-Key header with a unique value (e.g., a UUID). If a request with the same key has already been processed, the original response will be returned without resending the email.
curl -X POST https://api.topmail.so/api/v1/email/send \-H "Authorization: Bearer tm_live_abc123" \-H "Content-Type: application/json" \-H "Idempotency-Key: unique-request-id-12345" \-d '{"to": "user@example.com","subject": "Order confirmation","html": "<p>Your order has been confirmed.</p>"}'
Bulk Sending Best Practices
Follow these guidelines when sending high volumes of email via the API:
Use the batch endpoint
Use POST /email/batch (up to 1,000 messages per request) instead of looping individual calls to POST /email/send. This reduces your API request count and is significantly more efficient.
Use idempotency keys for safe retries
Always include an Idempotency-Key header on send and batch requests. If a network error occurs, you can safely retry the same request without risking duplicate sends.
Handle rate limits gracefully
If you receive a 429 response, wait for the number of seconds in the Retry-After header before retrying. Use exponential backoff for consecutive 429s. Check the X-RateLimit-Remaining header to proactively throttle before hitting the limit.
Be aware of workspace sending limits
In addition to the per-key API rate limit (requests per minute), your workspace has hourly and daily email sending limits. These are separate from API rate limits and apply to the total number of emails delivered. Check your workspace settings for current limits.
Need higher limits?
If your use case requires higher API rate limits or email sending quotas, contact TopMail support to discuss your needs.