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/emails
POST/v1/emails/batch
GET/v1/emails/:id
GET/v1/emails
POST/v1/emails/:id/cancel
GET/v1/templates
POST/v1/templates
GET/v1/templates/:slug
PUT/v1/templates/:slug
DELETE/v1/templates/:slug
GET/v1/suppressions
POST/v1/suppressions
DELETE/v1/suppressions/:email
POST/v1/webhooks
GET/v1/webhooks
DELETE/v1/webhooks/:id
POST/v1/api-keys
GET/v1/api-keys
DELETE/v1/api-keys/:id
GET/v1/domains
POST/v1/domains
POST/v1/domains/:id/verify
GET/v1/events
GET/v1/analytics/overview
GET/v1/health

POST /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

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", ... }
  ]
}