Get your API key
API reference

Routes

Inbound routing rules — match an address and deliver it to a webhook, agent, or forward. See Inbound webhooks 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/routes List inbound routing rules.

List inbound routing rules.

Response body

array route.json

An inbound routing rule: which addresses it matches and what happens to matching mail (webhook POST, forward, store, drop, or an inbox agent). The per-route signing secret is returned ONCE on create and via GET /api/domains/:id/webhook/secret — list responses omit it.

FieldTypeNotes
id req stringRoute id (rte_…).
user_id req stringOwning account (usr_…).
match_pattern req stringWhich recipient addresses this route matches: an exact address, `*@domain`, a prefix pattern like `ticket+*@domain`, or a `/regex/`.
action req "webhook" · "forward" · "store" · "drop" · "agent"What happens to a matching message.
destinationstring · nullWebhook URL or forward address. null for store/drop/agent actions.
agent_promptstring · nullInstructions for the built-in inbox agent (action 'agent' only).
agent_forward_tostring · nullExtra addresses the inbox agent's forward tool may reach, as a JSON array string. null = owner-controlled addresses only.
agent_context"message" · "thread" · "sender" · nullHow much mail the inbox agent may read once the sender is verified. null = 'message' (just the inbound email).
active req 0 · 11 while the route is enabled.
ack_mode req "lenient" · "ack"Webhook acknowledgement strictness: 'lenient' accepts any 2xx; 'ack' also requires {"status":"ok"} (or the x-mailkite-ack header) on every delivery.
last_status"ok" · "fail" · "running" · "timeout" · nullOutcome of the route's most recent action. null = never exercised.
last_status_messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
last_status_codeinteger · nullTransport status code of the last outcome, when relevant.
last_status_atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
consecutive_failuresintegerCurrent failing streak (0 when healthy).
first_failed_atinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
unhealthy_notified_atinteger · nullWhen the owner was emailed about the current streak, in Unix epoch milliseconds (UTC); null when it has never happened.
signing_secretstring · nullPer-route webhook signing secret (whsec_…). Returned on create; read later via GET /api/domains/:id/webhook/secret. null until first read lazily creates it.
created_at req integerWhen the route was created, in Unix epoch milliseconds (UTC).
POST /api/routes Create a route (match, action, destination).

Create a route (match, action, destination).

Request body

create-route-request.json
FieldTypeNotes
match req stringAddress pattern: exact, *@domain, addr+*@domain, or /regex/.
action"webhook" · "forward" · "store" · "drop" · "agent"What to do with matching mail. Defaults to webhook.
destinationstringRequired for action webhook (URL) or forward (address).
agentPromptstringRequired for action agent — instructions for the inbox agent.
agentForwardTostring[]For action agent: addresses the inbox agent's forward tool may send to. The agent can always forward to your account email and any address on a verified domain you own; this adds extra approved addresses (e.g. an escalation inbox).
agentContext"message" · "thread" · "sender"For action agent: how much mail the agent may read once the sender is verified (they replied to an earlier agent message from a DMARC-passing address). 'message' (default) = only the incoming email; 'thread' = this conversation; 'sender' = all past mail with this sender.
forwardSpambooleanFor action forward: also re-send mail scored as spam. Defaults to false — a forward re-sends over our own outbound infrastructure, so spam-verdicted mail is held (event forward.spam_held) rather than relayed. Set true if you would rather receive spam than risk missing anything; either way the message is stored and still fires webhook/agent routes.

Response body

route.json

An inbound routing rule: which addresses it matches and what happens to matching mail (webhook POST, forward, store, drop, or an inbox agent). The per-route signing secret is returned ONCE on create and via GET /api/domains/:id/webhook/secret — list responses omit it.

FieldTypeNotes
id req stringRoute id (rte_…).
user_id req stringOwning account (usr_…).
match_pattern req stringWhich recipient addresses this route matches: an exact address, `*@domain`, a prefix pattern like `ticket+*@domain`, or a `/regex/`.
action req "webhook" · "forward" · "store" · "drop" · "agent"What happens to a matching message.
destinationstring · nullWebhook URL or forward address. null for store/drop/agent actions.
agent_promptstring · nullInstructions for the built-in inbox agent (action 'agent' only).
agent_forward_tostring · nullExtra addresses the inbox agent's forward tool may reach, as a JSON array string. null = owner-controlled addresses only.
agent_context"message" · "thread" · "sender" · nullHow much mail the inbox agent may read once the sender is verified. null = 'message' (just the inbound email).
active req 0 · 11 while the route is enabled.
ack_mode req "lenient" · "ack"Webhook acknowledgement strictness: 'lenient' accepts any 2xx; 'ack' also requires {"status":"ok"} (or the x-mailkite-ack header) on every delivery.
last_status"ok" · "fail" · "running" · "timeout" · nullOutcome of the route's most recent action. null = never exercised.
last_status_messagestring · nullHuman-readable detail of the last outcome (e.g. 'HTTP 500').
last_status_codeinteger · nullTransport status code of the last outcome, when relevant.
last_status_atinteger · nullWhen the last outcome landed, in Unix epoch milliseconds (UTC); null when it has never happened.
consecutive_failuresintegerCurrent failing streak (0 when healthy).
first_failed_atinteger · nullWhen the current failing streak began, in Unix epoch milliseconds (UTC); null when it has never happened.
unhealthy_notified_atinteger · nullWhen the owner was emailed about the current streak, in Unix epoch milliseconds (UTC); null when it has never happened.
signing_secretstring · nullPer-route webhook signing secret (whsec_…). Returned on create; read later via GET /api/domains/:id/webhook/secret. null until first read lazily creates it.
created_at req integerWhen the route was created, in Unix epoch milliseconds (UTC).
DELETE /api/routes/:id Delete an inbound routing rule by id. Pair with createRoute to register and tear down a webhook destination — e.g. an automation platform subscribing on enable and cleaning up on disable.

Delete an inbound routing rule by id. Pair with createRoute to register and tear down a webhook destination — e.g. an automation platform subscribing on enable and cleaning up on disable.

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.
POST /api/routes/:id/deliver POST stored messages to one webhook route — including messages that arrived BEFORE the route existed, which no retry can reach (a retry replays an existing delivery row, and a new route has none). Use listRouteCandidates to find what a route could be sent. Webhook routes only: a forward would re-send real mail to a third party and an agent would re-spend model tokens, so both answer 400 `route_action`. At most 50 ids per request; same per-id `results` contract as retryDeliveries.

POST stored messages to one webhook route — including messages that arrived BEFORE the route existed, which no retry can reach (a retry replays an existing delivery row, and a new route has none). Use listRouteCandidates to find what a route could be sent. Webhook routes only: a forward would re-send real mail to a third party and an agent would re-spend model tokens, so both answer 400 `route_action`. At most 50 ids per request; same per-id `results` contract as retryDeliveries.

Request body

deliver-to-route-request.json

Which stored messages to POST to this route. Unlike a retry, these messages need no existing delivery on the route — this is how mail that arrived BEFORE the route existed reaches it. At most 50 ids per request.

FieldTypeNotes
messageIds req string[]Stored messages (msg_…) to deliver to this route's webhook, in any order.

Response body

replay-response.json

The outcome of a batch replay. Always 200 with per-id detail — one unreachable endpoint must not hide the outcomes of the rest — so check `ok` and `results`, not the HTTP status.

FieldTypeNotes
ok req booleanEvery requested id succeeded. False if anything failed or was skipped.
requested req integerHow many replays this call accounted for — equal to `results.length`.
delivered req integerHow many endpoints accepted the replay.
failed req integerHow many were attempted and rejected (or timed out).
skipped req integerHow many ids were never attempted — unresolvable, or a message with no delivery to replay.
results req replay-result[]One row per requested id, in no guaranteed order.
results[].messageId req string · nullThe message replayed (msg_…); null when the id could not be resolved to one.
results[].routeId req string · nullThe route whose webhook was targeted (rte_…); null when nothing was attempted.
results[].deliveryId req string · nullThe NEW delivery row this replay appended (dlv_…); null when nothing was attempted.
results[].ok req booleanThe endpoint accepted this replay.
results[].status req integer · nullHTTP status the endpoint returned; null when the request failed before a response, or nothing was attempted.
results[].reason req string · nullWhy it did not succeed. `not_found` = no such id on this account (a foreign id reads the same way, deliberately). `no_delivery` = the message has no webhook delivery to replay. Otherwise the delivery's own classification: `http`, `timeout`, `network`, `redirect`, or `no-ack`. Null when ok.
GET /api/routes/:id/candidates Stored inbound messages this route could be asked to deliver, newest first — the preview for replaying mail to a route defined after that mail arrived. Each row carries `delivered_here`, so you can send a route only what it has never seen. Page with `before` (the response's `nextBefore`); because matching happens after a bounded scan, a page can come back shorter than `limit` while more still remain — keep going until `nextBefore` is null.

Stored inbound messages this route could be asked to deliver, newest first — the preview for replaying mail to a route defined after that mail arrived. Each row carries `delivered_here`, so you can send a route only what it has never seen. Page with `before` (the response's `nextBefore`); because matching happens after a bounded scan, a page can come back shorter than `limit` while more still remain — keep going until `nextBefore` is null.

Response body

route-candidates-response.json

A page of messages one route could replay, newest first.

FieldTypeNotes
messages req route-candidate[]Matching messages, newest first.
messages[].id req stringMessage id (msg_…) — pass this to deliverToRoute.
messages[].from_addr req stringSender address as stored.
messages[].to_addr req stringRecipient the route's pattern matched.
messages[].subject req string · nullSubject line, or null when the message carried none.
messages[].received_at req integerWhen the message arrived, in Unix epoch milliseconds (UTC). Also the paging cursor.
messages[].delivered_here req 0 · 11 when this route already has a delivery for the message, 0 when it has never seen it.
nextBefore req integer · nullCursor for the next older page — pass as `before`. null when the scan reached the end. It is the last row SCANNED, not the last returned, so a page can legitimately come back shorter than `limit` while more remain.