SendpositoryDocs
API reference

Emails

Send one message or a batch, retrieve what was sent, and cancel a scheduled send before it goes out.

Updated

Send an email

POST /v1/emails

Requires a sending or full_access key.
curl -X POST https://api.sendpository.com/v1/emails \
  -H "Authorization: Bearer $SENDPOSITORY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: welcome-4821" \
  -d '{
    "from": "Acme <hello@mail.yourdomain.com>",
    "to": ["customer@example.com"],
    "subject": "Welcome to Acme",
    "html": "<p>Your account is ready.</p>",
    "text": "Your account is ready."
  }'

Body

FieldTypeRequiredNotes
fromstringYesMust be on a verified domain. Name <addr> accepted.
tostring[]YesA single string is accepted. to, cc and bcc together take at most 50 recipients.
subjectstringYesUp to 998 characters.
htmlstringOne ofHTML body.
textstringOne ofPlain-text body. Send both when you can.
ccstring[]No
bccstring[]NoNever appears in the delivered headers.
reply_tostring[]NoWhere replies go. Up to 10 addresses.
headersobjectNoCustom headers, string values.
tagsobjectNoString key/values, for filtering in the dashboard.
attachmentsarrayNoSee Attachments.
scheduled_atstringNoISO 8601. See Scheduling.
track_opensbooleanNoAdds a tracking pixel to the HTML body. Overrides your account default either way. See Open tracking.
track_clicksbooleanNoRewrites links so clicks are recorded. See Click tracking.
list_unsubscribebooleanNoAdds RFC 8058 one-click unsubscribe headers. Single-recipient messages only. Put {{unsubscribe_url}} in html or text for a visible link - it turns this on for the message. See Unsubscribe.

At least one of html or text is required.

Headers

HeaderNotes
AuthorizationBearer sp_.... Required.
Idempotency-KeyReplaying returns the original id. See Idempotency.

Response

{ "id": "d67e39ed-96be-4b35-ab22-853163d92c92" }

Send a batch

POST /v1/emails/batch

Up to 100 messages in one request.

Send an array of the same objects. Each message succeeds or fails on its own, so one bad recipient never discards the rest.

curl -X POST https://api.sendpository.com/v1/emails/batch \
  -H "Authorization: Bearer $SENDPOSITORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    { "from": "Acme <hello@mail.yourdomain.com>", "to": ["a@example.com"], "subject": "Hi", "html": "<p>1</p>" },
    { "from": "Acme <hello@mail.yourdomain.com>", "to": ["b@example.com"], "subject": "Hi", "html": "<p>2</p>" }
  ]'

Response

Results are returned in the order you sent them, so index 0 is your first message:

{
  "data": [
    { "id": "a1aeaca2-2b41-458e-a0da-887bcc65b802" },
    { "error": { "type": "domain_not_verified", "message": "The domain `nope.test` is not registered on this account." } }
  ]
}

Check every entry

The request returns 200 even when some messages failed. Iterate the array and look for error - don't assume a 200 means all sent.

If your quota runs out mid-batch, processing stops there - remaining entries are simply absent.


Retrieve an email

GET /v1/emails/{id}

Status and full delivery timeline.
curl https://api.sendpository.com/v1/emails/d67e39ed-96be-4b35-ab22-853163d92c92 \
  -H "Authorization: Bearer $SENDPOSITORY_API_KEY"
{
  "id": "d67e39ed-96be-4b35-ab22-853163d92c92",
  "from": "Acme <hello@mail.yourdomain.com>",
  "to": ["customer@example.com"],
  "subject": "Welcome to Acme",
  "status": "delivered",
  "created_at": "2026-08-30T09:12:41.203Z",
  "sent_at": "2026-08-30T09:12:41.640Z",
  "delivered_at": "2026-08-30T09:12:42.881Z",
  "track_opens": true,
  "opened_at": "2026-08-30T09:19:07.412Z",
  "open_count": 2,
  "track_clicks": true,
  "clicked_at": "2026-08-30T09:19:31.006Z",
  "click_count": 1,
  "events": [
    { "type": "queued", "occurred_at": "2026-08-30T09:12:41.203Z" },
    { "type": "sent", "occurred_at": "2026-08-30T09:12:41.640Z" },
    { "type": "delivered", "occurred_at": "2026-08-30T09:12:42.881Z" },
    { "type": "opened", "occurred_at": "2026-08-30T09:19:07.412Z" }
  ]
}

open_count is 0 on a message sent without track_opens, which is not the same as nobody having read it - check track_opens before drawing either conclusion, and read Open tracking on what the number is worth.

Statuses

StatusMeaning
queuedAccepted, not yet handed to the provider.
scheduledHeld for a future time.
sentWith the provider.
deliveredAccepted by the recipient's mailbox.
delayedTemporary failure; being retried.
bouncedPermanent failure. Address suppressed.
complainedMarked as spam. Address suppressed.
failedCouldn't be sent.
cancelledA scheduled message you cancelled.

List emails

GET /v1/emails

Paged, newest first.
ParameterNotes
limit1-100. Default 25.
offsetFor paging.
statusOne of the statuses above.
created_afterISO 8601. Only messages created at or after this.
created_beforeISO 8601. Only messages created at or before this.
curl "https://api.sendpository.com/v1/emails?created_after=2026-08-29T00:00:00Z&status=bounced" \
  -H "Authorization: Bearer $SENDPOSITORY_API_KEY"
{
  "data": [
    {
      "id": "d67e39ed-...",
      "to": ["customer@example.com"],
      "subject": "Welcome to Acme",
      "status": "delivered",
      "created_at": "2026-08-30T09:12:41.203Z"
    }
  ],
  "total": 412,
  "limit": 25,
  "offset": 0
}

total counts everything matching the filter, not just this page - so "how many bounced in the last 24 hours" is one call rather than paging to the end and counting.


Cancel a scheduled email

POST /v1/emails/{id}/cancel

Only while still scheduled.
curl -X POST https://api.sendpository.com/v1/emails/{id}/cancel \
  -H "Authorization: Bearer $SENDPOSITORY_API_KEY"
{ "id": "169a3e78-...", "status": "cancelled" }

Returns validation_error if the message has already gone to the provider.

On this page