TrekMail REST API Overview for Developers

Learn how the TrekMail REST API works, including authentication with bearer tokens, plan-based access, rate limits, and response formats.

Article details

Type, difficulty, plans, and last updated info.

Type
Reference
Difficulty
Intermediate
Plans
Starter · Pro · Agency
Last updated
Jun 20, 2026

The TrekMail API lets you manage domains, mailboxes, forwarding, DNS, email migrations, and full webmail operations — read, send, draft, schedule, folder management, contacts, calendar, identities, templates, and blocked senders — from any HTTP client or AI agent. Every request uses a bearer token, every response is JSON, and every action is audited.

What you get

  • REST API v1 with JSON request/response format.
  • Bearer token authentication — no cookies, no sessions.
  • Idempotency keys on mutating requests to prevent duplicate operations.
  • Per-token rate limiting with Retry-After headers.
  • Audit log visible in your dashboard under AI Agents & API → Audit Log.
  • MCP server with 216 tools, including 42 Drive tools for files, folders, uploads, share links, bulk operations, sync-device password management, and read-only Drive Add-on status, plus 7 per-domain White Label branding tools.
  • Dual-token architecture — separate ops tokens for infrastructure and message tokens for full email operations (read, send, draft, schedule, contacts, calendar, identities, templates, folders, and more).
  • Outbound deliverability + bounce insights — pull the same sent/delivered/hard/soft rollup your dashboard shows, plus per-recipient hard/soft bounces with the receiver's SMTP status code and response. See Deliverability and Bounces.
  • Mailbox storage usagelist_mailboxes and get_mailbox now return used_mb, quota_mb, allocation_mb, and is_pooled, so an agent can spot mailboxes approaching their limit without dashboard access.
  • Per-domain White Label branding — configure a domain's brand identity, logos, and branded dashboard/webmail hosts, read back the CNAME records to create, and verify DNS, entirely over the API or MCP. See White Label Branding API and MCP Guide.

Drive API and file automation

Drive is now part of the public API surface. The Drive API exposes 43 REST endpoints for Account Drive and mailbox Drive: spaces, usage, folder browsing, uploads, file and folder management, Trash, bulk actions, public share links, sync-device password management (rclone / Cyberduck / X-Plore / DAVx⁵ / Documents / FolderSync), and read-only Drive Storage Add-on status.

Drive uses eleven ops-token scopes: drive:account:read, drive:account:write, drive:account:share, drive:account:purge, drive:mailbox:read, drive:mailbox:write, drive:mailbox:share, drive:mailbox:purge, drive:addon:read, drive:devices:read, and drive:devices:write. Billing actions for the Drive Add-on — purchase, resize, and cancel — remain dashboard-only and are not exposed as API or MCP write operations.

Start with Drive API Overview or the Drive API Quickstart.

Dual-token architecture

The API uses two independent token types. You can use one or both depending on your needs:

Token type Prefix What it unlocks
Ops token tm_live_ Infrastructure tools — domains, DNS, mailboxes, invites, forwarding, delete intents, migrations, SMTP, tickets, account, billing, Cloudflare
Message token tm_msg_ Full webmail API — messages, folders, attachments, drafts, scheduled send, spam/ham reporting, bulk actions, contacts, contact groups, calendar, compose helpers (reply/forward), identities, templates, blocked senders

Ops tokens and message tokens have separate scopes and separate rate limits. A single agent can use both tokens simultaneously by configuring them in the MCP server environment.

Message tokens are available on Pro and Agency plans.

Before you start

  • All plans have API access:
    • Nano — Email Verifier. Add a Drive Storage Add-on and the full Drive API + 42 MCP tools come with it.
    • Starter — Full Drive, full Email Verifier, and read-only across the rest (domains, mailboxes, migrations, tickets, etc.). Writes for those run from the dashboard.
    • Pro / Agency — Everything: read, write, create, delete across every family, plus message tokens for email read/send.
  • Connecting an AI agent (Claude / Cursor / Windsurf / etc.)? Open claude.ai/customize/connectors, click +Add custom connector, and paste https://trekmail.net/mcp. Your browser handles OAuth — no manual token to create. See Connecting AI Agents (MCP) for the full path list (Claude.ai, Claude Desktop, Claude Code, mcp-remote, self-hosted stdio).
  • Writing your own integration? Create a tm_live_ token under AI Agents & API → Tokens → Create token and send it as Authorization: Bearer …. See Creating and Managing API Tokens.
  • New to the API? Click the Guide button at the top of the AI Agents & API page for an interactive walkthrough of the dashboard — it covers connection methods, token management, and the audit log in about 60 seconds.

How authentication works

Every request must include your token in the Authorization header:

Authorization: Bearer tm_live_abc123...

Ops tokens start with tm_live_ and message tokens start with tm_msg_. Both are shown once at creation. TrekMail stores only the SHA-256 hash — the plaintext cannot be recovered.

If the token is missing, revoked, or expired, the API returns 401 with an unauthenticated error code.

Base URL and versioning

All endpoints live under:

https://trekmail.net/api/v1

The base URL is shown on your AI Agents & API dashboard under Quick Reference. The version is in the URL path. When a v2 is introduced (if ever), v1 will continue to work.

Response format

Successful responses return JSON with a data key for single resources or a paginated list:

{
  "data": [
    { "id": 1, "domain": "example.com", "status": "active" }
  ],
  "links": { "next": "...", "prev": null },
  "meta": { "current_page": 1, "last_page": 1, "total": 1 }
}

Error responses follow a consistent structure:

{
  "error": {
    "code": "unauthenticated",
    "message": "Invalid or expired API token",
    "hint": "Check that your token is correct and has not been revoked.",
    "request_id": "req_abc123",
    "retryable": false
  }
}

Request IDs

Every response includes an X-Request-Id header. You can also pass your own via X-Request-Id in the request — it will be echoed back and logged in the audit trail.

Rate limits

Each token is rate-limited per minute. When you hit the limit, the API returns 429 with a Retry-After header indicating when you can retry.

Destructive operations (delete intents) have an additional daily limit per token and a cooldown between consecutive deletes.

Migration write operations (start, cancel, retry) have a dedicated rate limit of 10 requests per minute per token, plus a server-wide concurrency cap that returns 503 when too many migrations are running globally.

Idempotency

All POST and PUT requests require an Idempotency-Key header. If you send the same key with the same body, the API replays the original response without creating duplicates.

Idempotency-Key: create-mailbox-alice-2024

If you send the same key with a different body, the API returns 409 Conflict.

Mailbox storage allocation

Every endpoint that creates a mailbox or invite — POST /api/v1/mailboxes, /api/v1/mailboxes:bulk, /api/v1/mailboxes/invites, /api/v1/mailboxes/invites:bulk — accepts an optional storage_allocation_mb integer.

Value Meaning
Omitted (or null) Mailbox uses the shared account pool (default).
Positive integer (MB) Mailbox is dedicated — that exact amount is carved out of the account pool just for this mailbox.

Allocations are validated against the live pool minus existing dedicated mailboxes and pending dedicated invites. Bulk endpoints additionally validate the sum of allocations across the batch and reject the entire batch with 422 storage_pool_exceeded if it would over-commit. The pool refreshes when a dedicated mailbox is deleted, when an invite is redeemed (allocation moves to the new mailbox), and when a pending invite expires.

For invites the allocation is recorded on the access code and copied onto the new mailbox at redeem time. If the pool no longer fits the requested allocation at redeem time (e.g. another admin grew their dedicated allocation in the interim), the redeem gracefully downgrades the new mailbox to shared rather than failing — the recipient sees a notice on the success page.

Shared (team) mailboxes

A shared mailbox is a team inbox such as support@ or sales@ that several of your other mailboxes open from inside their own webmail — no shared password and no separate login. Access is flat: every member can read, and a single can_send flag controls whether that member can reply as the address (true) or is read-only (false). There are no member roles.

GET /api/v1/mailboxes and GET /api/v1/mailboxes/{id} now return mailbox_type ("user" or "shared") and a boolean is_shared; shared mailboxes also include shared_member_count. Use those fields to tell a team inbox apart from a normal one before calling the member endpoints.

Endpoint Method Scope Required What it does
/api/v1/mailboxes/{id}/members GET mailboxes:read List members of a shared mailbox (each: member_mailbox_id, email, can_read, can_send)
/api/v1/mailboxes/{id}/members POST mailboxes:write Add a member — body {member_mailbox_id, can_send?} (can_send defaults to true)
/api/v1/mailboxes/{id}/members/{member} PATCH mailboxes:write Toggle a member's reply access — body {can_send}
/api/v1/mailboxes/{id}/members/{member} DELETE mailboxes:write Remove a member (a shared mailbox always keeps at least one)
/api/v1/shared-mailboxes POST mailboxes:write Create a shared mailbox — body {domain_id, local_part, display_name, member_mailbox_ids[], storage_shared?, storage_mb?}
/api/v1/mailboxes/{id}/convert-to-shared POST mailboxes:write Convert an existing mailbox into a shared one — body {member_mailbox_ids[]} (rotates the old password so it can no longer sign in)
/api/v1/mailboxes/{id}/convert-to-regular POST mailboxes:write Convert a shared mailbox back to a regular one — body {password} (removes members and sets a fresh sign-in password)

The member endpoints reuse your existing mailboxes:read / mailboxes:write scopes — there is no separate shared-mailbox scope.

Available endpoints

Endpoint Method Scope Required
/api/v1/domains GET domains:read
/api/v1/domains/{id} GET domains:read
/api/v1/domains/{id}/dns-requirements GET domains:dns:read
/api/v1/domains/{id}/dns-recheck POST domains:dns:recheck
/api/v1/domains/{id}/spam-metrics GET domains:read
/api/v1/domains/{id}/spam-metrics/summary GET domains:read
/api/v1/dns-checks/{id} GET domains:dns:read
/api/v1/mailboxes GET mailboxes:read
/api/v1/mailboxes POST mailboxes:create
/api/v1/mailboxes/{id} PATCH mailboxes:write
/api/v1/mailboxes/invites POST mailboxes:invites:create
/api/v1/mailboxes/invites:bulk POST mailboxes:invites:create
/api/v1/mailboxes:bulk POST mailboxes:create
/api/v1/mailboxes/{id}/forwarding GET mailboxes:forwarding:read
/api/v1/mailboxes/{id}/forwarding PUT mailboxes:forwarding:write
/api/v1/mailboxes/{id}/rules GET mailboxes:rules:read
/api/v1/mailboxes/{id}/rules POST mailboxes:rules:write
/api/v1/mailboxes/{id}/rules/{ruleId} GET mailboxes:rules:read
/api/v1/mailboxes/{id}/rules/{ruleId} PUT mailboxes:rules:write
/api/v1/mailboxes/{id}/rules/{ruleId} DELETE mailboxes:rules:write
/api/v1/mailboxes/{id}/rules/reorder PATCH mailboxes:rules:write
/api/v1/mailboxes/{id}/auto-reply GET mailboxes:auto-reply:read
/api/v1/mailboxes/{id}/auto-reply PUT mailboxes:auto-reply:write
/api/v1/mailboxes/{id}/sieve GET mailboxes:rules:read
/api/v1/mailboxes/{id}/sieve PUT mailboxes:rules:write
/api/v1/mailboxes/{id}:delete-intent POST mailboxes:delete
/api/v1/delete-intents/{id}:confirm POST mailboxes:delete
/api/v1/me GET (any valid ops token)
/api/v1/mailboxes/{id}/message-tokens POST mailboxes:message-tokens:manage
/api/v1/mailboxes/{id}/message-tokens GET mailboxes:message-tokens:manage
/api/v1/message-tokens/{id} DELETE mailboxes:message-tokens:manage
/api/v1/messages GET messages:read (message token)
/api/v1/messages/{uid} GET messages:read (message token)
/api/v1/messages/{uid} PATCH messages:write (message token)
/api/v1/messages/send POST messages:send (message token)
/api/v1/messages/_ping GET messages:read (message token, diagnostic)
/api/v1/messages/{uid}/attachments/{index} GET messages:read (message token)
/api/v1/messages/{uid}/attachments GET messages:read (message token)
/api/v1/messages/{uid}/raw GET messages:read (message token)
/api/v1/messages/folders POST messages:write (message token)
/api/v1/messages/folders/{path} PATCH messages:write (message token)
/api/v1/messages/folders/{path} DELETE messages:write (message token)
/api/v1/messages/{uid}:spam POST messages:write (message token)
/api/v1/messages/{uid}:ham POST messages:write (message token)
/api/v1/messages:bulk POST messages:write (message token)
/api/v1/messages/folders:empty POST messages:write (message token)
/api/v1/messages/drafts POST messages:write (message token)
/api/v1/messages/drafts/{uid} PUT messages:write (message token)
/api/v1/messages/scheduled POST messages:send (message token)
/api/v1/messages/scheduled GET messages:read (message token)
/api/v1/messages/scheduled/{id} PATCH messages:send (message token)
/api/v1/messages/scheduled/{id} DELETE messages:send (message token)
/api/v1/messages/contacts GET messages:read (message token)
/api/v1/messages/contacts POST messages:write (message token)
/api/v1/messages/contacts/{id} PATCH messages:write (message token)
/api/v1/messages/contacts/{id} DELETE messages:write (message token)
/api/v1/messages/contacts:import POST messages:write (message token)
/api/v1/messages/contacts:export GET messages:read (message token)
/api/v1/messages/calendar/events GET messages:read (message token)
/api/v1/messages/calendar/events POST messages:write (message token)
/api/v1/messages/calendar/events/{id} PATCH messages:write (message token)
/api/v1/messages/calendar/events/{id} DELETE messages:write (message token)
/api/v1/messages/{uid}/reply GET messages:read (message token)
/api/v1/messages/{uid}/reply-all GET messages:read (message token)
/api/v1/messages/{uid}/forward GET messages:read (message token)
/api/v1/messages/contact-groups POST messages:write (message token)
/api/v1/messages/contact-groups/{id} PATCH messages:write (message token)
/api/v1/messages/contact-groups/{id} DELETE messages:write (message token)
/api/v1/messages/contact-groups/{id}/members POST messages:write (message token)
/api/v1/messages/contact-groups/{id}/members DELETE messages:write (message token)
/api/v1/messages/identities GET messages:read (message token)
/api/v1/messages/identities POST messages:write (message token)
/api/v1/messages/identities/{id} PATCH messages:write (message token)
/api/v1/messages/identities/{id} DELETE messages:write (message token)
/api/v1/messages/templates GET messages:read (message token)
/api/v1/messages/templates POST messages:write (message token)
/api/v1/messages/templates/{id} PATCH messages:write (message token)
/api/v1/messages/templates/{id} DELETE messages:write (message token)
/api/v1/messages/blocked-senders GET messages:read (message token)
/api/v1/messages/blocked-senders POST messages:write (message token)
/api/v1/messages/blocked-senders/{id} DELETE messages:write (message token)
/api/v1/mailboxes/{id}/enable-imap POST mailboxes:write (ops token)
/api/v1/migrations/test-connection POST migrations:write
/api/v1/migrations GET migrations:read
/api/v1/migrations/{id} GET migrations:read
/api/v1/migrations POST migrations:write
/api/v1/migrations/{id}:cancel POST migrations:write
/api/v1/migrations/{id}:retry POST migrations:write
/api/v1/migrations/{id} DELETE migrations:write
/api/v1/migrations/bulk/preview POST migrations:write
/api/v1/migrations/bulk POST migrations:write
/api/v1/migrations/bulk GET migrations:read
/api/v1/migrations/bulk/{batch} GET migrations:read
/api/v1/migrations/bulk/{batch}:cancel POST migrations:write
/api/v1/migrations/bulk/{batch}:retry POST migrations:write
/api/v1/migrations/bulk/{batch}:resume POST migrations:write
/api/v1/migrations/bulk/{batch} DELETE migrations:write
/api/v1/migrations/bulk/{batch}/jobs/{job}/password PATCH migrations:write
/api/v1/account GET account:read
/api/v1/billing/status GET billing:read
/api/v1/billing/invoices GET billing:read
/api/v1/domains POST domains:create
/api/v1/domains/{id} DELETE domains:delete
/api/v1/domains/{id}/catch-all PATCH domains:write
/api/v1/domains/{id}/dkim:retry POST domains:write
/api/v1/domains/{id}/note PATCH domains:write
/api/v1/domains/{id}/branding GET domains:read
/api/v1/domains/{id}/branding PATCH domains:write
/api/v1/domains/{id}/branding/logo/{slot} PUT domains:write
/api/v1/domains/{id}/branding/logo/{slot} DELETE domains:write
/api/v1/domains/{id}/branding/verify-dns POST domains:write
/api/v1/domains/{id}/branding/preview POST domains:write
/api/v1/domains/{id}/branding DELETE domains:write
/api/v1/domains:bulk-add POST domains:create
/api/v1/mailboxes/{id} GET mailboxes:read
/api/v1/mailboxes/{id}/password POST mailboxes:write
/api/v1/mailboxes/{id}/note PATCH mailboxes:write
/api/v1/mailboxes/{id}:pause POST mailboxes:write
/api/v1/mailboxes/{id}:resume POST mailboxes:write
/api/v1/mailboxes/{id}/members GET mailboxes:read
/api/v1/mailboxes/{id}/members POST mailboxes:write
/api/v1/mailboxes/{id}/members/{member} PATCH mailboxes:write
/api/v1/mailboxes/{id}/members/{member} DELETE mailboxes:write
/api/v1/shared-mailboxes POST mailboxes:write
/api/v1/mailboxes/{id}/convert-to-shared POST mailboxes:write
/api/v1/mailboxes/{id}/convert-to-regular POST mailboxes:write
/api/v1/tickets GET tickets:read
/api/v1/tickets/{id} GET tickets:read
/api/v1/tickets/{id}/messages GET tickets:read
/api/v1/tickets POST tickets:write
/api/v1/tickets/{id}/reply POST tickets:write
/api/v1/tickets/{id}:close POST tickets:write
/api/v1/domains/{id}/smtp GET smtp:read
/api/v1/domains/{id}/smtp PUT smtp:write
/api/v1/domains/{id}/smtp/profiles GET smtp:read
/api/v1/domains/{id}/smtp/profiles POST smtp:write
/api/v1/domains/{id}/smtp/profiles/{connectionId} PUT smtp:write
/api/v1/domains/{id}/smtp/profiles/{connectionId} DELETE smtp:write
/api/v1/domains/{id}/smtp:test POST smtp:write
/api/v1/domains/{id}/smtp:test-status/{jobId} GET smtp:read
/api/v1/smtp/default GET smtp:read
/api/v1/smtp/default PUT smtp:write
/api/v1/smtp (legacy, back-compat) GET smtp:read
/api/v1/smtp (legacy, back-compat) PUT smtp:write
/api/v1/smtp/{id} (legacy, back-compat) DELETE smtp:write
/api/v1/smtp:test (legacy, back-compat) POST smtp:write
/api/v1/smtp:test-status/{jobId} (legacy, back-compat) GET smtp:read
/api/v1/messages/{uid} DELETE messages:write (message token)
/api/v1/messages/{uid}:move POST messages:write (message token)
/api/v1/messages/folders GET messages:read (message token)
/api/v1/aliases GET mailboxes:read
/api/v1/aliases POST mailboxes:write
/api/v1/aliases/{id} PATCH mailboxes:write
/api/v1/aliases/{id} DELETE mailboxes:write
/api/v1/verify POST verify:write
/api/v1/verify/bulk POST verify:write
/api/v1/verify/bulk/{jobId} GET verify:read
/api/v1/verify/bulk/{jobId}/download GET verify:read
/api/v1/verify/credits GET verify:read
/api/v1/verify/bulk GET verify:read
/api/v1/verify/bulk/{jobId}/cancel POST verify:write
/api/v1/verify/bulk/{jobId} DELETE verify:write
/api/v1/cloudflare/validate-token POST cloudflare:read
/api/v1/cloudflare/zones POST cloudflare:read
/api/v1/cloudflare/connect POST cloudflare:write
/api/v1/cloudflare/preview POST cloudflare:read
/api/v1/cloudflare/apply POST cloudflare:write
/api/v1/cloudflare/tokens GET cloudflare:read
/api/v1/cloudflare/tokens/{id} DELETE cloudflare:delete

The Cloudflare endpoints follow the same flow as the dashboard: validate a token, list zones, connect domains, preview the DNS changes, then apply them. Both /cloudflare/preview and /cloudflare/apply accept two optional per-domain controls:

  • included_records — an allowlist of which records to touch, keyed by domain ID: { "123": ["mx_primary", "spf_record"] }. Records you leave out are skipped, so you can apply only MX and SPF and come back for DKIM later. Omit the field to apply every record.
  • confirmed_conflicts — when preview flags a record that already exists with a different value, list its record ID here (same { domain_id: [record_ids] } shape) to authorise replacing it.

The record IDs (mx_primary, spf_record, dkim_primary, dmarc_main, …) come straight from the preview response, so a typical agent calls preview first and feeds the IDs it wants back into apply:

POST /api/v1/cloudflare/apply
{
  "domain_ids": [123],
  "included_records": { "123": ["mx_primary", "spf_record"] },
  "confirmed_conflicts": { "123": ["dmarc_main"] }
}

Per-domain SMTP routing and the account default

SMTP is configured per domain. Each domain picks one of three routes — managed platform sending, a saved SMTP profile (your own provider, reusable across domains), or "not configured" — and a single account-wide default decides which route new domains start on.

Per-domain endpoints (smtp:read / smtp:write):

Endpoint Method What it does
/api/v1/domains/{id}/smtp GET Current route: smtp_mode, effective_smtp_mode, profile, effective_profile
/api/v1/domains/{id}/smtp PUT Set the route — body {smtp_mode: platform|profile|not_configured|inherit, smtp_connection_id?, set_account_default?, apply_to_all?}
/api/v1/domains/{id}/smtp/profiles GET List the account's saved SMTP profiles
/api/v1/domains/{id}/smtp/profiles POST Create a profile and use it for this domain
/api/v1/domains/{id}/smtp/profiles/{connectionId} PUT Update a profile (affects every domain using it)
/api/v1/domains/{id}/smtp/profiles/{connectionId} DELETE Delete a profile (domains using it are reassigned to the account default)
/api/v1/domains/{id}/smtp:test POST Test a route — returns {job_id, poll_url}
/api/v1/domains/{id}/smtp:test-status/{jobId} GET Poll a test job

A few notes on the route body:

  • smtp_mode=platform selects managed sending; smtp_mode=profile requires smtp_connection_id; not_configured clears the route.
  • smtp_mode=inherit makes the domain live-follow the account default — whenever the default changes, this domain changes with it. The web UI always writes concrete routes, but the backend still supports inherit, which is why GET returns effective_smtp_mode showing what inherit currently resolves to.
  • set_account_default: true is the API equivalent of the dashboard's Make this the account default toggle (new domains start on this route). apply_to_all: true is the Apply to all domains button (a one-time switch of every domain to this route).

Account-wide default endpoints (smtp:read / smtp:write):

Endpoint Method What it does
/api/v1/smtp/default GET Returns default_smtp_mode (null until you set one), effective_default_smtp_mode (the plan baseline used when unset), default_smtp_connection_id, and profile
/api/v1/smtp/default PUT Set the default — body {smtp_mode: platform|profile|not_configured, smtp_connection_id?, apply_to_all?}

Deleting a profile that was the account default resets the default to the plan baseline.

Legacy endpoints. The account-level GET/PUT /api/v1/smtp (and DELETE /api/v1/smtp/{id}, POST /api/v1/smtp:test, GET /api/v1/smtp:test-status/{jobId}) remain for backward compatibility but no longer control per-domain routing — use the per-domain and /smtp/default endpoints above. The legacy MCP tools get_smtp_config / update_smtp_config are deprecated for the same reason.

Per-domain White Label branding

Branding is configured per domain (domains:read / domains:write). A domain runs its own brand (mode=custom), inherits the account default (mode=inherit), or is off. Settings always save; the branded dashboard.yourbrand.com / mail.yourbrand.com hosts only go live once the White Label add-on is active (without it they stay draft). The branded hosts point a CNAME at wl.trekmail.net.

Endpoint Method What it does
/api/v1/domains/{id}/branding GET Read branding — mode, white_label_addon_active, brand, hosts, the dns_records to create, and cname_target
/api/v1/domains/{id}/branding PATCH Partial-merge update — mode, name, primary_color/accent_color, dashboard_enabled/dashboard_label, webmail_enabled/webmail_label, support_email, support_url, sender_email, scope
/api/v1/domains/{id}/branding/logo/{slot} PUT Upload a base64 logo (slot = light|dark|favicon; PNG/JPG, ICO for favicon, ≤1 MB, no SVG)
/api/v1/domains/{id}/branding/logo/{slot} DELETE Remove a logo slot
/api/v1/domains/{id}/branding/verify-dns POST Queue DNS verification (422 white_label_inactive without the add-on)
/api/v1/domains/{id}/branding/preview POST Mint a short-lived preview URL (422 preview_disabled or no_brand)
/api/v1/domains/{id}/branding?scope=domain|all DELETE Clear branding for this domain or the whole account

PATCH is a partial merge — omitted fields are preserved. If branding is currently off you must pass mode to re-enable it, and a custom sender_email must be on a domain with a verified DKIM key. The full walkthrough — including the autonomous "set brand → read CNAMEs → apply_cloudflare_dns → verify → poll" agent flow — is in the White Label Branding API and MCP Guide.

Internal support endpoints (HMAC auth)

These endpoints use HMAC-SHA256 authentication (not bearer tokens) and are used by the AI support pipeline:

Endpoint Method Auth
/api/v1/support/reply POST HMAC
/api/v1/support/messages/{id} PUT HMAC
/api/v1/support/messages/{id} DELETE HMAC

The OpenAPI specification is available at /api/openapi.json for import into Postman, Insomnia, or code generators.

Quick fixes

  • 401 "unauthenticated": Check that the Authorization: Bearer <token> header is present and the token has not been revoked or expired.
  • 403 "plan_api_disabled": The requested scope isn't on your plan. Nano covers Email Verifier (and Drive if you've bought the Drive Storage Add-on). Upgrade to Starter or higher for the rest of the API.
  • 403 "token_scope_blocked_by_plan": Your token has scopes that are not available on your current plan. Revoke the token and create a new one with allowed scopes.
  • 422 "missing_idempotency_key": Add an Idempotency-Key header to POST and PUT requests.
  • 429 rate limited: Wait for the duration in the Retry-After header before retrying.

Sending email — body, headers, deliverability

POST /api/v1/messages/send takes the request shape {to, subject, body: {text, html}, attachments, reply_to_message_id, headers}.

  • body.text and body.html are both optional, but at least one is required. If you provide only body.text we auto-generate an HTML alternative using <p> paragraphs (blank lines split paragraphs; single newlines become <br>) so the message renders as ordinary email in every modern client. If you need monospace, send the literal <pre>...</pre> in body.html.
  • headers is an optional object of user-supplied outbound headers. The whitelist is List-Unsubscribe, List-Unsubscribe-Post, Reply-To, and any X-* custom tracking header. Other names (From, Subject, Message-Id, Authentication-Results, etc.) are platform-managed and rejected with 422. Values containing CR/LF are also rejected (header injection protection). Values are capped at 998 chars per RFC 2822.
  • For bulk/automation use cases, see the Bulk-sender deliverability headers section for List-Unsubscribe setup and the account-wide auto_list_unsubscribe toggle.

Related articles

Jump to nearby guides that continue the workflow.

We use cookies for essential functionality. No ads, no ad tracking.

Sign in to TrekMail

Access your dashboard, mailboxes and DNS.

or
or

Reset email sent

If an account exists for this email, we've sent password reset instructions.

By continuing, you agree to TrekMail's Terms and Privacy Policy.