POST /api/v1/webhook-subscriptions/{subscriptionId}/rotate-secret
Replaces the secret used to sign requests. The subscription goes back to
pending_validation and stops receiving deliveries until it is
verified with the new
secret.
| Role in the organization | This endpoint |
|---|---|
OWNER | Allowed |
ADMIN | Allowed |
MEMBER | Refused — 403 |
| Requirement | Rule for this endpoint |
|---|---|
| Business role | None beyond the organization role |
| Resource scope | The subscription belongs to the key's organization |
| Participation | None |
| Resource state | Any status |
| Parameter | Type | Required | Description |
|---|---|---|---|
subscriptionId | UUID | yes | Webhook subscription of the key's organization |
| Header | Required | Description |
|---|---|---|
x-api-key | yes | API key created for this organization |
Idempotency-Key | yes | Stable key for this command; a replay returns the first result |
X-Correlation-Id | no | Caller correlation id, returned or generated |
| Field | Type | Required | Nullable | Description |
|---|---|---|---|---|
secret | string | yes | no | New secret, 16 to 512 characters; never returned |
{"secret": "whsec_2e8b4d6f0a1c3e5a7b9d"}curl -X POST "$COHORTE_API_BASE_URL/api/v1/webhook-subscriptions/$SUBSCRIPTION_ID/rotate-secret" \
-H "x-api-key: $COHORTE_API_KEY" \
-H "Idempotency-Key: webhook-crm-sync-rotate-2026-09" \
-H "Content-Type: application/json" \
-d '{"secret":"whsec_2e8b4d6f0a1c3e5a7b9d"}'200 OK — the secret was replaced; data.status is pending_validation. replayed: true when the key was already used.400 Bad Request — invalid_request: missing Idempotency-Key, invalid JSON, or a secret out of bounds; details names the field.401 Unauthorized — unauthorized: the key is missing, invalid, expired or no longer attached to a member.403 Forbidden — forbidden: the bearer is not OWNER or ADMIN.404 Not Found — not_found: the subscription is unknown or belongs to another organization.500 Internal Server Error — internal_error: unexpected failure.| Field | Type | Nullable | Description |
|---|---|---|---|
success | boolean | no | true for a successful request |
replayed | boolean | no | true when the idempotency key was already used |
data.id | UUID | no | Subscription identifier |
data.name | string | no | Display name |
data.endpointUrl | string | no | Destination as scheme://host[:port]/path; the query string is never returned |
data.eventNames | string[] | no | Subscribed event names |
data.status | string | no | pending_validation, validating, active, paused or validation_failed |
data.lastValidatedAt | ISO 8601 string | yes | Last successful verification, in UTC |
data.lastValidationError | string | yes | Why the last verification failed |
data.secretConfigured | boolean | no | Always true; the secret itself is never returned |
data.createdAt | ISO 8601 string | no | Creation time, in UTC |
data.updatedAt | ISO 8601 string | no | Last change, in UTC |
correlationId | string | no | Correlation id for support |
{
"success": true,
"replayed": false,
"data": {
"id": "6b1e2c3d-4f50-4a61-8b72-93a4b5c6d7e8",
"name": "CRM sync",
"endpointUrl": "https://hooks.example.com/cohorte",
"eventNames": ["enrollment.created", "enrollment.closed"],
"status": "pending_validation",
"lastValidatedAt": null,
"lastValidationError": null,
"secretConfigured": true,
"createdAt": "2026-09-20T09:10:00.000Z",
"updatedAt": "2026-09-22T08:00:00.000Z"
},
"correlationId": "b7d3e1f0-2a4c-4e8b-9c1d-0f2e3a4b5c6d"
}Idempotency-Key returns the first result and does not
replace the secret a second time. The rotation is recorded in the
organization's audit trail, without the secret.