---
title: "Mail to a webhook"
description: "Hand the mail of an address straight to your own server instead of a mailbox, and copy what an address receives to up to five endpoints."
url: "https://nextmail.sh/docs/knowledge/developers/webhook-routing"
area: "Knowledge base"
category: "Developers"
---

# Mail to a webhook

Hand the mail of an address straight to your own server instead of a mailbox, and copy what an address receives to up to five endpoints.

## Addresses that post to a webhook

An address whose destination is `webhook` is not a mailbox. Each message it receives is kept exactly as it arrived for 30 days and posted to one webhook endpoint of the workspace as the event `email.routed`, with the text, the HTML, every attachment and the whole message behind signed links. Nothing is filed, so nothing shows in the app, a mail app or a search. It suits mail a program reads: replies to a ticket system, receipts to parse, a support form that sends its mail on.

- In the web app, open the address, edit Delivers to on its Delivery tab, choose A webhook only and pick the endpoint. In the phone app, Where mail goes on the Delivery tab of the address screen offers the same four destinations.
- Over REST, `destination: "webhook"` and `webhookId` on `POST /domains/{id}/addresses` and `PATCH /domains/{id}/addresses/{addressId}`, and every address reports them.
- In the SDKs, the same fields on `domains.createAddress` and `domains.updateAddress`, written the way each language writes names.
- Over MCP and in the assistant, the same fields on `addDomainAddress` and `updateDomainAddress`.
- A route of the domain can do the same for a whole pattern of local parts: `action: "webhook"` on `POST /domains/{id}/routes` sends every address it catches to its endpoint, and `routedBy` names the route.

The limits of the address still apply before anything is posted: a receiving hold, the receiving caps and the allowed senders turn mail away exactly as they do for a mailbox, and every message counts toward the caps. Rules, out of office replies, AI labels, tasks, automations and the mailbox counts do not run, because there is no mailbox for them to act on, and the storage quota of the address does not count what is routed.

> When its endpoint is switched off or deleted, the address files its mail in its own mailbox instead, so nothing is lost while it has nowhere to go. It posts again once the endpoint is switched back on, or once the address names another one. A route whose endpoint is gone or switched off takes no mail, and the next route or the catch-all decides.

Mail apps do not reach an address that posts to a webhook, since it has no mailbox to open. They reach it again once its endpoint is deleted, because its mail then lands in its mailbox, and they reach a group address that keeps a copy. Sending from the address works as before.

## The email.routed event

`email.routed` goes only to the endpoint the address or the route names and to the taps of the address. No endpoint subscribes to it, it is not in the event list of the webhook catalogue, and its retries and replays skip the subscription and the address and domain lists of the endpoint. It has the signature, the retry policy and the dead letters of every other event, and every attempt carries `content` and `raw` whatever the endpoint asks for, read when the attempt is sent, so the delivery log keeps the event without the message.

| Field | What it holds |
| --- | --- |
| `routedMessageId` | The stored message, an id that starts with `rtd_`. It is kept 30 days after it arrived. |
| `to` | The address the message was handled as, without a plus tag: the address itself, or the address a route caught. |
| `recipient` | The address it was sent to, exactly as the sending server gave it, plus tag included, so a tag can carry a ticket number or a token. |
| `from`, `fromName`, `subject`, `date`, `messageId` | Read off the headers, as in `email.received`. |
| `receivedAt`, `size` | When it arrived, and its size in bytes. |
| `spam` | True when it carries the signs that file mail to Spam, or its sender is blocked by name. It is posted either way. |
| `verdicts` | What the receiving server found for SPF, DKIM, DMARC, spam and viruses, each `pass`, `fail`, `gray`, `processing_failed` or `disabled`, and `arc`, the verdict for the servers that forwarded the message on its way here: `pass`, `fail`, `none` or `temperror`. |
| `routedBy` | `{ kind, id }`: `address` and the address id, or `route` and the route id. |
| `content` | The text, the HTML up to 512 KiB and every attachment, inline images included, each with a download link. |
| `raw` | A link to the whole message as a `message/rfc822` file, byte for byte as it arrived. |

- The links in `content` and `raw` work for an hour after each attempt, while the endpoint stays switched on, and until the stored message is deleted. They are `/public/webhook-files/{token}` and `/public/webhook-raw/{token}`, the same as for `email.received`, and need no credential.
- A routed message is deleted 30 days after it arrived, by an hourly job, and with its domain when the domain is removed. A replay after that carries the event without `content` and `raw`.
- A message that arrives again, after a retry of the sending server or a second attempt on our side, is stored and posted once per address.

## Taps

A tap copies everything an address receives to an endpoint, without changing where the mail goes. An address takes up to five, as `tapWebhookIds` on the same address requests. A message filed in its mailbox reaches each tap as `email.received`, and a message posted to a webhook as `email.routed`.

- In the web app, Copies to webhooks on the Delivery tab of the address picks up to five endpoints. In the phone app, the webhook copies are set under Where mail goes on the same tab of the address screen.
- A tap is targeted: it reaches the endpoint whatever its subscription and its address and domain lists say. On `email.received` the data then carries `tap: true`.
- An endpoint gets each message once. When it also subscribes to `email.received`, or is the endpoint the address posts to, the second copy is not sent.
- A tap whose endpoint was deleted is ignored. A delivery report that arrives at the address is `email.bounced` and does not reach the taps, and neither does mail a forward-only address archives.
- A group address reaches its taps only when it keeps a copy, since the copy is the message it files. Each member address has taps of its own.

> Taps and webhook addresses send what an address receives to a server you run, so set them on addresses whose mail you mean to leave Nextmail.

- [Webhooks](https://nextmail.sh/docs/knowledge/developers/webhooks.md): Events, signatures, payloads and the delivery log.
- [Webhook retries and dead letters](https://nextmail.sh/docs/knowledge/developers/webhook-retries.md): How a failed delivery is tried again and sent once more.
- [Message source](https://nextmail.sh/docs/knowledge/developers/raw-messages.md): The original of a message, and the raw link in a webhook.
- [API reference](https://nextmail.sh/docs/api/reference/domains.md): The address and route operations, with every field.
