---
title: "Create a webhook"
method: POST
path: "/webhooks"
tags: ["Webhooks"]
---

# Create a webhook

`POST /webhooks`

## Request

- Base URL: `https://shipmail.to/api/v1`
- URL: `https://shipmail.to/api/v1/webhooks`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://shipmail.to/api/auth/oauth2/authorize`, token URL `https://shipmail.to/api/auth/oauth2/token`)

Register a webhook endpoint to receive event notifications. Maximum 10 webhooks per account. The secret is only returned at creation time. Pass `mailbox_ids` or `domain_ids` to receive mailbox and domain events for only those resources; unknown IDs are rejected with a 422 whose details code is `invalid_mailbox_ids` or `invalid_domain_ids`. Pass `authorization` to send that value as the `Authorization` header of every delivery; it is write-only. Pass `standard_webhooks_secret` to sign every delivery the way Standard Webhooks receivers check; it is write-only too.

## Headers

- `X-Shipmail-Organization` string

## Request body

- CreateWebhookRequest
  - `url` string, uri, required: HTTPS endpoint URL.
  - `events` WebhookEventType[], required: Event types to subscribe to.
  - `description` string: Human-readable description.
  - `mailbox_ids` string[], nullable: Only deliver events for these mailboxes (plus mailboxes of `domain_ids`). Omit or null for every mailbox. Organization events are always delivered.
  - `domain_ids` string[], nullable: Only deliver events for these domains and their mailboxes. Omit or null for every domain.
  - `authorization` string, nullable: Sent verbatim as the `Authorization` header of every delivery to this webhook, including test events and replays. For example `Bearer <key>`. Printable ASCII only. Stored encrypted and never returned; responses carry `has_authorization` instead. Signature headers are sent as usual.
  - `standard_webhooks_secret` string, nullable: A Standard Webhooks signing secret from the receiving service, such as a Grok automation webhook trigger: `whsec_` followed by a 24 to 64 byte key in standard base64 (with or without padding) or base64url. When set, every delivery, test event and replay also carries `webhook-id` (the event id, the same on every retry and replay), `webhook-timestamp` and `webhook-signature`. Stored encrypted and never returned; responses carry `has_standard_webhooks_secret` instead. The `X-Shipmail-Signature` headers are sent as usual.
  - `include_reply_text` boolean: Add `reply_text`, the sender's new text without quoted history or signature, to `message.received` deliveries. Turning it on needs an API key that also grants `messages:read`. Otherwise the request fails with 403. The same check applies to a URL, event, mailbox or domain change on a webhook that includes it.
  - `authenticated_senders_only` boolean: Deliver `message.received` only when the sender is authenticated (`sender_authenticated: true`): the sender passed DMARC, or Shipmail's own record shows a Shipmail mailbox sent the message. Other events are unaffected. Use it for agents that act on mail and must not trust a forged From address.
  - `allowed_senders` string[], nullable: Deliver `message.received` only when `data.from.address` equals an address entry (`jane@customer.com`) or its domain equals a domain entry (`customer.com`) exactly. Subdomains do not match, and a message without a From address never matches. Entries are trimmed, lowercased and deduplicated. Null or `[]` delivers every sender. Combines with `authenticated_senders_only`; other events are unaffected.

## Response `201`

Webhook created. The secret field is only included in this response.

- object
  - `object` 'webhook', required
  - `id` string, required
  - `url` string, uri, required
  - `events` WebhookEventType[], required
  - `active` boolean, required
  - `description` string, nullable, required
  - `mailbox_ids` string[], nullable, required: Mailboxes this webhook is limited to, or null. See Scoping in the webhooks guide.
  - `domain_ids` string[], nullable, required: Domains this webhook is limited to, or null. When both lists are null the webhook receives events for every mailbox.
  - `has_authorization` boolean, required: True when deliveries carry an `Authorization` header set through `authorization`. The value itself is never returned.
  - `has_standard_webhooks_secret` boolean, required: True when deliveries carry Standard Webhooks signatures (`webhook-id`, `webhook-timestamp`, `webhook-signature`) from a secret set through `standard_webhooks_secret`. The secret itself is never returned.
  - `include_reply_text` boolean, required: True when `message.received` deliveries carry `reply_text`.
  - `authenticated_senders_only` boolean, required: True when `message.received` is delivered only for messages whose sender is authenticated (`sender_authenticated: true`): the sender passed DMARC, or Shipmail's own record shows a Shipmail mailbox sent the message.
  - `allowed_senders` string[], nullable, required: Addresses and domains `message.received` is limited to, lowercased. Null when every sender is delivered.
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `secret` string, required: HMAC signing secret. Only returned at creation time.

## Other responses

- `422`: Validation failed.
- `429`: Rate limit exceeded.

---

[API](https://skmtc.dev/shipmail/apis/shipmail-api.md) · [All operations](https://skmtc.dev/shipmail/apis/shipmail-api/llms.txt) · [OpenAPI document](https://skmtc.dev/shipmail/apis/shipmail-api/revisions/2c23e59d7e30?raw)
