Features Pricing Compare Cost calculator Guides Quickstarts Docs All systems nominal

A REST API for Amazon SES, no SigV4

Call Amazon SES over a REST API or an SMTP relay, authenticated with a Bearer token instead of an AWS SigV4 signature.

Last reviewed:

Sending one email with a Bearer token instead of SigV4

There is one call to learn: POST https://api.sendvia.io/v1/send, authenticated with Authorization: Bearer sv_live_your_api_key and a JSON body. Nothing on your side signs a request, builds a canonical string or rotates a session token; sendvia holds the IAM credentials for your SES account and makes the signed call to AWS itself.

curl -X POST https://api.sendvia.io/v1/send \
  -H "Authorization: Bearer sv_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "from":    "[email protected]",
    "to":      "[email protected]",
    "subject": "Your receipt",
    "html":    "<p>Thanks for your order.</p>",
    "tag":     "receipt"
  }'

A successful send answers {"success":true,"message_id":"...","log_id":4821}. Add a future send_at in ISO 8601, up to 72 hours out, and the same call instead answers {"success":true,"status":"scheduled","log_id":4821} without touching SES until that time arrives. The one error worth expecting early is a 422 on a from-address whose domain has not been verified in your account yet:

# 422
{"error":"The domain \"yourdomain.com\" has not been verified. Add and verify it in the dashboard first."}

That check exists because sendvia has no sending identity of its own to fall back on. Verify the domain on the dashboard first and the same request goes through. template_id or template_alias can replace subject, html and text entirely, with a variables object filling in {{tokens}} from a template you built once in the dashboard.

Sending to more than one recipient in a call

to also accepts an array, up to 50 entries, each a plain address or a {"email":"...","name":"..."} object, deduplicated by address before anything is sent. The response shape changes shape to match: a results array with one entry per recipient, a running sent and failed count, and an HTTP status that tells you how the batch went without parsing the body first, 200 if everything sent, 207 if some recipients failed and others did not, 502 if every single one failed.

A recipient already on your blocklist is not attempted at all; it comes back as {"email":"...","status":"blocked"} in the same array, logged the same way a real send would be so it still shows up in your history.

Batching different emails together

POST /v1/send/batch is for a different problem: not one email to many people, but many different emails in one HTTP round trip, up to 500 messages in a messages array. Each entry is its own from, to (a single address here, not an array), subject and body, and can carry its own template_alias and variables, so a thousand personalised receipts can go out as one request instead of a thousand. Results come back indexed by position rather than by email, {"index":0,"status":"sent","message_id":"...","log_id":4901}, with the same 200 / 207 / 502 status logic as a multi-recipient send.

Retrying safely without sending twice

Send an Idempotency-Key header on either endpoint and a retry within 24 hours of the original, whether from a timeout, a proxy retry, or a queue worker firing twice, returns the exact original response again, with an X-Idempotent-Replayed: true header added so you can tell the two apart. No key means no such protection: a retried call is a second send.

Every other endpoint

The send calls are most of what a transactional integration needs; the rest of the API covers everything else the dashboard can do.

Every route the API serves under /v1, grouped by what it manages. Endpoints marked Premium require that plan on the account, regardless of who holds the API key.
MethodPathPlanWhat it does
POST/v1/sendFreeSend to one address or an array of addresses
POST/v1/send/batchFreeSend up to 500 distinct messages in one call
DELETE/v1/scheduled/{id}FreeCancel a message queued with send_at, before it goes out
GET / POST/v1/templatesFreeList or create reusable templates
PUT / DELETE/v1/templates/{id}FreeUpdate or remove one template
GET/v1/domainsFreeList verified sending domains on the account
GET/v1/statsFreeAggregate counts, optionally filtered by tag or date range
GET/v1/logsFreeSearch sent messages by address, status or tag
GET/v1/blocklistFreeThe 200 most recently suppressed addresses
GET/v1/blocklist/checkFreeWhether one address is currently suppressed
POST/v1/blocklist/unblockFreeRemove one address from the blocklist
GET / POST/v1/listsPremiumList or create mailing lists
POST/v1/lists/{id}/subscribersPremiumAdd subscribers to a list in bulk
POST/v1/lists/{id}/importPremiumQueue a CSV file for background import
GET/v1/lists/{id}/import/{importId}PremiumCheck an import's progress
DELETE/v1/lists/{id}PremiumDelete a list, refused while a newsletter is sending to it
GET / POST/v1/newslettersPremiumList or create newsletter campaigns
GET / PUT / DELETE/v1/newsletters/{id}PremiumRead, edit or delete one draft campaign
POST/v1/newsletters/{id}/sendPremiumQueue a draft for sending to its target lists
GET/v1/newsletters/{id}/statusPremiumSend progress, broken down by recipient status

No HTTP client at all: the SMTP relay

Point any SMTP-speaking application at smtp.sendvia.io on port 587, or 2525 if your network blocks 587, with STARTTLS, username sendvia (the value is not checked) and your API key as the password. It runs through the identical pipeline as the REST calls above: the same domain verification, the same tracking, the same blocklist. SMTP has no field for a tag, so one is read out of the subject line instead, {{tag=receipt}} anywhere in the text, stripped before the recipient sees it, letters, numbers, hyphens and underscores only, up to 64 characters, matching the same tag field the JSON API accepts.

Host:       smtp.sendvia.io
Port:       587 (or 2525 if blocked)
Encryption: STARTTLS
Username:   sendvia
Password:   sv_live_your_api_key

One honest caveat: the relay is a paid add-on on top of premium rather than something every account gets. There is no self-serve switch for it and no published price; you email support once you are on premium and they turn it on for your account.

Limits, and what this API does not do

The AWS side is still yours to set up: an account, an IAM user scoped to SES and SNS, and a domain that starts inside Amazon's sandbox at 200 emails a day to addresses you have separately verified, until a production access review clears, usually in 12 to 24 hours. None of that goes away because you never touch a signature.

On top of that, the API itself has fixed ceilings rather than negotiated ones: 50 recipients on a single /v1/send call, 500 messages on a batch, and 72 hours as far as send_at will schedule ahead. There is no official SDK in any language, so every sample on this page is a plain HTTP call or a raw SMTP connection rather than an installed package. Nor is there a dedicated IP option to request through the API, an inbound email or parsing endpoint to receive mail on, or teams or sub-accounts to scope a key to: one API key reaches everything on the account it belongs to. Logs kept against a message run 7 days on the free plan and 90 on premium ($19 a month), and that is also the window /v1/logs can search.