Transactional API
Send receipts, alerts, and product messages from your application.
View as Markdown ↗Project key. Acts in the project the key belongs to. An all-access key works too — name the project with an X-Project-Id header. How keys and scopes work.
Endpoint
/api/v1/emailsAuthenticate every request with an Email Bump API key. The sender must use your shared testing domain or a verified sending domain.
Send an email
Body parameters
fromstringrequiredSender, optionally with a display name: Acme <[email protected]>. Must be your shared or a verified domain.
tostringrequiredRecipient email address. A one-element array (["[email protected]"]) is accepted too, for clients ported from an API that takes a list.
subjectstringoptionalSubject line (or inherited from a template).
htmlstringoptionalHTML body.
textstringoptionalPlain-text body.
reply_tostringoptionalReply-To address.
templateobjectoptionalRender a saved template: { "id", "variables" } instead of raw content.
template_idstringoptionalFlat alternative to template.id.
variablesobjectoptionalFlat alternative to template.variables. Values are readable both at the top level ({{ order_id }}) and under contact.
attachmentsarrayoptionalFiles to send along. See Attachments.
scheduled_atstringoptionalRFC 3339 future time to send later instead of now.
streamstringoptionaltransactional (default) or marketing. See Marketing over the API.
curl -X POST https://emailbump.com/api/v1/emails \
-H "Authorization: Bearer ebk_your_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <[email protected]>",
"to": "[email protected]",
"reply_to": "[email protected]",
"subject": "Your order has shipped",
"html": "<h1>On the way</h1><p>Tracking: EB-2048</p>",
"text": "Your order is on the way. Tracking: EB-2048"
}'Provide html, text, or both. When only HTML is supplied, Email Bump creates a text version. When only text is supplied, it creates a minimal HTML part.
This endpoint takes a single to address — there's no cc or bcc. Each send is metered, tracked and unsubscribed on its own, so a list of five addresses is five calls. Pass more than one and you'll get a 400 saying exactly that. For real cc/bcc, send over SMTP with the same key; to reach an audience in one request, use a campaign.
Marketing over the API
Add "stream": "marketing" to send a promotional one-off without building a campaign or flow — an announcement to one person, a hand-triggered win-back, your own sending logic driving the API. The message then carries what marketing mail must: the recipient is checked against their contact record and refused if they've unsubscribed or their address is suppressed, the marketing footer with its unsubscribe link is appended, one-click unsubscribe headers are set, and the send is classified under the marketing stream in analytics.
curl -X POST https://emailbump.com/api/v1/emails \
-H "Authorization: Bearer ebk_your_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <[email protected]>",
"to": "[email protected]",
"subject": "Something new for you",
"html": "<p>We just launched…</p>",
"stream": "marketing"
}'A recipient who isn't a contact yet is created as one, the same way the Events API does — the unsubscribe link needs a record to point at. scheduled_at isn't supported on this stream yet; to schedule marketing email, use a campaign. Transactional sends (the default) are unchanged: no footer, no consent gate — receipts and password resets go to unsubscribed recipients too, as they should.
What happened to it
/api/v1/emails/{id}The id POST /v1/emails returned, and what became of the message:
curl https://emailbump.com/api/v1/emails/50b0b9a4-d447-480a-ad5b-3e1b999dd842 \
-H "Authorization: Bearer ebk_your_key"{
"object": "email",
"id": "50b0b9a4-…",
"to": "[email protected]",
"subject": "Your order has shipped",
"status": "sent",
"error": null,
"events": [
{ "type": "send", "occurred_at": "2026-08-03T01:27:57Z" },
{ "type": "delivery", "occurred_at": "2026-08-03T01:27:59Z" },
{ "type": "open", "occurred_at": "2026-08-03T01:31:12Z", "machine_open": false }
]
}status is our own record of the submission; events is what the provider reported afterwards. The distinction matters: "sent" means it was accepted for delivery, not that it arrived. A message to a domain with no MX record is "sent" and then bounces — the bounce event is where that shows up, usually within seconds.
Attachments
Send a receipt with its invoice, a report, a photo — anything, as base64. Up to 20 files totalling 25 MB per message.
Each attachment
filenamestringrequiredThe name the recipient sees when they save it.
contentstringrequiredThe file, base64-encoded. A whole data: URI works too, so a browser's FileReader result can be passed straight through.
content_typestringoptionalMIME type. Guessed from the filename when you leave it out.
content_idstringoptionalSet this to embed the file in the HTML instead of listing it at the bottom, and reference it as <img src="cid:the-id">.
curl -X POST https://emailbump.com/api/v1/emails \
-H "Authorization: Bearer ebk_your_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <[email protected]>",
"to": "[email protected]",
"subject": "Your invoice",
"html": "<p>Thanks! Your invoice is attached.</p><img src=\"cid:logo\">",
"attachments": [
{ "filename": "invoice-4821.pdf", "content": "JVBERi0xLjQK..." },
{ "filename": "logo.png", "content": "iVBORw0KGgo...", "content_id": "logo" }
]
}'A PDF invoice is normal and fine. Executables, archives and macro-enabled documents are what spam filters look for — if a message must carry one, a link to it delivers far better than the file itself.
Schedule for later
Add scheduled_at (RFC 3339, must be in the future) to send at a specific time. The response comes back with status: "scheduled"; Email Bump delivers it when it comes due.
curl -X POST https://emailbump.com/api/v1/emails \
-H "Authorization: Bearer ebk_your_key" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <[email protected]>",
"to": "[email protected]",
"subject": "Your trial ends soon",
"html": "<p>Just a heads up…</p>",
"scheduled_at": "2026-08-01T09:00:00Z"
}'Use a template
Pass a saved template ID and variables instead of raw content. Variables are available at the top level and under contact.
{
"from": "Acme <[email protected]>",
"to": "[email protected]",
"template": {
"id": "9d7073b0-63b0-4ad0-925e-c43f04619ec2",
"variables": {
"first_name": "Jamie",
"order_id": "EB-2048"
}
}
}Response
{
"id": "6dde5322-c940-43e5-84cc-97a2d8c69c08",
"message_id": "provider-message-id",
"to": "[email protected]",
"from": "Acme <[email protected]>",
"subject": "Your order has shipped",
"status": "sent",
"created_at": "2026-07-20T18:42:10Z"
}Every accepted send is recorded in Developers → Transactional, including its rendered content, original payload, and event timeline.