Sending Emails

Send transactional and marketing emails through the SentinMail API

Overview

SentinMail provides three ways to send emails:

MethodEndpointUse Case
Send to ListPOST /api/emails/send/to-list/Send a saved template to an entire subscriber list
Send to ReceiversPOST /api/emails/send/to-receivers/Send a saved template to specific recipients with per-recipient variables
Send CustomPOST /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:

  1. An API key — see Authentication
  2. An SMTP configuration — your sending server credentials
  3. A template (for methods 1 & 2) — the email content with [[variables]]
  4. A subscriber list (for method 1 only)
Info

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.

Code
bash
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:

FieldTypeRequiredDescription
companyUUIDNo*Company ID (*auto-resolved with API key auth)
namestringYesCampaign name (max 255 chars)
template_idUUIDYesEmail template to send
smtp_config_idUUIDYesSMTP configuration to use
list_idUUIDYesSubscriber list to send to
scheduled_forISO-8601 datetimeNoSchedule for later (null = send immediately)
attachment_idsUUID[]NoMedia file UUIDs to attach

Schedule for later

Code
bash
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 }'
Warning
Scheduling requires the `scheduled_campaigns` feature on your plan.

Response (201 Created):

Code
json
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.

Code
bash
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:

FieldTypeRequiredDescription
companyUUIDNo*Company ID (*auto-resolved with API key auth)
namestringYesCampaign name (max 255 chars)
template_idUUIDYesEmail template to send
smtp_config_idUUIDYesSMTP configuration to use
receiversobject[]YesArray of recipient objects (each must have email)
scheduled_forISO-8601 datetimeNoSchedule for later
attachment_idsUUID[]NoMedia 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:

Code
json
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}
FieldTypeRequiredDescription
emailstringYesPrimary recipient
ccstring[]NoCarbon copy recipients (visible to all)
bccstring[]NoBlind carbon copy recipients (hidden from others)
any other fieldstringNoAvailable as [[field_name]] template variable
Info
CC and BCC recipients receive the same rendered email as the primary recipient. Only the primary recipient's opens and clicks are tracked.

Response (201 Created):

Code
json
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}
Info
Receivers are automatically saved as subscribers to your company (best-effort — won't fail if subscriber limit is reached).

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.

Code
bash
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:

FieldTypeRequiredDescription
companyUUIDNo*Company ID (*auto-resolved with API key auth)
namestringYesCampaign name (max 255 chars)
smtp_config_idUUIDYesSMTP configuration to use
subjectstringYesEmail subject line (supports [[variables]])
bodystringYesEmail HTML body (supports [[variables]])
receiversobject[]YesArray of recipient objects (each must have email). Supports cc and bcc arrays.
attachment_idsUUID[]NoMedia file UUIDs to attach
Warning

Custom sends do not support scheduling. Use "Send to List" or "Send to Receivers" if you need scheduled delivery.

Tip

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

VariableDescription
[[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:

Code
json
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

Code
txt
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

Code
txt
1[[#each items]]
2 <li>[[this]]</li>
3[[/each]]

Campaign Management

Check campaign status

Code
bash
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:

Code
bash
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

Code
bash
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:

Code
txt
1From: <sender_name> <sender_email>
2Reply-To: <reply_to_email>
3Message-ID: <auto-generated>
4X-Mailer: SentinMail
5Precedence: bulk
6List-Unsubscribe: <unsubscribe_url>
7List-Unsubscribe-Post: List-Unsubscribe=One-Click
8List-Id: <list_name> <sender_domain>
Info
The `List-Unsubscribe` and `List-Unsubscribe-Post` headers enable one-click unsubscribe in email clients that support it (Gmail, Apple Mail, etc.).

Error Responses

Validation error (400)

Code
json
1{
2 "template_id": ["Template not found."],
3 "receivers": ["Receiver at index 0 missing \"email\" key."]
4}

Permission denied (403)

Code
json
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)

Code
json
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": 100
7 }
8}

SMTP rate limit (429)

Code
json
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 doMethodEndpoint
Newsletter to all subscribersSend to ListPOST /api/emails/send/to-list/
Personalized email to specific peopleSend to ReceiversPOST /api/emails/send/to-receivers/
Transactional email (order confirmation, etc.)Send CustomPOST /api/emails/send/custom/
Schedule a campaign for laterSend to List / ReceiversAdd scheduled_for field
Resume a failed sendResumePOST /api/emails/campaigns/{id}/resume/
Cancel a scheduled sendCancelPOST /api/emails/campaigns/{id}/cancel/
Validate SMTP credentialsValidatePOST /api/emails/smtp/validate/
sendingemailapitransactionalmarketing