API reference
Emails API
Reference for sending, batch sending, listing, retrieving, rescheduling and canceling emails.
The emails API sends transactional email and lets you follow what happened to it. All requests use https://api.sooner.dev and a bearer API key. See Authentication and Errors.
| Endpoint | Permission |
|---|---|
POST /emails | sending_access or full_access |
POST /emails/batch | sending_access or full_access |
GET /emails, GET /emails/{id}, PATCH /emails/{id}, POST /emails/{id}/cancel | full_access |
Send an email
Sends one email. The full guide, with every field and limit, is Send an email.
| Field | Type | Required | Description |
|---|---|---|---|
from | string | Yes, unless a template sets it | hello@domain.com or Name <hello@domain.com>. The domain must be verified in the key's project. |
to | string or string[] | Yes | Recipients. At most 50 across to, cc and bcc. |
subject | string | Yes, unless a template sets it | 1 to 998 characters. |
html, text | string | One of html, text or template | The body. |
template | object | One of html, text or template | { "id": "...", "variables": { ... } }. |
cc, bcc | string or string[] | No | Additional recipients. |
reply_to | string or string[] | No | Reply-to addresses. replyTo is also accepted. |
headers | object | No | Custom headers as string keys and values. |
tags | object[] | No | Up to 10 { name, value } pairs. |
attachments | object[] | No | Up to 50 files. |
scheduled_at | string | No | ISO 8601 or in 1 hour, at most 30 days ahead. |
topic_id | string | No | Honors the recipient's topic subscription. |
Send an Idempotency-Key header to make retries safe. See Idempotency.
curl -X POST https://api.sooner.dev/emails \
-H "Authorization: Bearer $SOONER_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-confirmation" \
-d '{
"from": "Acme <orders@mail.yourcompany.com>",
"to": ["ada@example.com"],
"subject": "Your order is confirmed",
"html": "<p>Thanks for your order.</p>"
}'{ "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }Notable errors: invalid_from_address, restricted_api_key, validation_error, missing_required_field, not_found (unknown template or topic), invalid_idempotent_request, rate_limit_exceeded, daily_quota_exceeded and monthly_quota_exceeded.
Send a batch
Sends up to 100 emails in one request. The body is a JSON array where each element has the fields above, except attachments and scheduled_at, which are rejected. Validation is all or nothing. See Batch sending.
curl -X POST https://api.sooner.dev/emails/batch \
-H "Authorization: Bearer $SOONER_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{ "from": "Acme <hello@mail.yourcompany.com>", "to": ["ada@example.com"], "subject": "Hi Ada", "html": "<p>Hi</p>" },
{ "from": "Acme <hello@mail.yourcompany.com>", "to": ["grace@example.com"], "subject": "Hi Grace", "html": "<p>Hi</p>" }
]'{
"data": [
{ "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" },
{ "id": "01jcz8w3f2z1h6c8d4e2f9g7t3" }
]
}Notable errors: the same as POST /emails. A validation message names the failing position, for example (email 1).
List emails
Returns your project's emails, newest first, inside your plan's log retention. Needs full_access.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer | No | 1 to 100, default 20. |
after | string | No | The ID of the last email of the previous page. |
curl "https://api.sooner.dev/emails?limit=2" \
-H "Authorization: Bearer $SOONER_API_KEY"{
"object": "list",
"data": [
{
"object": "email",
"id": "01jcz8w3f2z1h6c8d4e2f9g7t3",
"from": "Acme <hello@mail.yourcompany.com>",
"to": ["grace@example.com"],
"cc": null,
"bcc": null,
"reply_to": null,
"subject": "Hi Grace",
"html": null,
"text": null,
"tags": [],
"created_at": "2026-11-14T10:30:58.000Z",
"scheduled_at": null,
"last_event": "delivered",
"status": "delivered"
}
],
"has_more": true
}In lists, html and text are always null. Retrieve one email to read its body. See Pagination.
Retrieve an email
Returns one email with its body. Needs full_access.
curl https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0 \
-H "Authorization: Bearer $SOONER_API_KEY"{
"object": "email",
"id": "01jcz8w3f2x9k4m7n6p5q2r1s0",
"from": "Acme <orders@mail.yourcompany.com>",
"to": ["ada@example.com"],
"cc": null,
"bcc": null,
"reply_to": null,
"subject": "Your order is confirmed",
"html": "<p>Thanks for your order.</p>",
"text": null,
"tags": [{ "name": "category", "value": "order_confirmation" }],
"created_at": "2026-11-14T10:30:58.000Z",
"scheduled_at": null,
"last_event": "delivered",
"status": "delivered"
}| Field | Description |
|---|---|
status | One of queued, scheduled, sending, sent, delivery_delayed, delivered, bounced, complained, failed, canceled. See Send an email. |
last_event | The most recent event without the email. prefix, for example opened or suppressed. |
scheduled_at | The send time, or null. |
Notable errors: not_found for an unknown ID, an ID from another project, or an email past its retention window.
Reschedule an email
Changes the send time of an email whose status is scheduled. Needs full_access.
| Field | Type | Required | Description |
|---|---|---|---|
scheduled_at | string | Yes | ISO 8601 or in 1 hour, at most 30 days from now. |
curl -X PATCH https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0 \
-H "Authorization: Bearer $SOONER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "scheduled_at": "2026-11-21T09:00:00Z" }'{ "object": "email", "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }Notable errors: validation_error ("Only scheduled emails can be updated." or an invalid or too distant scheduled_at) and not_found.
Cancel an email
Cancels a scheduled email. The status becomes canceled and its quota count is returned. Needs full_access.
curl -X POST https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0/cancel \
-H "Authorization: Bearer $SOONER_API_KEY"{ "object": "email", "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }Notable errors: validation_error ("Only scheduled emails can be canceled.") and not_found.
With the Node SDK
import { Resend } from "resend";
const resend = new Resend(process.env.SOONER_API_KEY, { baseUrl: "https://api.sooner.dev" });
const { data: sent } = await resend.emails.send({
from: "Acme <orders@mail.yourcompany.com>",
to: ["ada@example.com"],
subject: "Your order is confirmed",
html: "<p>Thanks for your order.</p>",
});
const { data: email } = await resend.emails.get(sent!.id);
console.log(email?.status, email?.last_event);