Documentation
Send your first email.
Everything here is the current API. MAILTRUSS_URL is the address of your Mailtruss deployment; MAILTRUSS_API_KEY is a key from the dashboard.
Quickstart
- 1Create an account and an organization.
- 2Add a sending domain under Domains and publish the five DNS records it shows.
- 3Create a key under API keys. It is shown once.
- 4Send:
curl -X POST "$MAILTRUSS_URL/api/v1/emails" \
-H "Authorization: Bearer $MAILTRUSS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@mail.acme.dev>",
"to": "ana@fabrikam.com",
"subject": "Welcome to Acme",
"html": "<p>Thanks for signing up.</p>"
}'The response is { "id": "…" }. Open the email in the dashboard to see its delivery trace, or fetch it with GET /api/v1/emails/:id.
Libraries and AI assistants
Official clients cover every endpoint below. They have no dependencies.
npm install mailtruss
import { MailClient } from "mailtruss";
const mail = new MailClient({ apiKey: process.env.MAILTRUSS_API_KEY! });
await mail.emails.send({ from: "you@yourdomain.com", to: "them@example.com", subject: "Hello", html: "<p>Hi</p>" });pip install mailtruss
from mailtruss import Mailtruss
m = Mailtruss(api_key="ms_live_...")
m.emails.send(from_="you@yourdomain.com", to="them@example.com", subject="Hello", html="<p>Hi</p>")To let an AI assistant (Claude, Cursor and other MCP clients) send mail and manage your account, add the MCP server with your API key. Set MAILTRUSS_MCP_READONLY=1 to allow reading only.
{ "mcpServers": { "mailtruss": { "command": "npx", "args": ["-y", "mailtruss-mcp"], "env": { "MAILTRUSS_API_KEY": "ms_live_..." } } } }Authentication
Send the key as a bearer token: Authorization: Bearer ms_live_…. Keys are either full access or sending only, which can call only the send endpoints. Keys are stored as a one-way hash and cannot be shown again.
Sending
Fields: from, to, subject, html and/or text, plus optional cc, bcc, reply_to (each up to 50 addresses), headers, tags, attachments (base64 content), scheduled_at, and template ({ "id", "variables" }). The from domain must be verified.
Idempotency
Add an Idempotency-Key header to make a send safe to retry. Repeating a request with the same key and body returns the first response instead of sending again. Reusing a key with a different body returns 409 invalid_idempotent_request.
Idempotency-Key: welcome-user-4812Scheduling
Set scheduled_at to an ISO 8601 time up to 30 days ahead. Until it starts sending, reschedule with PATCH /api/v1/emails/:id or cancel with POST /api/v1/emails/:id/cancel.
Batches
POST /api/v1/emails/batch takes an array of up to 100 email objects and returns { "data": [{ "id" }, …] }.
Checking what happened
GET /api/v1/emails/:id returns the email with last_event: one of queued, scheduled, sent, delivered, delivery_delayed, bounced, complained, failed and others. The full trace, every recorded event and the diagnosis are in the dashboard. Webhooks can push the same events to your endpoint, signed with your endpoint’s secret.
Domains
POST /api/v1/domains with { "name": "mail.acme.dev" } returns the records to publish: three DKIM CNAMEs, and an MX and SPF TXT record for the return path. Call POST /api/v1/domains/:id/verify to re-check. Each group is reported as pending, verified or failed; DNS changes can take minutes to hours.
Endpoint reference
Sending endpoints work with both key types; everything else needs a full-access key. The same list is available as an OpenAPI 3.1 document you can load into any client generator.
| Method | Path | Does |
|---|---|---|
| POST | /api/v1/emails | Send one email |
| POST | /api/v1/emails/batch | Send up to 100 emails |
| GET | /api/v1/emails | List recent emails (limit, after) |
| GET | /api/v1/emails/:id | Retrieve an email and its last event |
| PATCH | /api/v1/emails/:id | Reschedule a scheduled email |
| POST | /api/v1/emails/:id/cancel | Cancel a scheduled email |
| GET | /api/v1/domains | List domains |
| POST | /api/v1/domains | Add a domain and get its DNS records |
| POST | /api/v1/domains/:id/verify | Re-check a domain's verification |
| GET | /api/v1/api-keys | List API keys |
| POST | /api/v1/api-keys | Create an API key (shown once) |
| DELETE | /api/v1/api-keys/:id | Revoke an API key |
| GET | /api/v1/domains/:id | Retrieve a domain and its DNS records |
| DELETE | /api/v1/domains/:id | Delete a domain |
| POST | /api/v1/contacts | Create or update a contact by email |
| GET | /api/v1/contacts | List contacts (q, tag, status, page, limit) |
| GET | /api/v1/contacts/:id | Retrieve a contact by id or email |
| PATCH | /api/v1/contacts/:id | Update a contact |
| DELETE | /api/v1/contacts/:id | Delete a contact |
| GET | /api/v1/segments | List segments |
| POST | /api/v1/segments | Create a segment from rules |
| GET | /api/v1/segments/:id | Retrieve a segment with its size |
| DELETE | /api/v1/segments/:id | Delete a segment |
| GET | /api/v1/topics | List topics |
| POST | /api/v1/topics | Create a topic |
| GET | /api/v1/templates | List templates |
| POST | /api/v1/templates | Create a template (optionally publish) |
| GET | /api/v1/templates/:id | Retrieve a template |
| PATCH | /api/v1/templates/:id | Save a new, unpublished version |
| POST | /api/v1/templates/:id/publish | Publish a version |
| DELETE | /api/v1/templates/:id | Delete a template |
| GET | /api/v1/broadcasts | List broadcasts |
| POST | /api/v1/broadcasts | Create a draft broadcast |
| GET | /api/v1/broadcasts/:id | Retrieve a broadcast with delivery stats |
| POST | /api/v1/broadcasts/:id/send | Send now or schedule |
| POST | /api/v1/broadcasts/:id/cancel | Cancel a scheduled or sending broadcast |
| DELETE | /api/v1/broadcasts/:id | Delete a draft |
| GET | /api/v1/suppressions | List suppressed addresses |
| POST | /api/v1/suppressions | Suppress an address |
| DELETE | /api/v1/suppressions/:id | Remove a bounce or manual entry |
| GET | /api/v1/webhooks | List webhook endpoints |
| POST | /api/v1/webhooks | Create an endpoint (signing secret shown once) |
| PATCH | /api/v1/webhooks/:id | Update or pause an endpoint |
| POST | /api/v1/webhooks/:id/rotate-secret | Rotate the signing secret (old one works 24 hours) |
| DELETE | /api/v1/webhooks/:id | Delete an endpoint |
| GET | /api/v1/logs | List API request logs |
Errors
Every error has the same shape, and every response has an x-request-id header you can search in API logs.
{ "name": "validation_error", "message": "subject: Missing `subject` field.", "statusCode": 422 }- 403invalid_api_key
- The key does not exist, was revoked, or has expired. Create a new one under API keys.
- 401missing_api_key
- Send the key as Authorization: Bearer <key>.
- 401restricted_api_key
- This key is sending-only. Use a full-access key for this endpoint.
- 403domain_not_verified
- The from domain is not verified. Open Domains, publish the DNS records and re-check.
- 422validation_error
- Fix the field named in the message. The request was rejected before processing; no quota was used.
- 429rate_limit_exceeded
- Back off and retry after the Retry-After header. Use /emails/batch to send many messages in one request.
- 429daily_quota_exceeded
- The daily cap was reached. It resets at 00:00 UTC.
- 429monthly_quota_exceeded
- The plan's monthly limit was reached.
- 409invalid_idempotent_request
- That Idempotency-Key was already used with a different body. Use a new key for a new request.
- 409concurrent_idempotent_requests
- Another request with this key is still processing. Wait a moment and retry with the same key.
- 422invalid_scheduled_at
- Use an ISO 8601 date-time in the future, at most 30 days ahead.
- 404not_found
- No such resource in this organization.
- 403account_suspended
- This organization is suspended. Contact support.