Get your API key
API reference

Domains

Add, verify, and manage domains: DNS records, catch-all webhooks, retention and encryption, and domain registration. See Domains & DNS for a guided walk-through.

Auth: Session token · Base URL: https://api.mailkite.dev

Endpoints

Each row expands to its request and response schema. The address bar follows along, so any endpoint can be linked to directly.

GET /api/domains List your domains, each with its webhook URL.

List your domains, each with its webhook URL.

Response body

array domain.json

A sending/receiving domain on your account. List and detail responses enrich the stored row with the domain's catch-all webhook (webhookUrl/webhookAckMode/webhookStatus) and the account's daily-limit state; write responses return the row alone, so those fields are optional here.

FieldTypeNotes
id req stringDomain id (dom_…).
user_id req stringOwning account (usr_…).
domain req stringThe domain name, lowercased.
status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
spf_verified req 0 · 11 once the SPF TXT record is in place.
dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
dmarc_verified req 0 · 11 once a DMARC policy is published.
zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
POST /api/domains Add a domain. Returns the domain + DNS records. Paid plans may pass `email_provider_id` to choose a provider (list available providers with listEmailProviders); free plans are always pinned to the platform default (SES US East 2 production).

Add a domain. Returns the domain + DNS records. Paid plans may pass `email_provider_id` to choose a provider (list available providers with listEmailProviders); free plans are always pinned to the platform default (SES US East 2 production).

Request body

create-domain-request.json
FieldTypeNotes
domain req string

Response body

create-domain-response.json

POST /api/domains — the new domain plus the complete DNS record set to publish (every enabled provider's records merged, so one set works wherever the domain lands).

FieldTypeNotes
domain req domainThe new domain row.
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
dns req dns-record[]The records to publish at your DNS provider.
dns[].type req "MX" · "TXT" · "CNAME" · "A"Record type.
dns[].name req stringFull hostname (FQDN) the record lives at.
dns[].value req stringBare RDATA value.
dns[].priorityintegerMX priority — present on MX records only.
warningsstring[]Present when a provider registration failed — the DNS set omits that provider's records until a later verify backfills them.
GET /api/domains/subdomain/suggest Suggest a free, currently-unclaimed subdomain label to prefill the input with, plus the `base` zone it would live on. Read-only — suggesting does not reserve the name. Always take the zone from `base` rather than hard-coding it: which zones are on offer changes over time.

Suggest a free, currently-unclaimed subdomain label to prefill the input with, plus the `base` zone it would live on. Read-only — suggesting does not reserve the name. Always take the zone from `base` rather than hard-coding it: which zones are on offer changes over time.

Response body

subdomain-suggestion.json

GET /api/domains/subdomain/suggest — a randomly generated, currently-unclaimed label to prefill the input with. Read-only: suggesting does not reserve the name, so claim it promptly or re-check before claiming.

FieldTypeNotes
subdomain req stringThe suggested label on its own (adjective + noun, sometimes with a 2-digit suffix).
domain req stringThe full hostname the label would become — `<subdomain>.<base>`.
base req stringThe zone this subdomain would live on. Always read it from here rather than hard-coding a hostname: which zone new subdomains are handed out under changes over time, and more than one may be on offer.
GET /api/domains/subdomain/check Check whether a free subdomain label can be claimed. Read-only and cheap — call it as the user types. `reason` explains a rejection and is safe to show verbatim.

Check whether a free subdomain label can be claimed. Read-only and cheap — call it as the user types. `reason` explains a rejection and is safe to show verbatim.

Response body

subdomain-availability.json

GET /api/domains/subdomain/check — whether a free subdomain label can be claimed. Read-only; safe to call on every keystroke as the user edits the label.

FieldTypeNotes
available req booleanTrue when the label passes validation and nobody holds it yet.
reasonstringWhy an unavailable label was rejected — show it verbatim. Either a format rule ('At least 3 characters', 'Use a single label (no dots)', 'Use letters, numbers and hyphens only', 'That subdomain is reserved') or 'Already taken'.
domainstringThe full hostname the label resolves to. Absent when the label is malformed enough that no hostname could be formed.
POST /api/domains/subdomain Claim a free MailKite subdomain — a `<label>.<base>` host on a zone we run (call suggestSubdomain for the current `base`; the pool changes over time and more than one may be offered). The fastest path to a sending identity: we host the zone, so it comes back already verified with an empty `dns` array — nothing for the customer to publish. Use it when you want onboarding to work without asking anyone to touch DNS; bring your own domain with createDomain when you want mail to come from your own name.

Claim a free MailKite subdomain — a `<label>.<base>` host on a zone we run (call suggestSubdomain for the current `base`; the pool changes over time and more than one may be offered). The fastest path to a sending identity: we host the zone, so it comes back already verified with an empty `dns` array — nothing for the customer to publish. Use it when you want onboarding to work without asking anyone to touch DNS; bring your own domain with createDomain when you want mail to come from your own name.

Request body

claim-subdomain-request.json

POST /api/domains/subdomain — the label to claim on the free pool zone.

FieldTypeNotes
subdomain req stringThe label to claim: 3–32 characters, lowercase letters, digits and hyphens, no leading or trailing hyphen, and not a reserved word. Just the label — a full hostname (`<label>.<base>`) is also accepted and the base stripped.

Response body

claim-subdomain-response.json

POST /api/domains/subdomain — the claimed subdomain, already verified. Unlike a domain you bring yourself, the pool zone covers this hostname entirely: `dns` is an empty array because there is nothing for you to publish.

FieldTypeNotes
domain req domainThe new domain row. Returned with `status: "verified"` and all four DNS checks passing — it can send and receive immediately (subject to the account email being verified).
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
dns req dns-record[]Always empty for a claimed subdomain. Present so the shape matches createDomain, where it carries the records to publish.
dns[].type req "MX" · "TXT" · "CNAME" · "A"Record type.
dns[].name req stringFull hostname (FQDN) the record lives at.
dns[].value req stringBare RDATA value.
dns[].priorityintegerMX priority — present on MX records only.
GET /api/domains/:id Get one domain with DNS records + webhook.

Get one domain with DNS records + webhook.

Response body

get-domain-response.json

GET /api/domains/:id — the domain (with webhook enrichment) plus its current DNS record set.

FieldTypeNotes
domain req domainA sending/receiving domain on your account. List and detail responses enrich the stored row with the domain's catch-all webhook (webhookUrl/webhookAckMode/webhookStatus) and the account's daily-limit state; write responses return the row alone, so those fields are optional here.
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
dns req dns-record[]The records to publish at your DNS provider.
dns[].type req "MX" · "TXT" · "CNAME" · "A"Record type.
dns[].name req stringFull hostname (FQDN) the record lives at.
dns[].value req stringBare RDATA value.
dns[].priorityintegerMX priority — present on MX records only.
DELETE /api/domains/:id Remove a domain.

Remove a domain.

Response body

delete-domain-response.json

DELETE /api/domains/:id — confirmation plus what infrastructure teardown accomplished. Teardown is best-effort and never blocks the delete; warnings record anything left behind.

FieldTypeNotes
ok req true
cleanup req objectWhat the teardown did.
cleanup.deregistered req string[]Provider types the domain was deregistered from.
cleanup.dnsDeleted req integerHow many MailKite-hosted DNS records were removed (0 when we don't host the domain's DNS).
cleanup.warnings req string[]Steps that failed — the only record that something was left behind.
POST /api/domains/:id/verify Check DNS and update status.

Check DNS and update status.

Response body

verify-domain-response.json

POST /api/domains/:id/verify — fresh DNS check results, the resulting status, and the sending provider's enablement outcome.

FieldTypeNotes
status req "verified" · "pending"'verified' once MX resolves; SPF/DKIM/DMARC refine sending but don't gate inbound.
checks req objectPer-record verification results from this check.
checks.mx req booleanMX resolves to MailKite.
checks.spf req booleanSPF TXT record is in place.
checks.dkim req booleanDKIM records are accepted by the sending provider.
checks.dmarc req booleanA DMARC policy is published.
checkedAt req integerWhen this check ran, in Unix epoch milliseconds (UTC).
sending req objectOutcome of enabling outbound sending with the domain's provider (attempted once SPF+DKIM pass).
sending.ok req booleanThe provider call succeeded.
sending.enabledbooleanFinal enabled state reported by the provider.
sending.dkimVerifiedbooleanWhether the provider accepted the DKIM records (can lead `enabled`).
sending.alreadyEnabledbooleanTrue when the domain was already registered with the provider (no-op).
sending.providerIdstringProvider-specific domain identifier, when reported.
sending.errorstringWhy the provider call failed, when it did.
PUT /api/domains/:id/webhook Set or replace the domain's catch-all webhook.

Set or replace the domain's catch-all webhook.

Request body

set-webhook-request.json
FieldTypeNotes
url req string

Response body

set-webhook-response.json

PUT /api/domains/:id/webhook — the domain, the stored webhook, and this route's signing secret (returned here so the receiver can verify x-mailkite-signature without a second call).

FieldTypeNotes
domain req domainThe domain row (without webhook enrichment — the webhook you just set is alongside).
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
webhookUrl req stringThe stored webhook URL.
ackMode req "lenient" · "ack"Acknowledgement strictness in effect.
routeId req stringThe catch-all route (rte_…) that carries this webhook.
signingSecret req stringThis route's signing secret (whsec_…). Store it server-side; verify every delivery.
PUT /api/domains/:id/tracking-webhook Set or replace the domain's dedicated tracking-event webhook: an HTTPS endpoint that receives signed email.* engagement events (email.sent / email.bounced / email.complained / email.opened / email.clicked, shaped per the tracking-event schema) SEPARATELY from inbound mail. This is the split-endpoint override for consumers that keep engagement events away from their inbound webhook (e.g. django-anymail's paired views); most consumers should prefer setWebhookEvents, which delivers everything to the one inbound webhook. When both are configured, this URL wins for engagement events. Returns the signing secret (the same account secret as inbound deliveries).

Set or replace the domain's dedicated tracking-event webhook: an HTTPS endpoint that receives signed email.* engagement events (email.sent / email.bounced / email.complained / email.opened / email.clicked, shaped per the tracking-event schema) SEPARATELY from inbound mail. This is the split-endpoint override for consumers that keep engagement events away from their inbound webhook (e.g. django-anymail's paired views); most consumers should prefer setWebhookEvents, which delivers everything to the one inbound webhook. When both are configured, this URL wins for engagement events. Returns the signing secret (the same account secret as inbound deliveries).

Request body

set-tracking-webhook-request.json
FieldTypeNotes
url req stringHTTPS endpoint to receive signed email.* engagement events (sent/bounced/complained/opened/clicked). Deliberately separate from the inbound-mail webhook, so inbound consumers never see event types they don't expect.

Response body

set-tracking-webhook-response.json
FieldTypeNotes
domain req objectThe updated domain row, now carrying tracking_webhook_url.
signingSecret req stringThe account webhook secret events are signed with (same secret as inbound deliveries) — verify the x-mailkite-signature header against it.
DELETE /api/domains/:id/tracking-webhook Remove the domain's tracking-event webhook (engagement events stop).

Remove the domain's tracking-event webhook (engagement events stop).

Response body

ok-response.json

The uniform acknowledgement for deletes and other actions with nothing else to report.

FieldTypeNotes
ok req trueAlways true — a non-2xx error body is returned otherwise.
PUT /api/domains/:id/webhook-events Opt the domain's inbound webhook into engagement events — one webhook, all events. Pass "all" or a list of email.* tracking types (email.sent / email.bounced / email.complained / email.opened / email.clicked) and MailKite delivers them to the same webhook route that receives the domain's inbound mail; consumers switch on the payload's `type` (inbound mail is type email.received, engagement events follow the tracking-event schema). Off by default, so existing inbound consumers never see event types they didn't opt into. Events at the inbound webhook are signed with that route's secret (account secret fallback) — the same key inbound deliveries already use. If a dedicated tracking webhook URL is set (setTrackingWebhook), engagement events go there instead.

Opt the domain's inbound webhook into engagement events — one webhook, all events. Pass "all" or a list of email.* tracking types (email.sent / email.bounced / email.complained / email.opened / email.clicked) and MailKite delivers them to the same webhook route that receives the domain's inbound mail; consumers switch on the payload's `type` (inbound mail is type email.received, engagement events follow the tracking-event schema). Off by default, so existing inbound consumers never see event types they didn't opt into. Events at the inbound webhook are signed with that route's secret (account secret fallback) — the same key inbound deliveries already use. If a dedicated tracking webhook URL is set (setTrackingWebhook), engagement events go there instead.

Request body

set-webhook-events-request.json
FieldTypeNotes
events req "all" · "email.sent" · "email.bounced" · "email.complained" · "email.opened" · "email.clicked"[]Which email.* engagement events the domain's inbound webhook also receives: the literal "all", or a non-empty list of event types. Consumers distinguish deliveries by the payload's `type` (inbound mail is email.received; engagement events follow the tracking-event schema).

Response body

set-webhook-events-response.json
FieldTypeNotes
domain req objectThe updated domain row, now carrying webhook_events (a JSON-encoded "all" or event-type list).
signingSecret req stringThe account webhook secret (fallback signing key). Events delivered to the inbound webhook are signed with that route's own secret when it has one, falling back to this — the same keys inbound deliveries already use, so verification code needs no change.
DELETE /api/domains/:id/webhook-events Opt the domain's inbound webhook back out of engagement events (inbound email.received only — the default).

Opt the domain's inbound webhook back out of engagement events (inbound email.received only — the default).

Response body

ok-response.json

The uniform acknowledgement for deletes and other actions with nothing else to report.

FieldTypeNotes
ok req trueAlways true — a non-2xx error body is returned otherwise.
DELETE /api/domains/:id/webhook Remove the domain's webhook.

Remove the domain's webhook.

Response body

ok-response.json

The uniform acknowledgement for deletes and other actions with nothing else to report.

FieldTypeNotes
ok req trueAlways true — a non-2xx error body is returned otherwise.
GET /api/domains/:id/webhook/secret Get this domain's webhook signing secret (whsec_…) — the per-route secret used to verify x-mailkite-signature on its inbound deliveries. Server-side only (SDK/REST); never exposed to MCP or browser client libraries.

Get this domain's webhook signing secret (whsec_…) — the per-route secret used to verify x-mailkite-signature on its inbound deliveries. Server-side only (SDK/REST); never exposed to MCP or browser client libraries.

Response body

webhook-secret-response.json

GET /api/domains/:id/webhook/secret — this domain's webhook signing secret. Server-side only; never exposed to MCP or browser clients.

FieldTypeNotes
secret req stringThe per-route signing secret (whsec_…) for the domain's catch-all webhook.
POST /api/domains/:id/webhook/test Send a signed test event to the domain's webhook.

Send a signed test event to the domain's webhook.

Response body

test-webhook-response.json

POST /api/domains/:id/webhook/test — outcome of the signed test delivery.

FieldTypeNotes
ok req booleanThe endpoint accepted the test event (2xx, and acknowledged when ackMode is 'ack').
status req integerHTTP status the endpoint returned (0 when the request failed before a response).
GET /api/domains/register/check Check whether a domain is available to register, and at what price. Read-only — no charge.

Check whether a domain is available to register, and at what price. Read-only — no charge.

Response body

domain-availability-response.json

GET /api/domains/register/check — whether a domain can be registered through MailKite, and at what price. Read-only; no charge.

FieldTypeNotes
configured req booleanFalse when domain registration isn't available on this deployment — the other fields are then placeholders.
domain req stringThe normalized domain that was checked.
available req booleanTrue when the domain can be registered.
premium req booleanTrue for registry-premium names, which can't be registered here.
reasonstringWhy an unavailable domain can't be registered (e.g. 'Already on MailKite').
priceobjectWhat you'd actually be charged (registrar cost grossed up with fees). Absent when unavailable.
price.amount req numberPrice in major units of `currency`.
price.currency req stringISO 4217 currency code.
price.period req integerHow many periodUnits the amount covers.
price.periodUnit req "y" · "m"Unit of `period` — years or months.
POST /api/domains/register Register (buy) a domain on the customer's behalf; provisions mail DNS and adds it to the account in one call. Charges the registrar.

Register (buy) a domain on the customer's behalf; provisions mail DNS and adds it to the account in one call. Charges the registrar.

Request body

register-domain-request.json
FieldTypeNotes
domain req string
contact req object
contact.firstName req string
contact.lastName req string
contact.email req string
contact.phone req stringE.164-ish, +<cc>.<number>, e.g. +1.4155551234
contact.address req string
contact.city req string
contact.zip req string
contact.country req stringISO 3166-1 alpha-2, e.g. US
contact.statestringISO 3166-2 subdivision, e.g. US-CA
contact.organizationstring
contact.type"individual" · "company" · "association" · "publicbody"
yearsinteger
dryRunboolean

Response body

register-domain-response.json

POST /api/domains/register — a dry run returns just { registration, dryRun }; a real registration returns the new domain, its DNS set (auto-provisioned on the registrar when we manage its DNS), the registration reference, and the charge.

FieldTypeNotes
registration req objectThe registrar's view of the registration.
registration.status req stringRegistrar status (e.g. 'registered', 'pending').
registration.referencestring · nullRegistrar order/operation reference, when provided.
dryRuntruePresent (true) only on dry-run responses — nothing was charged or created.
domaindomainThe new domain row. Real registrations only.
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
dnsdns-record[]The mail DNS record set. Real registrations only.
dns[].type req "MX" · "TXT" · "CNAME" · "A"Record type.
dns[].name req stringFull hostname (FQDN) the record lives at.
dns[].value req stringBare RDATA value.
dns[].priorityintegerMX priority — present on MX records only.
dnsProvisionedbooleanTrue when the records were published automatically on the registrar's DNS.
chargeobjectWhat was charged. Real registrations only.
charge.amount req integerAmount in minor units (cents).
charge.currency req stringLowercase ISO 4217 code.
charge.status req "captured" · "capture_failed"'capture_failed' means the domain is registered and yours, but the payment capture needs follow-up.
PUT /api/domains/:id/retention Toggle zero-retention passthrough for the domain ({ zeroRetention }). With it on, message bodies are relayed to your webhook and never stored.

Toggle zero-retention passthrough for the domain ({ zeroRetention }). With it on, message bodies are relayed to your webhook and never stored.

Request body

set-retention-request.json
FieldTypeNotes
zeroRetention req booleantrue turns zero-retention passthrough ON: inbound mail for the domain is delivered to your webhook and never stored — no replay, no dashboard history, your endpoint holds the only copy. false restores normal retention.

Response body

domain-response.json

The uniform write-response for domain settings (retention, encryption): the updated domain row.

FieldTypeNotes
domain req domainThe updated domain row (without webhook enrichment).
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
PUT /api/domains/:id/encryption Enable at-rest encryption for the domain with a public key ({ publicKey }). Stored bodies are encrypted to it; only the holder of the private key can read them back.

Enable at-rest encryption for the domain with a public key ({ publicKey }). Stored bodies are encrypted to it; only the holder of the private key can read them back.

Request body

set-encryption-request.json
FieldTypeNotes
publicKey req stringThe public key stored bodies will be encrypted to: a PEM (SPKI) RSA key, an armored OpenPGP public key, or an X.509 certificate (S/MIME). MailKite keeps only the public key and its fingerprint — never a private key.

Response body

domain-response.json

The uniform write-response for domain settings (retention, encryption): the updated domain row.

FieldTypeNotes
domain req domainThe updated domain row (without webhook enrichment).
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.
DELETE /api/domains/:id/encryption Disable at-rest encryption for the domain. Messages stored while it was on stay encrypted.

Disable at-rest encryption for the domain. Messages stored while it was on stay encrypted.

Response body

domain-response.json

The uniform write-response for domain settings (retention, encryption): the updated domain row.

FieldTypeNotes
domain req domainThe updated domain row (without webhook enrichment).
domain.id req stringDomain id (dom_…).
domain.user_id req stringOwning account (usr_…).
domain.domain req stringThe domain name, lowercased.
domain.status req "pending" · "verified" · "failed"Overall verification state. Inbound routing requires at least MX verification; `verified` follows the MX check.
domain.mx_verified req 0 · 11 once the MX record resolves to MailKite — the gate for inbound mail, webhooks, and routes.
domain.spf_verified req 0 · 11 once the SPF TXT record is in place.
domain.dkim_verified req 0 · 11 once the DKIM records are accepted by the sending provider.
domain.dmarc_verified req 0 · 11 once a DMARC policy is published.
domain.zero_retention0 · 11 when zero-retention passthrough is on: inbound mail is delivered to the webhook and never stored (no replay, no dashboard history).
domain.track_opens0 · 1Domain-wide default for outbound open tracking (a per-send trackOpens overrides it).
domain.enc_public_keystring · nullAt-rest encryption public key (PEM SPKI, armored OpenPGP key, or X.509 cert). null = encryption off.
domain.enc_key_algstring · nullKey algorithm recorded for the uploaded key ('RSA-OAEP-256', 'PGP', or 'S/MIME'). null = encryption off.
domain.enc_fingerprintstring · nullFingerprint of the uploaded key (sha256 of the SPKI/cert DER, or the OpenPGP fingerprint).
domain.enc_formatstring · nullWhich client-decryptable standard the key is: 'pgp' or 'smime'. null = off or legacy custom envelope.
domain.provider req stringEmail sending provider type currently backing this domain (e.g. 'ses', 'cloudflare'). Assigned by MailKite, never chosen by the caller.
domain.email_provider_idstring · nullRegistry id of the concrete provider instance backing the domain.
domain.dns_tokensstring · nullProvider-specific DNS tokens as a JSON string (e.g. SES DKIM CNAME targets). Feeds the dns record set; null until providers report them.
domain.last_checked_atinteger · nullWhen domain verification last ran, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.created_at req integerWhen the domain was added, in Unix epoch milliseconds (UTC).
domain.webhookUrlstring · nullURL of the domain's catch-all webhook, or null when none is set. List/detail responses only.
domain.webhookAckModestring · nullThe webhook's acknowledgement mode: 'lenient' (any 2xx) or 'ack' (2xx + explicit ok). List/detail responses only.
domain.webhookStatusobject · nullHealth of the webhook's most recent delivery, or null when it has never been exercised. List/detail responses only.
domain.webhookStatus.statusstring · null'ok', 'fail', 'running', or 'timeout'.
domain.webhookStatus.messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
domain.webhookStatus.codeinteger · nullHTTP status of the last delivery, when relevant.
domain.webhookStatus.atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.consecutiveFailuresintegerCurrent failing streak (0 when healthy).
domain.webhookStatus.firstFailedAtinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
domain.webhookStatus.unhealthybooleanTrue once the failing streak passes the unhealthy threshold.
domain.dailyLimitReachedbooleanTrue when the owning account has hit its daily send cap. List/detail responses only.