# Rotate the signing secret

POST /v1/webhooks/{webhook_id}/secret/rotate

Source: https://cherami.to/docs/api/webhooks/rotate-webhook-secret



{/* Generated from apps/web/openapi. Edit the contract, not this file. */}

`POST /v1/webhooks/{webhook_id}/secret/rotate`

Requires an [API key](https://cherami.to/docs/api#authentication): `Authorization: Bearer YOUR_CREDENTIAL`.

Generates a new signing secret and returns it. The body is optional: by default the replaced secret stays valid for 24 hours so a receiver can switch without rejecting signatures; `immediate: true` expires the replaced secret at once.

Only the replaced secret is affected. A secret from an earlier rotation that is still inside its own 24-hour overlap keeps that expiry; immediate rotation does not revoke every earlier secret. If every previous secret must stop signing now, delete the webhook and add it again.

An uncertain response (`503 webhook_rotation_uncertain`) is safe to repeat: the request is replayed identically and no second rotation happens. The webhook's `secret_rotated_at` and `previous_secret_expires_at` show whether a rotation took effect before you repeat it.

## Parameters [#parameters]

| Parameter    | Location | Required | Type   | Meaning                                        |
| ------------ | -------- | -------- | ------ | ---------------------------------------------- |
| `webhook_id` | path     | Yes      | string | Owned Cherami resource ID returned by the API. |

## Request body [#request-body]

JSON object. Total UTF-8 request body limit: 4096 bytes.

### Schema: RotateWebhookSecret [#schema-rotatewebhooksecret]

| Field       | Required | Type    | Meaning and constraints                                                                                                                                           |
| ----------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `immediate` | No       | boolean | true expires the replaced secret at once; otherwise it keeps signing for 24 hours alongside the new one. Only the replaced secret is affected. `default`: `false` |

Unknown fields are rejected.

## curl example [#curl-example]

Replace resource-ID placeholders with returned IDs. Supply `CHERAMI_API_KEY` through your private shell environment.

Save the intended payload as a private `request.json` file and replace illustrative values. For an uncertain response, follow the [operation-specific recovery rules](https://cherami.to/docs/api/errors#retry-by-operation).

```json
{
  "immediate": false
}
```

```sh
curl --silent --show-error --include --request POST \
  "https://cherami.to/v1/webhooks/WEBHOOK_ID/secret/rotate" \
  --header "Authorization: Bearer $CHERAMI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json
```

## Responses [#responses]

### HTTP 200 [#http-200]

Successful operation; inspect resource state and outcome fields.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[WebhookRotation](#schema-webhookrotation)

```json
{
  "id": "55555555-5555-4555-8555-555555555555",
  "secret": "whsec_EXAMPLE_NOT_A_REAL_SECRET",
  "secret_rotated_at": "2026-10-01T00:00:00.000Z",
  "previous_secret_expires_at": "2026-10-02T00:00:00.000Z"
}
```

### HTTP 400 [#http-400]

`invalid_json`: Send a valid UTF-8 JSON object, not an array or scalar.

`invalid_webhook_rotation`: The rotation body accepts only an optional boolean immediate field.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 401 [#http-401]

`unauthorized`: Provide a valid bearer credential. Use [human-approved recovery](https://cherami.to/docs/guides/recovery) if access is lost.

* `X-Request-ID`: Support correlation ID, not an idempotency key.
* `WWW-Authenticate`: `"Bearer"`

Content type: `application/json`.

[Error](#schema-error)

### HTTP 404 [#http-404]

`not_found`: Resource is absent or inaccessible to this account. Reply targets must be in the sending inbox.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 409 [#http-409]

`webhook_operation_in_progress`: Another change to this webhook is in flight. Retry after a minute; read the webhook first if an earlier change was uncertain.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 413 [#http-413]

`body_too_large`: Reduce the JSON request to the endpoint's body limit.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 415 [#http-415]

`unsupported_media_type`: Send `Content-Type: application/json`.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 503 [#http-503]

`webhook_provider_unavailable`: The webhook provider refused or could not take the request and nothing was applied. Retry later.

`webhook_rotation_uncertain`: The provider did not confirm the rotation. Repeat the rotation request: it replays the same rotation and never rotates twice.

`webhooks_unavailable`: Webhook management is unavailable or the operation's outcome is unknown. Read or list webhooks before repeating a change; adding again can create a duplicate.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

## Schema: WebhookRotation [#schema-webhookrotation]

| Field                        | Required | Type   | Meaning and constraints                                                                                                                          |
| ---------------------------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                         | Yes      | string | Cherami resource ID, distinct from the RFC Message-ID. Use the returned value.                                                                   |
| `secret`                     | Yes      | string | Signing secret for Standard Webhooks verification (whsec\_ prefix). Returned on creation, explicit retrieval and rotation only. Store privately. |
| `secret_rotated_at`          | Yes      | string | UTC service instant with milliseconds and Z suffix. `format`: `date-time` `pattern`: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$`             |
| `previous_secret_expires_at` | Yes      | string | UTC service instant with milliseconds and Z suffix. `format`: `date-time` `pattern`: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$`             |

## Schema: Error [#schema-error]

| Field   | Required | Type   | Meaning and constraints |
| ------- | -------- | ------ | ----------------------- |
| `error` | Yes      | object |                         |

### `error` fields [#error-fields]

| Field     | Required | Type   | Meaning and constraints                                                                       |
| --------- | -------- | ------ | --------------------------------------------------------------------------------------------- |
| `code`    | Yes      | string | Programmatic error code. Handle unrecognized codes by status and operation-specific recovery. |
| `message` | Yes      | string | Human-readable context, not a stable string to match.                                         |

[HTTP conventions, errors and pagination](https://cherami.to/docs/api/errors) · [Download OpenAPI 3.1](https://cherami.to/openapi.json)
