Webhooks
Webhooks send signed JSON notifications to your service when activity happens in Broodnet. Use them to start an agent workflow when mail arrives, record policy decisions, or keep another system in sync without polling.
Broodnet adheres to the Standard Webhooks 1.0 wire format for symmetric signing and verification. This covers the whsec_ secret format, the three webhook-* headers, and how each signature is calculated. Broodnet’s event names, payloads, endpoint scopes, delivery history, and retry policy remain specific to Broodnet.
Endpoint scope
Section titled “Endpoint scope”Choose the scope when you create an endpoint:
| Scope | Configuration | Receives |
|---|---|---|
| Organization | Omit mailboxId |
Every subscribed event in the active organization |
| Mailbox | Set mailboxId |
Subscribed activity associated with that mailbox |
Organization endpoints can subscribe to every webhook event. Mailbox endpoints can subscribe to email_received, email_sent, email_blocked, and mailbox-originated outbound_consent_requested events.
An event associated with a mailbox is delivered to both matching organization endpoints and matching endpoints for that mailbox. Organization-level events are delivered only to organization endpoints.
Deleting a mailbox also deletes its mailbox-scoped endpoints. Subscribe to mailbox_deleted on an organization endpoint if you need a final notification after a mailbox is removed.
Event catalog
Section titled “Event catalog”| Event | Organization endpoint | Mailbox endpoint |
|---|---|---|
email_received |
Yes | Yes |
email_sent |
Yes | Yes |
email_blocked |
Yes | Yes |
mailbox_created |
Yes | No |
mailbox_deleted |
Yes | No |
outbound_consent_requested |
Yes | When the request is associated with that mailbox |
outbound_consent_approved |
Yes | No; consent decisions are organization-level events |
outbound_consent_denied |
Yes | No; consent decisions are organization-level events |
Create an endpoint
Section titled “Create an endpoint”Webhook management accepts a bearer session or an organization API key. API keys need webhook:create to create endpoints, webhook:read to read endpoints and deliveries, and webhook:manage to update, rotate, or delete them.
Create an organization endpoint:
curl https://api.broodnet.com/api/webhooks \ --request POST \ --header "x-organization-key: brn_your_key_here" \ --header "content-type: application/json" \ --data '{ "url": "https://agents.example.com/broodnet/events", "eventTypes": ["email_received", "email_blocked"], "description": "Agent triage" }'To restrict the endpoint to one mailbox, include its ID:
{ "url": "https://agents.example.com/broodnet/triage", "eventTypes": ["email_received"], "mailboxId": "mailbox-id", "description": "Support inbox triage"}If the destination requires a bearer token or API key, add one authentication header:
{ "url": "https://agents.example.com/broodnet/triage", "eventTypes": ["email_received"], "mailboxId": "mailbox-id", "authHeader": { "name": "Authorization", "value": "Bearer destination-token" }}Broodnet encrypts the header value and includes it with every delivery. Read responses expose authHeaderName so you can see which header is configured, but never return its value.
Destinations must use HTTPS, resolve successfully, and resolve only to publicly routable addresses. At least one supported eventTypes value is required.
The create response includes a Standard Webhooks signing secret with the whsec_ prefix. Store it immediately: Broodnet returns the plaintext secret only when the endpoint is created or its secret is rotated.
Manage an endpoint
Section titled “Manage an endpoint”The webhook API provides:
| Method | Path | Purpose |
|---|---|---|
GET |
/api/webhooks |
List endpoints in the active organization |
GET |
/api/webhooks/:id |
Get one endpoint |
PATCH |
/api/webhooks/:id |
Change its URL, subscriptions, active state, description, or authentication header |
POST |
/api/webhooks/:id/rotate-secret |
Replace its signing secret |
DELETE |
/api/webhooks/:id |
Delete the endpoint and cancel its pending deliveries |
Set active to false to stop new deliveries without deleting the configuration. Deliveries already waiting to run are marked exhausted when processing sees an inactive endpoint.
The scope cannot be changed after creation. Create another endpoint if you need to move between organization and mailbox scope.
When updating an endpoint, omit authHeader to keep the current header, send a new name and value to replace it, or send null to remove it. Header names must be valid HTTP tokens. Broodnet rejects transport and delivery headers such as Host, Content-Type, User-Agent, webhook-*, X-Forwarded-*, and X-Broodnet-*.
Payload envelope
Section titled “Payload envelope”Every event uses the same envelope. The data object changes by event type.
{ "id": "audit-event-id", "type": "email_received", "occurredAt": "2026-07-18T10:00:00.000Z", "organization": { "id": "organization-id", "name": "Acme Agents", "slug": "acme-agents" }, "mailbox": { "id": "mailbox-id", "name": "Support", "address": "support@acme.broodnet.com", "localPart": "support", "domain": "acme.broodnet.com" }, "actor": { "type": "system", "id": "dovecot", "name": "Dovecot", "email": null, "address": null }, "data": { "from": "customer@example.com", "subject": "Account access" }}mailbox is null when the event has no mailbox context. Actor fields that do not apply to the actor type are also null.
The envelope id identifies the underlying event. The webhook-id header identifies one endpoint’s delivery of that event and remains stable when Broodnet retries it. Store webhook-id as your idempotency key.
Availability and limits
Section titled “Availability and limits”Webhooks are available on paid plans. The number of endpoints depends on the organization’s current plan; see the pricing page for current limits.
Delivery volume operates under fair use rather than a published hard call quota. A few thousand deliveries are a normal use case. Broodnet may throttle or suspend delivery volume that is abusive or degrades the service for other organizations.
When an organization moves to a plan without webhook support, Broodnet suspends new event fan-out and exhausts deliveries that were already waiting to run. Existing endpoint configuration is preserved. New matching events resume after the organization upgrades to a plan that includes webhooks.
Verify signatures
Section titled “Verify signatures”The optional authentication header proves Broodnet’s identity to your destination. Signature verification serves a separate purpose: it lets your handler verify that Broodnet created the payload and that it was not changed in transit.
Following the Standard Webhooks symmetric signature format, Broodnet signs the delivery ID, attempt timestamp, and exact request body with HMAC-SHA256 using the endpoint’s signing secret.
| Header | Value |
|---|---|
webhook-id |
Delivery identifier; unchanged across retries |
webhook-timestamp |
Integer Unix timestamp for this delivery attempt |
webhook-signature |
v1,<base64 HMAC-SHA256> |
X-Broodnet-Event |
Event type convenience header; the payload is authoritative |
Content-Type |
application/json |
User-Agent |
Broodnet-Webhooks/1.0 |
The signed content is:
webhook-id.webhook-timestamp.raw-bodyUse a Standard Webhooks receiver library to verify the signature before parsing or processing the payload:
import { Webhook } from "standardwebhooks"
export function verifyBroodnetWebhook(rawBody: Buffer, headers: Record<string, string>, secret: string): unknown { return new Webhook(secret).verify(rawBody, headers)}Pass the webhook-id, webhook-timestamp, and webhook-signature request headers through unchanged. The verifier authenticates all three inputs and rejects attempts outside its timestamp tolerance.
Do not stringify a parsed object for verification. Whitespace and property order are part of the signed bytes. Verify first, then record webhook-id before scheduling work so a retry cannot create a duplicate job.
Respond and retry
Section titled “Respond and retry”Return any 2xx response to acknowledge a delivery. Broodnet treats other status codes, connection failures, and requests that take longer than 15 seconds as failures.
A delivery is attempted up to five times. After the first failed attempt, retries are scheduled after:
- 30 seconds
- 2 minutes
- 10 minutes
- 30 minutes
Endpoint processing runs on an interval, so actual delivery time can be slightly later. A destination that becomes unsafe to contact is exhausted immediately rather than retried.
Keep handlers short: verify the signature, persist or enqueue the event, return 2xx, then do expensive work asynchronously.
Inspect delivery history
Section titled “Inspect delivery history”List delivery records for an endpoint:
curl "https://api.broodnet.com/api/webhooks/webhook-id/deliveries?status=failed&limit=50" \ --header "x-organization-key: brn_your_key_here"You can filter by status (pending, success, failed, or exhausted) and by eventType. Results are paginated with limit and offset; limit defaults to 50 and cannot exceed 200.
Each record contains the immutable payload snapshot, attempt count, maximum attempts, last error, response code, next retry time, and creation time. Delivery history is retained for 30 days.