Documentation
Send your first email in five minutes.
If you have used Resend or Postmark before, this will feel familiar. If not, every step below is plain copy-paste.
Generate an API key
Sign up, then visit /dashboard/api-keys/new. Save the key somewhere safe. We never show it again.
Verify a sending domain
Go to /dashboard/domains/new, enter your domain, and publish the three DNS records (DKIM, SPF, DMARC) we show you. Click "Verify DNS now" once the records propagate.
Send your first email
Via curl:
curl -X POST https://postdex.io/v1/emails \
-H "Authorization: Bearer pdx_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@mail.acme.com>",
"to": "user@example.com",
"subject": "Hello",
"html": "<p>Sent via Postdex.</p>"
}'Via the SDK:
import { Postdex } from "postdex";
const client = new Postdex(process.env.POSTDEX_API_KEY!);
await client.emails.send({
from: "Acme <hello@mail.acme.com>",
to: "user@example.com",
subject: "Hello",
html: "<p>Sent via Postdex.</p>",
});Idempotency
Pass an Idempotency-Key header (or idempotencyKey in the SDK) on every send. We cache the response for 24 hours so retries do not double-send.
Receiving webhooks
Add a destination at /dashboard/webhooks/new. We POST JSON to your URL on every email event with an HMAC-SHA256 signature in X-Postdex-Signature.
// Verify in your webhook handler:
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = createHmac("sha256", process.env.WEBHOOK_SECRET!)
.update(rawBody)
.digest("hex");
const provided = req.headers["x-postdex-signature"]?.replace(/^sha256=/, "");
if (!timingSafeEqual(Buffer.from(expected), Buffer.from(provided))) {
return res.status(401).end();
}Rate limits
Free tier: 100 sends per rolling 7 days. Paid tiers raise that to the monthly quota of your plan. We return 429 with a Retry-After header when you exceed.
Error shape
Every 4xx and 5xx response uses the same JSON shape:
{
"name": "validation_error",
"message": "Human-readable explanation.",
"statusCode": 422
}