---
title: "Add a webhook"
method: POST
path: "/v1/webhooks"
tags: ["webhooks"]
---

# Add a webhook

`POST /v1/webhooks`

## Request

- Base URL: `https://cherami.to`
- URL: `https://cherami.to/v1/webhooks`
- Auth: HTTP bearer

Adds a webhook: an HTTPS URL that receives one or both event types, for every inbox on the account (including ones created later) or for up to 10 listed inboxes. The `201` response includes the signing `secret`; after that it appears only when you [retrieve it](https://cherami.to/docs/api/webhooks/get-webhook-secret).

- `message.received`: a received message is ready to read.
- `message.sent`: the provider accepted a sent message. This is not delivery.

A delivery carries identifiers only (`id`, `type`, `timestamp`, `data.inbox_id`, `data.message_id`): fetch the message through the API, and deduplicate on `id`. Verify the signature on the raw body and answer with a 2xx within 15 seconds once you've stored the event: see [Verify and acknowledge](https://cherami.to/docs/guides/webhooks#verify-and-acknowledge).

If the response is lost, [list webhooks](https://cherami.to/docs/api/webhooks/list-webhooks), find the URL and retrieve its secret rather than adding it again.

## Request body

- CreateWebhook
  - `url` string, required: HTTPS URL on a public host, without credentials or a fragment; at most 2048 characters. It can't be changed later: to move it, add a new webhook and delete this one.
  - `event_types` WebhookEventType[], required
  - `inbox_ids` string[]: Up to 10 of the account's live inboxes. Empty or omitted means every inbox, including ones created later.

## Response `201`

Created.

- CreatedWebhook: status is active (delivering), disabled (not delivering until you set enabled to true; you or repeated delivery failures turned it off), pending (creation or removal not yet confirmed; read it again shortly) or unknown (the configuration couldn't be read and its fields are null; if it stays unknown, delete the webhook and add it again). pending_operation names the unconfirmed work.
  - `id` string, required: Cherami resource ID.
  - `created_at` string, date-time, required: UTC timestamp with milliseconds.
  - `url` string, nullable, required: Destination URL; null while status is pending or unknown.
  - `event_types` WebhookEventType[], nullable, required
  - `inbox_ids` string[], nullable, required: Scoped inboxes; empty means every inbox.
  - `deleted_inbox_ids` string[], required: Scoped inboxes that have since been deleted. They match nothing; edit inbox_ids to remove them.
  - `enabled` boolean, nullable, required
  - `updated_at` string, date-time, nullable, required: UTC timestamp with milliseconds.
  - `status` 'active' | 'disabled' | 'pending' | 'unknown', required
  - `pending_operation` object, nullable, required
    - `kind` 'create' | 'delete' | 'rotate', required
    - `started_at` string, date-time, required: UTC timestamp with milliseconds.
  - `secret_rotated_at` string, date-time, nullable, required: UTC timestamp with milliseconds.
  - `previous_secret_expires_at` string, date-time, nullable, required: UTC timestamp with milliseconds.
  - `secret` string, required: Signing secret for Standard Webhooks verification (whsec_...). Returned only on creation, retrieval and rotation.

## Other responses

- `400`: `invalid_json`: Send a valid UTF-8 JSON object, not an array or scalar. `invalid_webhook`: Adding a webhook takes only `url`, `event_types` and `inbox_ids`. `invalid_webhook_url`: Use an https URL with a public hostname (not an IP address), without credentials or a fragment. `invalid_webhook_event_types`: Supply a nonempty array of `message.received`, `message.sent` or both. `invalid_webhook_inbox_ids`: Supply `inbox_ids` as an array of at most 10 inbox IDs, or an empty array for every inbox. `unknown_webhook_inbox`: An `inbox_ids` entry is not a live inbox of this account. Remove the IDs listed in `error.details.inbox_ids`.
- `401`: `unauthorized`: Send a valid API key as `Authorization: Bearer <key>`. If the key stopped working, get a new one through [recovery](https://cherami.to/docs/guides/recovery).
- `413`: `body_too_large`: Reduce the JSON request to the operation's body limit.
- `415`: `unsupported_media_type`: Send `Content-Type: application/json`.
- `503`: `webhook_provider_unavailable`: The request could not be completed, and nothing changed. Retry later. `webhook_creation_uncertain`: The new webhook was not confirmed and is listed as `pending`. Within a few minutes the webhook in `error.details.webhook_id` becomes `active`, and you [retrieve its secret](https://cherami.to/docs/api/webhooks/get-webhook-secret), or leaves the list, and you add it again. `webhook_secret_unavailable`: The signing secret could not be read; retry the read later. When `error.details.webhook_id` is present the webhook was added: retrieve its secret rather than adding it again. `webhooks_unavailable`: Webhook management failed. If it was a change, it may have been applied: list or read the webhooks before repeating it.

---

[API](https://skmtc.dev/cherami/apis/cherami-http-api.md) · [All operations](https://skmtc.dev/cherami/apis/cherami-http-api/llms.txt) · [OpenAPI document](https://skmtc.dev/cherami/apis/cherami-http-api/revisions/7bd993d538af?raw)
