Rotate a webhook endpoint's signing secret
The webhook_endpoints.rotate_secret operation: POST /api/v1/webhook_endpoints/{id}/rotate_secret in the REST API and the webhook_endpoints_rotate_secret tool in the MCP server.
/api/v1/webhook_endpoints/{id}/rotate_secretReplaces the endpoint's signing secret and returns the new one in secret. It is shown only once.
- Without
overlapSeconds(or with 0) the old secret stops at once: deliveries are signed with the new one only, and receivers still checking the old one fail until they switch (failed deliveries are retried). - With
overlapSeconds(up to 86400) the old secret keeps signing next to the new one untilpreviousSecretExpiresAt: each delivery carries twov1=signatures, and a receiver accepts it when either matches. Deploy the new secret, then let the overlap run out.
Rotating again ends an earlier overlap at once.
At a glance
- REST:
POST /api/v1/webhook_endpoints/{id}/rotate_secret - MCP tool:
webhook_endpoints_rotate_secret - Operation ID:
webhook_endpoints.rotate_secret - Scope: Brand
- Permission:
webhooks:admin - Risk tier:
security - Dry run: Supported (send
Ownrecord-Dry-Run: true) - Idempotency:
Idempotency-Keyrequired - Modes: Test and live
- Authentication: API key with the
webhooks:adminscope - Since: M4
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | we_… ID |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
overlapSeconds | integer (0 to 86400) | No | Seconds the old secret keeps signing next to the new one (0–86400, default 0: it stops at once). During the overlap each delivery carries two v1 signatures. |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | we_… ID | |
object | "webhook_endpoint" | |
brandId | brd_… ID | |
livemode | boolean | |
url | string | |
description | string or null | |
events | array of string | |
enabled | boolean | |
disabledReason | one of manual, failing or null | |
disabledAt | date-time or null | |
failingSince | date-time or null | |
secretRotatedAt | date-time or null | |
previousSecretExpiresAt | date-time or null | |
metadata | object | |
createdAt | date-time | |
updatedAt | date-time | |
secret | string |
Examples
Rotate with a one-hour overlap
POST /api/v1/webhook_endpoints/we_01m4tx5nj8emwbyf7y9vcf47z0/rotate_secret
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"overlapSeconds": 3600
}
Response (200):
{
"id": "we_01m4tx5nj8emwbyf7y9vcf47z0",
"object": "webhook_endpoint",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"url": "https://example.com/webhooks/ownrecord",
"description": "Order fulfilment",
"events": ["customer.created", "checkout.session.completed"],
"enabled": true,
"disabledReason": null,
"disabledAt": null,
"failingSince": null,
"secretRotatedAt": "2026-10-14T09:30:00.000Z",
"previousSecretExpiresAt": "2026-10-14T10:30:00.000Z",
"metadata": {},
"createdAt": "2026-10-13T12:00:00.000Z",
"updatedAt": "2026-10-14T09:30:00.000Z",
"secret": "whsec_ExampleOnlyNotARealSigningSecret0000000"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | overlapSeconds isn't a whole number from 0 to 86400. | Fix the fields listed in details.issues and send the request again. |
resource_not_found | 404 | No endpoint with this ID is visible to the request. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
version_conflict | 409 | The endpoint changed at the same time; retry. | Fetch the object again and retry the change. |
permission_denied | 403 | You can't manage this brand's webhooks. | Ask an organization owner or admin for a role or scope that allows this action. |
It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, scope_required, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.