Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Mail Send Reference

The /v3/mail/send endpoint and related batch endpoints. For the guide-level walk-through, see Sending Mail.

Base path: /v3/mail/ Required permission: mail.send (send), mail.schedule (batch endpoints, send_at), mail.cancel (cancel scheduled).

POST /v3/mail/send#

Queue one or more messages for delivery.

Request body:

Top-level fields:

Field Type Required Notes
personalizations array Yes 1–1000 items; each creates one message
from object Yes {email, name?}; must use verified domain (live only)
reply_to object No {email, name?}
reply_to_list array No Alternative to single reply_to
subject string Conditional Required unless each personalization has one, or template supplies it
content array Conditional Required unless template_id is set
template_id string Conditional Must start with d-; mutually exclusive with content
attachments array No Max 10 items; total body ≤ 20 MB
headers object No String → string
categories array No Max 10 items, each ≤ 255 chars
custom_args object No Arbitrary metadata
send_at integer No Unix timestamp; future schedule. Requires mail.schedule
batch_id string No From POST /v3/mail/batch
asm object No {group_id, groups_to_display?}
ip_pool_name string No Named outbound IP pool
mail_settings object No See below
tracking_settings object No See below

Personalization fields:

Field Type Required Notes
to array of {email, name?} Yes 1–1000 recipients
cc array of {email, name?} No
bcc array of {email, name?} No
subject string No Overrides top-level
headers object No Overrides top-level
substitutions object No Legacy SendGrid substitutions
dynamic_template_data object No Template rendering context
custom_args object No Overrides top-level
send_at integer No Per-personalization schedule

Content fields:

Field Type Required Notes
type string Yes text/plain or text/html
value string Yes Body content

Attachment fields:

Field Type Required Notes
content string (base64) Yes File bytes
filename string Yes 1–255 chars
type string No MIME type
disposition string No attachment (default) or inline
content_id string Conditional Required for inline

mail_settings fields:

Field Type Notes
sandbox_mode.enable bool Force sandbox for this request
bypass_list_management.enable bool Ignore all suppression lists
bypass_bounce_management.enable bool Ignore bounce suppressions
bypass_spam_management.enable bool Ignore spam-report suppressions
bypass_unsubscribe_management.enable bool Ignore unsubscribe suppressions
footer.enable bool Add a footer
footer.text string Plain-text footer
footer.html string HTML footer

tracking_settings fields:

Field Type Notes
click_tracking.enable bool
click_tracking.enable_text bool Also rewrite plain-text URLs
open_tracking.enable bool
open_tracking.substitution_tag string Custom pixel placement
subscription_tracking.enable bool
subscription_tracking.text string Plain-text unsubscribe footer
subscription_tracking.html string HTML unsubscribe footer
subscription_tracking.substitution_tag string Custom placement
ganalytics.enable bool
ganalytics.utm_source string
ganalytics.utm_medium string
ganalytics.utm_campaign string
ganalytics.utm_term string
ganalytics.utm_content string

Response (202 Accepted):

Single personalization:

json
1
{"message_id": "msg_01HXYZ"}

Multiple personalizations:

json
1
{"message_ids": ["msg_01HXYZ", "msg_01HXYZ2", "msg_01HXYZ3"]}

Error responses:

Status Shape When
400 {"errors": [...]} Validation — missing required field, conflict between content and template_id, invalid from, etc.
401 {"detail": "..."} Missing or invalid credentials
403 {"detail": "Missing required scope: mail.send"} Key lacks scope
403 {"detail": "Sender domain not verified"} from.email domain unverified (live only)
413 {"detail": "Request body exceeds maximum size of 20 MB"} Body too large
422 {"detail": [...]} Pydantic schema error
429 {"detail": "Rate limit exceeded"} Honor Retry-After header

POST /v3/mail/batch#

Create a new batch ID.

Request: empty body.

Response (201):

json
1
{"batch_id": "bat_01HXYZ"}

Permission: mail.send (creating IDs isn't restricted to mail.schedule).

GET /v3/mail/batch/{batch_id}#

Aggregate status for every message with this batch_id.

Response (200):

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "batch_id": "bat_01HXYZ",
  "total_messages": 4872,
  "status_counts": {
    "queued": 12,
    "processing": 8,
    "sent": 3200,
    "delivered": 1600,
    "bounced": 50,
    "failed": 2,
    "cancelled": 0,
    "sandbox": 0
  },
  "created_at": "2026-04-23T09:00:00Z",
  "completed_at": null,
  "is_complete": false
}

is_complete flips to true when every message has reached a terminal state.

Errors: 404 if batch_id has no messages or belongs to a different tenant.

Updated 2026-05-17 01:33:27 View source (.md) rev 1