POST /send-email
Queues an email for immediate sending or schedules it for a future date.
Advanced API (
recipient/sender/html) — the only one with scheduled sending, templates, and dry_run. For simple integrations, the v1 API uses REST-style names and a stable contract.Authentication
Bearer token with a project API key:sk_proj_*/sk_live_* (Live) and sk_test_* (Test) are accepted. The project_id is inferred from the key.
Learn more in Authentication.
Request Body
Attachments
Each object in theattachments array has the following structure:
Limits:
- Maximum 10 attachments per email.
- Maximum 10 MB per individual attachment.
- Maximum 10 MB total per email (sending-infrastructure limit).
Scheduled Sending
Thescheduled_at field supports multiple formats:
ISO 8601 timestamp
Natural language (English)
Time zone (timezone)
The timezone field is used to interpret scheduled_at correctly. It supports:
- IANA name:
"America/Santiago","US/Eastern","Europe/Madrid" - Offset:
"+09:00","-05:00","UTC-3","GMT+9" - Integer number:
-3,9(no fractions — for half-hour zones like India, use the"+05:30"format)
timezone is provided, the project’s default time zone (default_timezone) is used. If the project has no time zone configured, UTC is assumed.
Learn more in Schedule Sends.
Manage a Scheduled Send
APOST /send-email with scheduled_at returns a scheduled_send_id (see the scheduled send response). With that ID you can inspect, reschedule, or cancel the send while it is still in pending status.
GET /v1/scheduled-sends/:id
Returns the status of the scheduled send.- cURL
200 OK
Responds
404 NOT_FOUND if the ID does not exist in the project.
PATCH /v1/scheduled-sends/:id
Reschedules the send to a new date. Only while the status ispending.
- cURL
200 OK — the updated row, with the same columns as the GET.
DELETE /v1/scheduled-sends/:id
Cancels the send if it is stillpending.
- cURL
200 OK
Dry Run
With"dry_run": true, the endpoint renders the email without sending it: it does not queue, log activity, or consume quota. Useful for validating the payload and variables before an actual send.
html_preview is truncated to 8000 characters. Variables with no value in data are left as {{variable}} in the preview.
Headers
Learn more about
Idempotency-Key in API Keys.
Request Example
Immediate send
- cURL
- Node.js
- Python
200 OK
Scheduled send with attachments
- cURL
- Node.js
- Python
200 OK
Learn more in Node.js SDK.
Immediate Send Response
Sending is asynchronous: the endpoint queues the email and responds immediately with200 (see the example above). The actual send happens in the background.
The final send result (delivered, bounced, failed) does not come in this response: it is queried via the Activity API using
email_id, or received via webhooks (email.send, email.delivery, email.bounce, etc.).
Send Type
Theemail_type field declares the nature of the send, and that determines which suppressions apply to it.
Why the default is
transactional: a bounce is a signal from the mailbox (that address does not
exist) and must block everything, but a newsletter unsubscribe is a signal from the user about
your campaigns — it cannot stop them from receiving their verification code or the result they
asked for.campaign and automation are treated as marketing. Any other value (individual,
welcome, …) is accepted for backwards compatibility and counts as transactional.
Suppressed Recipient
If the recipient is on the project’s suppression list, the email is not queued: the API responds200 immediately with suppressed: true, and nothing goes out to the recipient.
Which suppressions apply depends on the send type: a transactional send is only blocked by a hard bounce or complaint; a marketing send is also blocked by voluntary unsubscribes.
This is an intentional
200 (not an error): retrying the same send returns the same result. The right thing to do is to flag the address in your system and stop sending to it — you can also listen to the email.suppressed webhook to sync it automatically. The list is managed in the dashboard (Audience → Suppressed).
Automatic Reply-To (Inbound Email)
All sends via the API automatically include aReply-To header with the sender’s name:
email.inbound webhook with attachments. See Webhooks → Inbound.
Sender Verification
Thesender must be verified before you can send. There are two ways:
- Verify the individual email via magic link (
POST /domains/verify-email). - Verify the entire domain via DNS (
POST /domains/register) — recommended, improves deliverability and lets you send from any address on the domain.
200 with queued: true, and the rejection happens in the background. The failure is recorded in the activity record (current_status: "failed", error_message), queryable via Activity with the email_id.
Learn more in Domains API.
Error Codes
Since sending is asynchronous, the only synchronous errors (in the HTTP response) are validation errors:
Send errors (unverified sender, invalid attachment, rate limit) occur in the background: the activity record moves to
current_status: "failed" with the detail in error_message.