---
title: "Webhooks for Agent Mailboxes"
author: "Broodnet Team"
date: "2026-07-18"
category: "announcements"
description: "Broodnet webhooks send signed event callbacks for one mailbox or an entire organization, so agents can react to mail without polling."
featuredImage: "https://broodnet.com/img/blog/webhooks/eed9d980ac6527d78e10f70fdec241b8c27733b9.jpg"
---
Polling a mailbox works, but it is an awkward foundation for an agent that may run for days or weeks.

Broodnet webhooks offer a more direct approach. They send a signed HTTPS callback when mail state changes or recipient consent moves through its lifecycle, so your app can start the right worker only when there is something to process.

IMAP and the CLI still handle the messages themselves. The webhook simply tells your system **when something changed and which mailbox needs attention**.

## One endpoint for an organization, or one for a mailbox

You can subscribe at either the organization or mailbox level, depending on where you want the routing decision to happen.

An **organization-wide webhook** receives selected events from across the organization. It works well when you have a shared event queue, want a single operational audit trail, or route work for several agents through one endpoint.

A **mailbox-scoped webhook** follows just one mailbox. This is useful when that mailbox belongs to a particular agent, customer, environment, or workflow and needs its own destination.

The organization-wide scope includes matching mailbox activity as well as organization-level mailbox events. A mailbox-scoped subscription only sees activity for its selected mailbox, and is removed along with that mailbox if you delete it.

The public event set is intentionally small:

- `email_received`
- `email_sent`
- `email_blocked`
- `mailbox_created`
- `mailbox_deleted`
- `outbound_consent_requested`
- `outbound_consent_approved`
- `outbound_consent_denied`

Mailbox-scoped subscriptions can use the three email events and `outbound_consent_requested`. Mailbox lifecycle and recipient consent decisions stay in the organization-wide scope.

## The event is a trigger, not a copy of the email

An `email_received` callback identifies the organization, mailbox, actor, and event involved. It also includes the sender and subject when those details are available, which is usually enough to decide where the work should go.

The full message body is deliberately left out. Once you have accepted the event, fetch the message through IMAP or the Broodnet CLI.

```json
{
  "id": "event_01",
  "type": "email_received",
  "occurredAt": "2026-07-18T09:42:11.000Z",
  "organization": {
    "id": "org_01",
    "name": "Operations",
    "slug": "operations"
  },
  "mailbox": {
    "id": "mailbox_01",
    "name": "Inbound triage",
    "address": "triage@broodnet.com",
    "localPart": "triage",
    "domain": "broodnet.com"
  },
  "actor": {
    "type": "system",
    "id": "dovecot",
    "name": "Dovecot",
    "email": null,
    "address": null
  },
  "data": {
    "from": "alerts@example.com",
    "subject": "Production latency above threshold"
  }
}
```

This keeps callbacks small enough to route quickly without turning them into a second mail-access channel. Reading the message still requires the mailbox's own credentials and permissions.

## Verify before processing

Every webhook gets its own `whsec_` signing secret. Broodnet shows it once, stores it encrypted, and uses it to sign each delivery with the Standard Webhooks 1.0 HMAC-SHA256 format.

Each request includes a stable delivery ID, a timestamp for the current attempt, and its signature:

```typescript
import { Webhook } from "standardwebhooks"

function verifyDelivery(body: Buffer, headers: Record<string, string>, secret: string): unknown {
  return new Webhook(secret).verify(body, headers)
}
```

Pass the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers through to the verifier. It authenticates the exact string `webhook-id.webhook-timestamp.raw-body` and rejects stale timestamps. Hold on to the raw body until verification is finished. If you parse and then re-serialize the JSON first, the bytes can change even when the data looks identical, causing a valid signature check to fail.

Some receivers also require their own credential before they will accept a request. You can add one custom authentication header to a webhook, such as `Authorization: Bearer <token>` or `x-openclaw-token: <token>`. Broodnet stores the value encrypted and includes it with every delivery. This authenticates Broodnet to the receiver; the HMAC signature separately lets the receiver verify that the payload came from Broodnet and was not changed in transit.

Before scheduling anything, store `webhook-id` as an idempotency key. It stays the same across retries while `webhook-timestamp` changes for each attempt. The payload's `type` field is authoritative; `X-Broodnet-Event` carries the same value as a routing convenience.

You can rotate the signing secret from the webhook detail view. Rotation takes effect immediately, so the previous secret will no longer verify new deliveries.

## Failed deliveries remain visible

Once your endpoint has safely accepted an event, return a `2xx` response. Any other response, or a network failure, marks the delivery as failed.

Broodnet retries a failed delivery up to five times, waiting 30 seconds, two minutes, ten minutes, and then thirty minutes between attempts. Unsafe destinations are not retried: webhook URLs must use HTTPS and resolve to a publicly routable address.

Every attempt leaves a useful trail. The delivery log records its status, attempt count, response code, last error, payload, and next retry time. Logs are retained for 30 days by default and can be filtered by status or event type, giving operators somewhere concrete to look when an event was not processed.

You can also pause an endpoint without throwing away its configuration. Pending deliveries are exhausted while it is paused rather than piling up for a later replay.

![What AI agents need is webhooks!](https://broodnet.com/img/blog/webhooks/3dcd81b2549bf2100e8f9140d5e0502922a9b47e.jpg)

## Build an autonomous inbound triage agent

Inbound triage is a natural fit for mailbox-scoped webhooks. Give the triage agent its own mailbox, subscribe its endpoint to `email_received`, and use each callback to start a small, predictable flow:

1. Verify the signature and record `webhook-id`.
2. Add a job containing the mailbox ID and event ID to your queue.
3. Return `2xx` without waiting for the agent to finish.
4. Let the worker retrieve the new message using IMAP or the CLI.
5. Classify the message according to your rules.
6. Escalate, file, summarize, or route the result to the operator.

The webhook does not need to decide what deserves attention. Its job is to provide a reliable starting signal and enough context for your triage system to pick up the right mailbox.

From there, the policy can be as simple or as specific as the work requires. Production alerts might be routed immediately, receipts grouped into a daily accounting batch, suspicious mail sent to a review queue, and newsletters left for a weekly digest. The same inbox can support each of those flows without a polling loop.

Broodnet's outbound policy still governs anything the mailbox sends in response. A triage agent can report to organization members and sibling mailboxes, while external delivery follows the organization's recipient-consent and reply-window rules. If a send is blocked or a recipient changes their decision, `email_blocked` and consent events can bring the workflow back in at the right point.

## Other useful event loops

**Wake an agent when mail arrives.** Send `email_received` events to an agent runtime with an authenticated webhook ingress. [OpenClaw](https://docs.openclaw.ai/automation/cron-jobs#webhooks) can map an incoming payload to a wake event or an isolated agent run. Configure a dedicated OpenClaw hook mapping for the Broodnet payload, point a mailbox-scoped webhook at that mapped endpoint, then set the webhook's custom header to either `Authorization: Bearer <hooks.token>` or `x-openclaw-token: <hooks.token>`. Broodnet supplies the credential on every callback, while the mapping turns the event into instructions for the agent that owns the mailbox.

Keep the OpenClaw Gateway behind a trusted reverse proxy and expose only the hook path over HTTPS. Use a dedicated hook token rather than the Gateway authentication token. If the mapping cannot verify Broodnet's HMAC signature itself, place a small verification service in front of it.

[Claude Code Channels](https://code.claude.com/docs/en/channels-reference) can likewise push external events into a running session, but still needs a public HTTPS receiver that authenticates the callback and translates the Broodnet event into the channel's expected input.

Treat the message as untrusted input even after the callback has been verified. Give an inbox-facing agent only the tools it needs, and keep sensitive actions behind explicit policy or operator review.

**Test real email delivery in CI.** Send organization-wide `email_received` events to one shared test-event receiver. Each run uses a dedicated mailbox, and the receiver correlates the callback by mailbox ID before notifying the waiting job. The test can then retrieve the message through IMAP. One stable HTTPS endpoint can serve the entire suite.

**Process operational mail.** Route invoices and receipts into an accounting queue, turn emailed monitoring alerts into incident jobs, or send support mail to the agent responsible for that customer. The callback starts the workflow; the worker retrieves and processes the message separately.

**Keep an eye on outbound policy.** Send `email_blocked` and recipient-consent events to an operator queue, where your system can distinguish a temporary wait for approval from an outbound attempt that actually needs attention.

**Provision downstream state.** Organization-wide `mailbox_created` and `mailbox_deleted` events can create or remove mailbox-specific indexes, queues, credentials, or monitoring as mailboxes are added and removed.

**Separate customers or environments.** When destinations must stay isolated, attach a webhook to each mailbox. If a central router already owns that separation, one organization-wide endpoint is simpler.

## Start with the event boundary

To get started, open **Webhooks** in the Broodnet dashboard. Choose an organization or mailbox scope, select the events you care about, and enter a public HTTPS endpoint. Add the receiver's authentication header when it requires one. Save the signing secret when it appears, because it will not be shown again.

Once the endpoint is connected, new mail can start the workflow directly. There is no polling interval to tune and no unnecessary mailbox traffic while the agent is idle.

For payloads, headers, signature verification, retries, event-specific fields, and the Standard Webhooks compatibility details, read the [webhook documentation](https://docs.broodnet.com/webhooks/overview/).