One REST API for transactional email, with SMS on the same endpoint. Idempotent by design, so a retry never sends twice.
Create a workspace, verify your sending domain, and copy your key from API Keys. Test keys sandbox everything.
One POST request. The response returns a message id you can follow for as long as the record lives.
Read the current status of any message with a GET. Delivery, bounce and complaint outcomes land there as SES reports them.
curl -X POST https://api.kazuni.io/v1/emails/send \
-H "Authorization: Bearer cl_live_..." \
-H "Idempotency-Key: order-4519-confirmation" \
-H "Content-Type: application/json" \
-d '{
"from": "Yourshop <receipts@yourshop.mw>",
"to": "amina@example.com",
"subject": "Your order is on its way",
"html": "<p>Order 4519 shipped today.</p>",
"text": "Order 4519 shipped today."
}'
202 { "id": "9f2c1a84-...", "status": "queued" }The TypeScript SDK is published: npm i @kazuni/sdk
/v1/emails/sendSend one transactional email/v1/emails/{id}Delivery status for one email/v1/messages/sendSame shape with a channel field: email or sms/v1/messages/{id}Delivery status for one message, any channel/v1/contactsList, create, update and delete contacts and their identifiers/v1/meVerify a key and read the permissions it carriesSigned delivery events posted to your endpoint. Today the same outcomes are readable by polling a message.
Author once, send a template name plus data, with a per-channel variant chosen server-side.
Resolve a saved audience to its members from the API.
A send returns immediately as queued. Poll the message to see where it got to.
GET /v1/messages/9f2c1a84-...
200 {
"id": "9f2c1a84-...",
"channel": "email",
"from": "receipts@yourshop.mw",
"to": "amina@example.com",
"subject": "Your order is on its way",
"segments": null,
"status": "delivered",
"error": null,
"queuedAt": "2026-07-24T09:14:02.118Z",
"sentAt": "2026-07-24T09:14:03.402Z",
"deliveredAt": "2026-07-24T09:14:06.881Z"
}