Mail.tdPremium

API REFERENCE

Email infrastructure built for receiving.

Built for developers and enterprises receiving email at scale.

Base URL https://api.mail.tdJSON over HTTPSOpenAPI 3.0.3

Quick start

Use an API token from the Pro dashboard. The domain in the example is a placeholder—select one returned by GET /api/domains.

curl
# Base URL: https://api.mail.td
TOKEN=td_xxxxxxxxxxxxxxxxxxxx

# 1. Choose an available receiving domain
curl https://api.mail.td/api/domains

# 2. Create an inbox
curl -X POST https://api.mail.td/api/accounts \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"address":"build-42@example.com","password":"use-a-long-password"}'

# 3. Read its messages (UUID or full email address both work)
curl "https://api.mail.td/api/accounts/build-42@example.com/messages?page=1" \
  -H "Authorization: Bearer $TOKEN"
Path identifiers

{account_id} accepts either the mailbox UUID or its full email address. URL-encode addresses when required by your HTTP client.

Authentication

External integrations use one credential: a Pro API token. Send it in every protected request. Tokens start with td_, do not expire automatically and can be revoked from the dashboard or API.

HTTP
Authorization: Bearer td_xxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Public exception

GET /api/domains is public so clients can discover available receiving domains before creating an inbox.

API token

Every other endpoint on this page uses a long-lived td_ token scoped to its registered user and owned resources.

Domains & mailboxes

Create and manage receive-only addresses. Passwords are never returned; save them when created.

GET/api/domainsPublic

List system domains available for new inboxes.

Returns200 · { domains: [{ id, domain, is_active, pro_only }] }
POST/api/accountsAPI token

Create an inbox on an available system domain or a verified custom domain. The returned mailbox token is separate from your API token.

Input{ address, password }
Returns201 · { id, address, token }
GET/api/accounts/{account_id}API token

Get address, role, storage quota and usage.

Returns200 · { id, address, role, quota, used, created_at }
DELETE/api/accounts/{account_id}API token

Permanently delete an inbox and all of its messages.

Returns204 · No Content
PUT/api/accounts/{account_id}/reset-passwordAPI token

Replace mailbox credentials and invalidate its existing mailbox tokens.

Input{ password } or { auth_key }
Returns200 · { message: "password_reset" }

Messages

Lists are paginated at 30 messages. A page with fewer than 30 items is the final page. Message detail contains sanitized display fields plus attachment metadata.

GET/api/accounts/{account_id}/messages?page=1API token

List messages newest first, 30 per page.

Returns200 · { messages: MessageSummary[], page }
DELETE/api/accounts/{account_id}/messagesAPI token

Delete all messages currently visible in the inbox.

Returns200 · { deleted, reclaimed_bytes }
PUT/api/accounts/{account_id}/messages/readAPI token

Mark up to 200 message IDs, or every message, as read.

Input{ ids: ["..."] } or { all: true }
Returns200 · { updated }
GET/api/accounts/{account_id}/messages/{id}API token

Read bodies, headers and attachment metadata.

Returns200 · Message
DELETE/api/accounts/{account_id}/messages/{id}API token

Delete one message and reclaim its storage.

Returns204 · No Content
GET/api/accounts/{account_id}/messages/{id}/sourceAPI token

Download the original RFC 822 message.

Returns200 · message/rfc822 (.eml)
PUT/api/accounts/{account_id}/messages/{id}/readAPI token

Mark one message as read. Idempotent.

Returns204 · No Content
GET/api/accounts/{account_id}/messages/{id}/attachments/{index}API token

Download an attachment using its zero-based index from message detail.

Returns200 · Binary body with Content-Type and Content-Disposition
Message response
{
  "id": "f5e6d7c8-...",
  "sender": "noreply@example.com",
  "from": "Example <noreply@example.com>",
  "subject": "Verify your email",
  "address": "build-42@example.com",
  "text_body": "Click the link below to verify...",
  "html_body": "<html>...</html>",
  "size": 4523,
  "is_read": false,
  "attachments": [
    { "index": 0, "filename": "result.pdf", "content_type": "application/pdf", "size": 12345 }
  ],
  "created_at": "2026-08-03T10:30:00Z"
}

User management

These endpoints operate across resources owned by the registered user.

GET/api/user/meAPI token

Get user, plan, status, limits and monthly operation usage.

Returns200 · ProUser
GET/api/user/accounts?cursor=&q=API token

List owned inboxes. Use cursor for the next page and q to search.

Returns200 · { accounts, next_cursor }
DELETE/api/user/accounts/{id}API token

Delete an owned inbox and its messages.

Returns204 · No Content
PUT/api/user/accounts/{id}/reset-passwordAPI token

Reset an owned inbox password.

Input{ password } or { auth_key }
Returns200 · { message: "password_reset" }
GET/api/user/accounts/{id}/messages?page=1API token

List messages for an owned inbox.

Returns200 · { messages, page }

Custom domains

Add the DNS record returned by the create call, then request verification. Only verified domains can receive mail.

GET/api/user/domainsAPI token

List custom domains and their verification state.

Returns200 · { domains: CustomDomain[] }
POST/api/user/domainsAPI token

Add a receiving domain and receive the required DNS records.

Input{ domain: "mail.example.com" }
Returns201 · { id, domain, verify_token, dns_records }
POST/api/user/domains/{id}/verifyAPI token

Check the required TXT and MX records and activate a correctly configured domain.

Returns200 · { verify_status, txt_record?, mx_record?, dns_records?, message? }
DELETE/api/user/domains/{id}API token

Remove a custom domain. Delete every inbox on the domain first or the API returns 409 domain_has_accounts.

Returns204 · No Content

API tokens

GET/api/user/tokensAPI token

List active token metadata. Secret token values are never returned again.

Returns200 · { tokens: [{ id, name, last_used_at, created_at, revoked_at }] }
POST/api/user/tokensAPI token

Create a long-lived API token. Store the returned td_ token immediately.

Input{ name: "CI inboxes" }
Returns201 · { id, name, token }
DELETE/api/user/tokens/{id}API token

Revoke a token immediately.

Returns204 · No Content

Webhooks

GET/api/user/webhooksAPI token

List webhook endpoints and delivery health.

Returns200 · { webhooks: Webhook[] }
POST/api/user/webhooksAPI token

Register an HTTPS endpoint for email.received. The signing secret is returned once.

Input{ url, events: ["email.received"] }
Returns201 · { id, url, events, secret, status, created_at }
DELETE/api/user/webhooks/{id}API token

Delete a webhook.

Returns204 · No Content
POST/api/user/webhooks/{id}/rotateAPI token

Rotate the signing secret; the previous secret stops working immediately.

Returns200 · { id, secret }
GET/api/user/webhooks/{id}/deliveriesAPI token

Inspect recent attempts, status codes, latency and errors.

Returns200 · { deliveries: WebhookDelivery[] }

Official SDKs

All three clients use the same td_ API token and cover accounts, messages, custom domains, webhooks, tokens and user management with typed errors.

Node.js / TypeScript

npm install mailtd

Zero dependencies, native fetch and full TypeScript definitions.

Python

pip install mailtd

httpx-based, dataclass models and context-manager support.

Go

go get github.com/mailtd/mailtd-go

Standard net/http, context support and typed structs.

Node.js / TypeScript
import { MailTD } from "mailtd";

const client = new MailTD("td_...");
const account = await client.accounts.create("test@example.com", {
  password: "use-a-long-password"
});
const { messages } = await client.messages.list(account.id);
const message = await client.messages.get(account.id, messages[0].id);
console.log(message.subject, message.text_body);
Python
from mailtd import MailTD

client = MailTD("td_...")
account = client.accounts.create(
    "test@example.com", password="use-a-long-password"
)
messages, page = client.messages.list(account.id)
message = client.messages.get(account.id, messages[0].id)
print(message.subject, message.text_body)
Go
client := mailtd.NewClient("td_...")
ctx := context.Background()
password := "use-a-long-password"

account, err := client.Accounts.Create(ctx, "test@example.com", &mailtd.CreateOptions{
    Password: &password,
})
if err != nil { log.Fatal(err) }
result, err := client.Messages.List(ctx, account.ID, nil)
if err != nil { log.Fatal(err) }
message, err := client.Messages.Get(ctx, account.ID, result.Data[0].ID)

Real-time delivery with webhooks

External API integrations receive new-message events through webhooks. Register an HTTPS URL, respond with a 2xx within 5 seconds, and verify every signature against the raw request body. Failed deliveries are retried after 15 seconds, 1 minute, 5 minutes, 10 minutes and 20 minutes. A webhook is marked failing after five consecutive events each exhaust all delivery attempts; it then stops receiving new events.

Webhook signature
X-Webhook-ID: evt_...
X-Webhook-Timestamp: 1785740000
X-Webhook-Signature: sha256=<hex>

signed_payload = timestamp + "." + raw_request_body
expected = HMAC-SHA256(webhook_secret, signed_payload)

The flat data object includes complete text and HTML bodies for normal-size messages when content_status is ready. A status of failed or pending_timeout is an explicit metadata-only fallback. Attachments contain metadata and authenticated download paths, never attachment bytes or internal storage IDs. Events are capped at 1,000,000 bytes; check content_truncated and attachments_omitted. Delivery is at least once, so deduplicate with the stable event id.

email.received
{
  "id": "evt_f5e6d7c8-...", "type": "email.received", "created_at": "...",
  "data": { "email_id": "f5e6d7c8-...", "account_id": "a1b2c3d4-...",
    "address": "build-42@example.com", "from": "Example <noreply@example.com>",
    "to": "build-42@example.com", "sender": "noreply@example.com",
    "subject": "Verify your email", "preview_text": "Click the link...",
    "text_body": "Click the link below...", "html_body": "<p>Click the link below...</p>",
    "attachments": [{ "index": 0, "filename": "result.pdf",
      "content_type": "application/pdf", "size": 12345,
      "download_url": "/api/accounts/a1b2c3d4-.../messages/f5e6d7c8-.../attachments/0" }],
    "content_status": "ready", "content_truncated": false,
    "attachments_omitted": 0, "size": 4523, "created_at": "..." }
}

Errors, limits and operational rules

Status codes

400
Invalid input
401
Missing, invalid or revoked token
403
Plan, ownership or PoW rejection
404
Resource not found or not owned
409
Address or resource conflict
410
Message expired
429
Rate or operation limit
503
Temporary dependency failure

Rate limits

  • Mailbox creation: 1 request per second per authenticated user or anonymous IP.
  • Other API routes: 10 requests per second per authenticated user or anonymous IP.
  • Registered users also have a monthly operations quota shown by GET /api/user/me.
  • Back off after 429; use webhooks instead of aggressive polling.
  • Never expose a td_ token or webhook secret in browser code.
Error envelope
{ "error": "invalid_or_expired_token", "message": "Optional human-readable detail" }