API reference

API reference

A RESTful JSON API over HTTPS. The base URL is https://api.mailstack.voostack.com. Authenticate every request with a Bearer API key; the key resolves to your organization and all resources are scoped to it.

Authentication

Authorization: Bearer ms_live_xxxxxxxxxxxxxxxxxxxx

Keys carry scopes (for example emails:send). A request to an endpoint your key isn't scoped for returns 403 Forbidden. Missing or invalid keys return 401 Unauthorized.

Errors & status codes

  • 202 Accepted — a send was queued.
  • 200 OK — a read/list/verify succeeded.
  • 400 Bad Request — validation failed (e.g. unverified from-domain).
  • 402 Payment Required — free tier exhausted with no payment method, or spend cap reached.
  • 403 Forbidden — missing scope or no active organization.
  • 404 Not Found — resource doesn't exist in your org.
  • 409 Conflict — duplicate (e.g. domain or key name already exists).

Endpoints

MethodPathDescription
POST/v1/emailsQueue a single email.
POST/v1/emails/batchQueue up to 100 emails.
GET/v1/emails/{id}Get a message and its status.
GET/v1/domainsList sending domains.
GET/v1/domains/{id}Get one domain with DNS records.
POST/v1/domainsRegister a sending domain.
POST/v1/domains/{id}/verifyRe-check DNS and verify.
DELETE/v1/domains/{id}Delete a domain.
GET/v1/api-keysList API keys (metadata only).
POST/v1/api-keysCreate a key (full secret shown once).
DELETE/v1/api-keys/{id}Revoke a key.
GET/v1/billing/usageCurrent-period usage & billing.
POST/v1/webhooksManage event webhooks (planned).

POST /v1/emails

Queue a single email. Requires the emails:send scope. Returns 202.

Request body

{
  "from": "hello@yourdomain.com",   // required, verified domain
  "fromName": "Acme",                // optional
  "to": "user@example.com",          // required
  "cc": null, "bcc": null, "replyTo": null,
  "subject": "Welcome",              // required
  "html": "<p>Hi</p>",               // html and/or text required
  "text": "Hi",
  "templateId": null,                // optional template id
  "variables": { "name": "Sam" },    // optional substitutions
  "tags": "welcome"                  // optional, comma-separated
}

Response 202

{ "id": "8f3a1c2e-…", "status": "queued" }

POST /v1/emails/batch

Queue up to 100 emails. Body is { "emails": [ EmailObject, … ] }. Each item is validated independently. Returns 202.

{
  "accepted": 1,
  "rejected": 1,
  "results": [
    { "index": 0, "id": "…", "status": "queued" },
    { "index": 1, "status": "rejected", "error": "Recipient is suppressed." }
  ]
}

GET /v1/emails/{id}

Fetch a message and its current status, scoped to your org.

{
  "id": "8f3a1c2e-…",
  "fromEmail": "hi@you.com", "fromName": "Acme",
  "toEmail": "user@example.com",
  "cc": null, "bcc": null, "replyTo": null,
  "subject": "Welcome",
  "status": "Sent",
  "providerMessageId": "0102018f…",
  "errorMessage": null,
  "tags": "welcome",
  "queuedAt": "2026-06-18T10:00:00Z",
  "sentAt": "2026-06-18T10:00:02Z",
  "createdAt": "2026-06-18T10:00:00Z"
}

GET /v1/domains

List all sending domains for your organization, each with its DNS records and status.

GET /v1/domains/{id}

Fetch a single domain.

POST /v1/domains

Register a sending domain. Body:

{ "domain": "yourdomain.com", "mailFromSubdomain": "mail" }

Response — a Pending domain with the DNS records to publish:

{
  "id": "3b1f…",
  "domain": "yourdomain.com",
  "status": "Pending",
  "customMailFromDomain": "mail.yourdomain.com",
  "dnsRecords": [
    { "type": "CNAME", "name": "…_domainkey.yourdomain.com",
      "value": "….dkim.amazonses.com", "ttl": 1800, "purpose": "DKIM" }
  ],
  "createdAt": "2026-06-18T10:00:00Z"
}

POST /v1/domains/{id}/verify

Re-check DNS for the domain. Flips status to Verified once all records resolve.

DELETE /v1/domains/{id}

Remove a sending domain from your organization.

GET /v1/api-keys

List your keys. The secret is never returned — only a masked hint:

[
  {
    "id": "…", "name": "Production",
    "keyPrefix": "abc12xyz",
    "maskedKey": "ms_live_abc12xyz_••••",
    "scopes": ["emails:send"],
    "lastUsedAt": "2026-06-18T09:55:00Z",
    "expiresAt": null, "revokedAt": null,
    "isActive": true,
    "createdAt": "2026-04-02T12:00:00Z"
  }
]

POST /v1/api-keys

Create a key. Body:

{ "name": "Production", "scopes": ["emails:send"], "expiresAt": null }

Response — the metadata plus the full key, shown once:

{
  "key": { "id": "…", "name": "Production", "keyPrefix": "abc12xyz", … },
  "fullKey": "ms_live_abc12xyz_ZmFrZXNlY3JldA"
}

DELETE /v1/api-keys/{id}

Revoke a key immediately. Revoked keys can't be reactivated.

GET /v1/billing/usage

Read the current period's usage and billing state for your org:

{
  "periodKey": "2026-06",
  "periodStart": "2026-06-01T00:00:00Z",
  "periodEnd": "2026-07-01T00:00:00Z",
  "emailsSent": 4200,
  "freeMonthlyAllowance": 3000,
  "freeRemaining": 0,
  "billableEmails": 1200,
  "pricePer1kCents": 40,
  "hasPaymentMethod": true,
  "monthlySpendCapCents": 5000
}

POST /v1/webhooks (planned)

Manage event webhook subscriptions. See the dedicated webhooks guide for event types, payloads, and signature verification.