MepMail Docs

Domains

Verify sending domains with guided DNS, BYODKIM, and per-domain configuration.

Every email must be sent from a domain your team has verified — the API rejects any other from address with 422. Domains are added and verified in the dashboard.

Verification

Adding a domain registers it with SES and shows the DNS records to add:

  • DKIM — a single millionsend._domainkey TXT record. MepMail generates an RSA-2048 keypair per domain and hands the private key to SES (BYODKIM), so verification is one TXT record instead of three CNAMEs. The private key is never stored.
  • MAIL FROM — MX and SPF (TXT) records for the bounce subdomain.

Two verification signals are shown side by side:

  • SES status — what SES reports. SES caches verification and can lag after you change records.
  • Live DNS check — MepMail resolves each record itself and reports Found / Missing / Mismatch immediately.

The API (GET /domains/{id}, POST /domains/{id}/verify, and the get_domain / verify_domain MCP tools) reports the same picture per record in records[]. status uses the Resend vocabulary: for DKIM and MAIL FROM it combines the live check with SES — found in DNS but not yet confirmed by SES reads pending, a different published value reads failed, no record reads not_started. Only those rows gate sending. The DMARC row follows RFC 7489 discovery: it reads verified when a policy covers the domain, including a parent domain's record for a subdomain sender — then inherited_from names the _dmarc record that answered and policy carries its p= value — and not_started when none does. Every record also carries live (found, missing, mismatch or unknown, what public DNS answers right now) and, when a row is not verified, a one-line detail saying why.

Regions

A deployment provisions identities in the SES regions it serves — its AWS_REGIONS, or the one region in AWS_REGION; MepMail Cloud serves sa-east-1 (São Paulo) today. The dashboard's add-domain form lists the served regions, holds back one still in the SES sandbox while another has production access, and defaults to the first production region. The API's optional region field (the create_domain MCP tool included) accepts any served region — the values its schema lists — and defaults to the first; any other value is rejected with 422 naming the served regions. Configuration sets, event topics and SES tenants are regional, so a domain anywhere else would hand out DNS records but never send or report events.

A domain has exactly one region. To move it, delete it and add it again in the other region — its DNS records change, since SES identities are per region. On MepMail Cloud a domain name another team holds is taken in every region.

Per-domain configuration

Once verified, a domain's Configuration tab controls:

  • Click tracking — off by default. When on, links are rewritten to redirect through your own tracking subdomain to record email.clicked events, then send the recipient on to the original URL. When off, your links ship untouched.
  • Open tracking — off by default. When on, a 1×1 pixel served from that same tracking subdomain records email.opened events. Tracking is app-layer and runs on your own domain — never SES's link rewriting.
  • TLS mode — opportunistic (default) or enforced, applied via the domain's SES configuration set.

Through the API and MCP, update_domain takes the same tracking settings, and create_domain accepts them as optional extras so a tracked domain can be stood up in one call (a Resend-shaped call that omits them is unchanged). Tracking is served from the domain's own tracking subdomain: pass tracking_subdomain (a label such as links) and the response's records[] gains a Tracking CNAME whose status turns verified once it resolves. On MepMail Cloud, turning either kind on without a subdomain is refused with a 422. Until the CNAME resolves, links are not routed through it — Cloud ships them clean, self-host falls back to the app host — and the domain shows as Partial in the dashboard: verified for sending, tracking not yet live. On Cloudflare the tracking record must stay DNS only (grey cloud): a proxied CNAME answers with Cloudflare's addresses instead of the target, which the records table reports as a mismatch, and TLS for the tracking host is served by MepMail.

Open-rate accuracy

Open tracking injects a 1×1 transparent pixel with a unique reference into the HTML body; a person loading that image records an email.opened event. It is a directional signal, not an exact count.

Fetches a machine plausibly made are recorded as prefetched, not opened: Apple Mail Privacy Protection downloads every image in the background whether or not the message is read, Gmail pre-fetches while the inbox is already open, and security scanners fetch the pixel within seconds of delivery. A prefetch shows on the email's timeline and as its own line under the open rate, but it never moves the status, never counts in the open rate, and never fires email.opened (endpoints can opt in to email.prefetched). The timing rule's window is OPEN_PREFETCH_WINDOW_SECONDS for self-hosters (default 10; 0 keeps only the user-agent rules). Links go through the same rules, plus two of their own: a desktop Chrome that reports a build number no browser has sent since Chrome's user-agent reduction, and two links of a message hit within a quarter of a second, are a machine's whatever they call themselves. A click recorded before the rest of its burst arrived is taken back — row, inferred open, counters, status and any delivery not yet posted.

A click is also an open. A person cannot click a link in a message they never rendered, so a click on an email with no open yet records the open as well, stamped just before the click and marked reason: "click". For a recipient whose images come through Apple Mail's cache this is the only open that can ever be recorded, so Apple-heavy audiences read lower on opens than they would elsewhere until they click.

Opens are under-counted when the recipient's client blocks images, when the email has no HTML part (a plain-text send carries no pixel), or when Gmail clips a message over ~102 KB and the recipient never expands it.

Clicks are the more reliable engagement signal. For purely transactional mail — receipts, password resets — consider leaving open tracking off: the pixel adds a tracking-shaped element some filters weigh against inbox placement, for a metric you can't fully trust anyway.

API keys and domains

An API key can be scoped to a single domain; such a key can only send from that domain (other domains return 403 restricted_api_key).

On this page