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.

EndpointPermission
POST /emailssending_access or full_access
POST /emails/batchsending_access or full_access
GET /emails, GET /emails/{id}, PATCH /emails/{id}, POST /emails/{id}/cancelfull_access

Send an email

POST/emails

Sends one email. The full guide, with every field and limit, is Send an email.

FieldTypeRequiredDescription
fromstringYes, unless a template sets ithello@domain.com or Name <hello@domain.com>. The domain must be verified in the key's project.
tostring or string[]YesRecipients. At most 50 across to, cc and bcc.
subjectstringYes, unless a template sets it1 to 998 characters.
html, textstringOne of html, text or templateThe body.
templateobjectOne of html, text or template{ "id": "...", "variables": { ... } }.
cc, bccstring or string[]NoAdditional recipients.
reply_tostring or string[]NoReply-to addresses. replyTo is also accepted.
headersobjectNoCustom headers as string keys and values.
tagsobject[]NoUp to 10 { name, value } pairs.
attachmentsobject[]NoUp to 50 files.
scheduled_atstringNoISO 8601 or in 1 hour, at most 30 days ahead.
topic_idstringNoHonors the recipient's topic subscription.

Send an Idempotency-Key header to make retries safe. See Idempotency.

Shell
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>"
  }'
JSON
{ "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

POST/emails/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.

Shell
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>" }
  ]'
JSON
{
  "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

GET/emails

Returns your project's emails, newest first, inside your plan's log retention. Needs full_access.

ParameterTypeRequiredDescription
limitintegerNo1 to 100, default 20.
afterstringNoThe ID of the last email of the previous page.
Shell
curl "https://api.sooner.dev/emails?limit=2" \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{
  "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

GET/emails/{id}

Returns one email with its body. Needs full_access.

Shell
curl https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0 \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{
  "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"
}
FieldDescription
statusOne of queued, scheduled, sending, sent, delivery_delayed, delivered, bounced, complained, failed, canceled. See Send an email.
last_eventThe most recent event without the email. prefix, for example opened or suppressed.
scheduled_atThe 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

PATCH/emails/{id}

Changes the send time of an email whose status is scheduled. Needs full_access.

FieldTypeRequiredDescription
scheduled_atstringYesISO 8601 or in 1 hour, at most 30 days from now.
Shell
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" }'
JSON
{ "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

POST/emails/{id}/cancel

Cancels a scheduled email. The status becomes canceled and its quota count is returned. Needs full_access.

Shell
curl -X POST https://api.sooner.dev/emails/01jcz8w3f2x9k4m7n6p5q2r1s0/cancel \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{ "object": "email", "id": "01jcz8w3f2x9k4m7n6p5q2r1s0" }

Notable errors: validation_error ("Only scheduled emails can be canceled.") and not_found.

With the Node SDK

TypeScript
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);