API reference
The Kovarro Mail REST API is JSON-only, scoped per tenant via your API key, and versioned. Every request requires Authorization: Bearer nm_… in the header. Base URL: https://api.example.com/v1/.
Endpoints
POST
/v1/emailsPOST
/v1/emails/batchGET
/v1/emails/:idGET
/v1/emailsPOST
/v1/emails/:id/cancelGET
/v1/templatesPOST
/v1/templatesGET
/v1/templates/:slugPUT
/v1/templates/:slugDELETE
/v1/templates/:slugGET
/v1/suppressionsPOST
/v1/suppressionsDELETE
/v1/suppressions/:emailPOST
/v1/webhooksGET
/v1/webhooksDELETE
/v1/webhooks/:idPOST
/v1/api-keysGET
/v1/api-keysDELETE
/v1/api-keys/:idGET
/v1/domainsPOST
/v1/domainsPOST
/v1/domains/:id/verifyGET
/v1/eventsGET
/v1/analytics/overviewGET
/v1/healthPOST /v1/emails
The primary send endpoint. Accepts a JSON body and returns the queued email.
Request
application/json
{
"from": "noreply@mail.example.com",
"to": "user@example.com",
"cc": ["other@example.com"],
"bcc": ["audit@yourapp.com"],
"replyTo": "support@example.com",
"subject": "Your password reset",
"html": "<p>Click to reset.</p>",
"text": "Click to reset.",
"templateSlug": "password-reset",
"data": { "name": "Sarah", "resetUrl": "https://..." },
"sendAt": "2026-06-20T09:00:00Z",
"undoWindowSeconds": 10,
"idempotencyKey": "u-42-pwreset-3",
"metadata": { "userId": "u_42" }
}Either html + text or templateSlug + data must be present. Templates win when both are sent.
Response
HTTP 202 Accepted
{
"id": "01J0X...",
"messageId": "msg_8gK9p2x1WqYz",
"status": "QUEUED",
"provider": "SMTP",
"to": "user@example.com",
"from": "Org Owner <noreply@mail.example.com>",
"subject": "Your password reset",
"providerMessageId": null
}Errors
400— invalid body (missing field, malformed email)401— invalid or revoked API key403— API key lacks theemails:sendscope, or tenant plan doesn't include developer API access409— recipient is on the suppression list429— rate limit exceeded; respectRetry-After(seconds). Pair the retry with anidempotencyKey.
POST /v1/emails/batch
Send up to 100 emails in one request — each item takes the same shape as POST /v1/emails. Each is processed independently, so a bad item is reported in its slot without sinking the rest. Response is 207 Multi-Status.
HTTP 207 Multi-Status
{
"total": 3,
"sent": 2,
"failed": 1,
"results": [
{ "index": 0, "ok": true, "id": "...", "status": "QUEUED", "to": "a@example.com", ... },
{ "index": 1, "ok": false, "statusCode": 409, "error": "Recipient bounced@example.com is on the suppression list (HARD_BOUNCE)" },
{ "index": 2, "ok": true, "id": "...", "status": "QUEUED", "to": "c@example.com", ... }
]
}