Sending Emails
Send transactional and marketing emails through the SentinMail API
Overview
SentinMail provides three ways to send emails:
| Method | Endpoint | Use Case |
|---|---|---|
| Send to List | POST /api/emails/send/to-list/ | Send a saved template to an entire subscriber list |
| Send to Receivers | POST /api/emails/send/to-receivers/ | Send a saved template to specific recipients with per-recipient variables |
| Send Custom | POST /api/emails/send/custom/ | Send a custom subject + HTML body (no template needed) |
All sends are processed asynchronously via Celery workers — the API returns immediately with a campaign object and emails are delivered in the background.
Prerequisites
Before sending, you need:
- An API key — see Authentication
- An SMTP configuration — your sending server credentials
- A template (for methods 1 & 2) — the email content with
[[variables]] - A subscriber list (for method 1 only)
When using API key auth, the company field is optional — it's automatically resolved from your key. You only need to pass company when using bearer token auth.
Method 1: Send to Subscriber List
Send a saved template to all subscribers in a list. Supports scheduling.
1curl -X POST https://api.sentinmail.app/api/emails/send/to-list/ \2 -H "X-API-Key: fm_your_api_key_here" \3 -H "Content-Type: application/json" \4 -d '{5 "name": "March Newsletter",6 "template_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",7 "smtp_config_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",8 "list_id": "c3d4e5f6-a7b8-9012-cdef-123456789012"9 }'Request body:
| Field | Type | Required | Description |
|---|---|---|---|
company | UUID | No* | Company ID (*auto-resolved with API key auth) |
name | string | Yes | Campaign name (max 255 chars) |
template_id | UUID | Yes | Email template to send |
smtp_config_id | UUID | Yes | SMTP configuration to use |
list_id | UUID | Yes | Subscriber list to send to |
scheduled_for | ISO-8601 datetime | No | Schedule for later (null = send immediately) |
attachment_ids | UUID[] | No | Media file UUIDs to attach |
Schedule for later
1curl -X POST https://api.sentinmail.app/api/emails/send/to-list/ \2 -H "X-API-Key: fm_your_api_key_here" \3 -H "Content-Type: application/json" \4 -d '{5 "name": "Black Friday Campaign",6 "template_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",7 "smtp_config_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",8 "list_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",9 "scheduled_for": "2026-11-29T08:00:00Z"10 }'Response (201 Created):
1{2 "id": "e5f6a7b8-c9d0-1234-efab-345678901234",3 "company": "d4e5f6a7-b8c9-0123-defa-234567890123",4 "name": "March Newsletter",5 "subject": "Your March Update",6 "campaign_type": "to_list",7 "status": "pending",8 "template": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",9 "lists": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],10 "scheduled_for": null,11 "total_emails": 0,12 "sent_count": 0,13 "failed_count": 0,14 "celery_task_id": "task-uuid",15 "created_at": "2026-03-18T10:00:00Z",16 "updated_at": "2026-03-18T10:00:00Z"17}Method 2: Send to Specific Receivers
Send a saved template to a custom list of recipients with per-recipient personalization. No subscriber list required — receivers are specified inline.
1curl -X POST https://api.sentinmail.app/api/emails/send/to-receivers/ \2 -H "X-API-Key: fm_your_api_key_here" \3 -H "Content-Type: application/json" \4 -d '{5 "name": "Renewal Reminder",6 "template_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",7 "smtp_config_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",8 "receivers": [9 {10 "email": "john@example.com",11 "first_name": "John",12 "plan": "Pro",13 "renewal_date": "2026-04-01"14 },15 {16 "email": "jane@example.com",17 "first_name": "Jane",18 "plan": "Enterprise",19 "renewal_date": "2026-04-15"20 }21 ]22 }'Request body:
| Field | Type | Required | Description |
|---|---|---|---|
company | UUID | No* | Company ID (*auto-resolved with API key auth) |
name | string | Yes | Campaign name (max 255 chars) |
template_id | UUID | Yes | Email template to send |
smtp_config_id | UUID | Yes | SMTP configuration to use |
receivers | object[] | Yes | Array of recipient objects (each must have email) |
scheduled_for | ISO-8601 datetime | No | Schedule for later |
attachment_ids | UUID[] | No | Media file UUIDs to attach |
Receiver object
Each receiver must have an email field. All other fields are optional and become available as template variables. You can also add cc and bcc arrays to copy additional recipients:
1{2 "email": "john@example.com",3 "first_name": "John",4 "last_name": "Doe",5 "plan": "Enterprise",6 "renewal_date": "2026-04-01",7 "cc": ["manager@example.com"],8 "bcc": ["accounting@internal.com"]9}| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Primary recipient |
cc | string[] | No | Carbon copy recipients (visible to all) |
bcc | string[] | No | Blind carbon copy recipients (hidden from others) |
| any other field | string | No | Available as [[field_name]] template variable |
Response (201 Created):
1{2 "id": "f6a7b8c9-d0e1-2345-fabc-456789012345",3 "company": "d4e5f6a7-b8c9-0123-defa-234567890123",4 "name": "Renewal Reminder",5 "campaign_type": "to_receivers",6 "status": "pending",7 "total_emails": 2,8 "sent_count": 0,9 "failed_count": 0,10 "created_at": "2026-03-18T10:00:00Z"11}Method 3: Send Custom Email
Send a fully custom email with inline subject and HTML body — no saved template needed. Best for transactional emails or one-off messages.
1curl -X POST https://api.sentinmail.app/api/emails/send/custom/ \2 -H "X-API-Key: fm_your_api_key_here" \3 -H "Content-Type: application/json" \4 -d '{5 "name": "Order Confirmation",6 "smtp_config_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",7 "subject": "Order #[[order_id]] confirmed, [[first_name]]!",8 "body": "<h1>Thanks, [[first_name]]!</h1><p>Your order <strong>#[[order_id]]</strong> for [[product]] has been confirmed.</p><p>Total: $[[total]]</p>",9 "receivers": [10 {11 "email": "john@example.com",12 "first_name": "John",13 "order_id": "12345",14 "product": "Wireless Headphones",15 "total": "79.99"16 }17 ]18 }'Request body:
| Field | Type | Required | Description |
|---|---|---|---|
company | UUID | No* | Company ID (*auto-resolved with API key auth) |
name | string | Yes | Campaign name (max 255 chars) |
smtp_config_id | UUID | Yes | SMTP configuration to use |
subject | string | Yes | Email subject line (supports [[variables]]) |
body | string | Yes | Email HTML body (supports [[variables]]) |
receivers | object[] | Yes | Array of recipient objects (each must have email). Supports cc and bcc arrays. |
attachment_ids | UUID[] | No | Media file UUIDs to attach |
Custom sends do not support scheduling. Use "Send to List" or "Send to Receivers" if you need scheduled delivery.
Both "Send to Receivers" and "Send Custom" support cc and bcc on each receiver object. See the receiver object docs above for details.
Template Variables
All three methods support personalization using [[variable]] syntax. Variables are replaced per-recipient.
Built-in variables
| Variable | Description |
|---|---|
[[email]] | Recipient's email address |
[[first_name]] | Recipient's first name |
[[last_name]] | Recipient's last name |
[[unsubscribe_url]] | Auto-generated one-click unsubscribe link |
[[view_in_browser_url]] | Link to view the email in a browser |
Custom variables
Any field you include in a receiver object is available as a variable:
1{2 "email": "john@example.com",3 "first_name": "John",4 "company_name": "Acme Corp",5 "coupon_code": "SAVE20"6}Use in template: Hi [[first_name]] from [[company_name]]! Use code [[coupon_code]] for 20% off.
Conditionals
1[[#if coupon_code]]2 Use code <strong>[[coupon_code]]</strong> for a discount!3[[#else]]4 Check out our latest deals.5[[/if]]Loops
1[[#each items]]2 <li>[[this]]</li>3[[/each]]Campaign Management
Check campaign status
1curl -X GET https://api.sentinmail.app/api/emails/campaigns/CAMPAIGN_ID/ \2 -H "X-API-Key: fm_your_api_key_here"Resume a paused/failed campaign
If a campaign partially completes (e.g., SMTP rate limit hit), resume it:
1curl -X POST https://api.sentinmail.app/api/emails/campaigns/CAMPAIGN_ID/resume/ \2 -H "X-API-Key: fm_your_api_key_here"Only campaigns with status partial or failed (with some sent emails) can be resumed. Already-sent recipients are skipped.
Cancel a scheduled campaign
1curl -X POST https://api.sentinmail.app/api/emails/campaigns/CAMPAIGN_ID/cancel/ \2 -H "X-API-Key: fm_your_api_key_here"Only campaigns with status scheduled can be cancelled.
Email Headers
SentinMail automatically adds these headers to every outgoing email:
1From: <sender_name> <sender_email>2Reply-To: <reply_to_email>3Message-ID: <auto-generated>4X-Mailer: SentinMail5Precedence: bulk6List-Unsubscribe: <unsubscribe_url>7List-Unsubscribe-Post: List-Unsubscribe=One-Click8List-Id: <list_name> <sender_domain>Error Responses
Validation error (400)
1{2 "template_id": ["Template not found."],3 "receivers": ["Receiver at index 0 missing \"email\" key."]4}Permission denied (403)
1{2 "detail": {3 "code": "feature_not_available",4 "message": "Your Free plan does not include scheduled_campaigns.",5 "feature": "scheduled_campaigns",6 "package": "Free"7 }8}Rate limit exceeded (429)
1{2 "detail": {3 "code": "rate_limit_exceeded",4 "message": "Your Free plan allows 100 API calls per day. Limit reached.",5 "limit": 100,6 "current": 1007 }8}SMTP rate limit (429)
1{2 "detail": {3 "code": "smtp_rate_limited",4 "message": "Hourly limit (50) would be exceeded.",5 "limit_type": "hourly"6 }7}Quick Reference
| What you want to do | Method | Endpoint |
|---|---|---|
| Newsletter to all subscribers | Send to List | POST /api/emails/send/to-list/ |
| Personalized email to specific people | Send to Receivers | POST /api/emails/send/to-receivers/ |
| Transactional email (order confirmation, etc.) | Send Custom | POST /api/emails/send/custom/ |
| Schedule a campaign for later | Send to List / Receivers | Add scheduled_for field |
| Resume a failed send | Resume | POST /api/emails/campaigns/{id}/resume/ |
| Cancel a scheduled send | Cancel | POST /api/emails/campaigns/{id}/cancel/ |
| Validate SMTP credentials | Validate | POST /api/emails/smtp/validate/ |