Send transactional email
Send product emails with durable acceptance, explicit scheduling, and safer retries.
POST /v1/notifications
Provide a verified from address, one recipient, and either inline content or a template. category defaults to transactional. Idempotency-Key is also accepted as a request header.
curl -X POST "$BASE_URL/api/v1/notifications" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourdomain.com",
"to": "alex@example.com",
"subject": "A warm welcome",
"html": "<h1>Good to have you here.</h1>",
"idempotency_key": "welcome-alex-001"
}'| Field / scope | Description | Requirement / access |
|---|---|---|
to / from | Recipient and verified sender email addresses | Required |
idempotency_key | Unique key for a logical request | Required |
subject / html / text | Inline email content | Content or template |
template_id / template_version | Template and optional immutable version | Optional |
variables | String, number or boolean values for placeholders | Optional |
scheduled_at | Future ISO 8601 timestamp | Optional |
attachment_ids | Ready, unexpired attachment IDs; at most 5 | Optional |
Idempotency and scheduling
An identical retry with the same key returns the original request. Reusing a key with different content conflicts. Scheduled requests still reserve capacity at acceptance. Cancel a message before sending with POST /v1/notifications/:id/cancel.
Acceptance is asynchronous
A 202 response acknowledges durable acceptance. Inspect the returned status and timeline; provider acceptance and recipient delivery happen later. An unknown provider outcome is not automatically resent, to avoid duplicate messages.
Send from JavaScript or Python
No vendor SDK is required. Run this on your server; do not expose API keys in frontend code.
// JavaScript (server-side)
const response = await fetch(process.env.BASE_URL + "/api/v1/notifications", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.API_KEY,
"Content-Type": "application/json" },
body: JSON.stringify({ from: "hello@yourdomain.com",
to: "alex@example.com", subject: "Welcome", html: "<p>Hello!</p>",
idempotency_key: "welcome-alex-001" })
});
if (!response.ok) throw new Error(await response.text());
const { data } = await response.json();
# Python (standard library)
import json, os, urllib.request
payload = {"from": "hello@yourdomain.com", "to": "alex@example.com",
"subject": "Welcome", "html": "<p>Hello!</p>",
"idempotency_key": "welcome-alex-001"}
req = urllib.request.Request(os.environ["BASE_URL"] + "/api/v1/notifications",
data=json.dumps(payload).encode(), method="POST",
headers={"Authorization": "Bearer " + os.environ["API_KEY"],
"Content-Type": "application/json"})
with urllib.request.urlopen(req) as response:
message = json.load(response)["data"]