API Reference
The Sendex API lets you send and receive email programmatically using your verified domains. All endpoints follow RESTful conventions and return JSON.
Base URL
https://sendexapi.com/api/v1Authentication
Every request must include your API key in the Authorization header as a Bearer token. Generate keys from the API Keys section of your dashboard.
Authorization: Bearer sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
send_only
Can send emails via POST /api/v1/emails. Cannot read or list messages.
full
Full read and write access: send, list, and retrieve emails and domains.
403 Forbidden.Rate Limits
Rate limits are enforced per API key. Exceeding the limit returns 429 Too Many Requests. The underlying AWS SES sending limits apply to outbound mail regardless of API limits.
Delivery & Retries
Sendex uses an internal queue between your API call and the actual SES send. This decouples your request from the network call to AWS, so transient SES failures are handled automatically without any effort on your side.
How it works
- Your
POST /api/v1/emailsrequest returns202 Acceptedimmediately — the email is queued, not yet sent. - A background worker picks up the job and sends it to AWS SES.
- If SES returns an error, the job is automatically retried with exponential backoff.
- Once sent (or permanently failed), a webhook fires so you know the outcome.
Retry schedule
| Attempt | Delay before retry |
|---|---|
| 1 → 2 | 30 s |
| 2 → 3 | 60 s |
| 3 → 4 | 2 min |
| 4 → 5 | 4 min |
| 5+ | 8 min → 30 min (cap) |
| Max attempts | 54 total — ~24 h coverage |
202 Accepted response does not mean the email was delivered — only that it was queued. Subscribe to the email.sent and email.failed webhooks to track the actual outcome.Errors
All errors return a JSON body with error (machine-readable slug) and message (human-readable string).
{
"error": "bad_request",
"message": "from, to, subject, and text or html are required."
}| HTTP status | error slug | Meaning |
|---|---|---|
| 400 | bad_request | Missing or invalid parameters. |
| 401 | unauthorized | Missing or invalid API key. |
| 403 | forbidden | Key lacks permission for this resource. |
| 404 | not_found | The requested resource does not exist. |
| 422 | unprocessable_entity | Domain is not verified for sending. |
| 429 | too_many_requests | Rate limit exceeded. |
| 502 | bad_gateway | Upstream SES error. |
| 500 | internal_server_error | Unexpected server error. |
Webhooks
Webhooks allow Sendex to push real-time event notifications to your server. Register endpoints from the Webhooks section of your dashboard.
Payload format
Every webhook request is an HTTP POST with a JSON body and a signature header.
POST https://your-server.com/webhook
Content-Type: application/json
X-Sendex-Signature: sha256=<hmac-sha256-hex>
{
"event": "email.sent",
"created_at": "2026-03-05T12:00:00.000Z",
"data": { ... }
}Verifying signatures
Compute HMAC-SHA256(secret, rawBody) and compare it to the value in the X-Sendex-Signature header (after stripping the sha256= prefix). Use a timing-safe comparison.
import crypto from "crypto";
function verifyWebhook(rawBody: string, secret: string, header: string): boolean {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(header),
Buffer.from(expected)
);
}Events
| Event | Description | Requires |
|---|---|---|
email.sent | Email successfully sent via SES. | — |
email.failed | Email send to SES failed. | — |
email.received | Inbound email received. | — |
email.delivered | SES confirmed delivery. | SES_CONFIG_SET |
email.bounced | SES reported a bounce. | SES_CONFIG_SET |
email.complained | Recipient marked as spam. | SES_CONFIG_SET |
email.opened | Recipient opened the email. | SES_CONFIG_SET + open tracking |
email.clicked | Recipient clicked a link. | SES_CONFIG_SET + click tracking |
broadcast.queued | Broadcast was created and queued. | — |
broadcast.started | Broadcast worker began sending. | — |
broadcast.completed | Broadcast finished sending. | — |
broadcast.paused | Broadcast was paused. | — |
broadcast.cancelled | Broadcast was cancelled. | — |
broadcast.failed | Broadcast worker encountered a fatal error. | — |
contact.created | A contact was created or upserted. | — |
contact.subscribed | Contact subscription was set to true. | — |
contact.unsubscribed | Contact unsubscribed (via API or unsubscribe page). | — |
SES delivery events setup
Events like email.delivered, email.bounced, and email.complained require an AWS SES Configuration Set with an SNS destination pointing to /api/ses-notifications. Set the SES_CONFIG_SET environment variable to the name of your configuration set.