# POST /v1/webhooks/{id}/secrets

Create a signing secret

Adds a second live secret to the webhook and answers its value once; it is never shown again. From then on every delivery carries one signature per live secret, the newest first, so the receiver can switch to the new secret and the old one can be deleted. A webhook holds at most two live secrets.

Takes a server key (`sk_…`, or `sk_test_…` on a test server) as a bearer token. TypeScript: `sendora.webhooks.createSecret(webhookId)`. Python: `sendora.webhooks.create_secret(webhook_id)`.

## Parameters

- `id` (path, required, string (uuid)): The webhook id.

## Responses

### 201 The new secret, its value shown this once.

- `secretId` (string (uuid), required)
- `createdAt` (string (date-time), required)
- `secret` (string, required): The secret itself, shown this once.

Example:

```json
{
  "secretId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
  "createdAt": "2026-09-20T12:00:00.000Z",
  "secret": "whsec_0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d"
}
```

## Errors

Every error answers `error`, the code, and `message`, a sentence for a person. A code that adds fields is shown in full below the table.

| Code | Status | Meaning |
| --- | --- | --- |
| `unauthorized` | 401 | The key is missing, malformed or revoked. |
| `wrong_token_kind` | 403 | The key is of the other kind: a server key (sk_) where an account key (ak_) is needed, or the reverse. The message names the kind the operation takes. |
| `not_found` | 404 | No such webhook of this server. |
| `secret_limit` | 409 | The webhook already holds two live secrets: one in use and one to roll to. Delete one before creating another. |

### `secret_limit`

- `max` (integer, required): Live secrets a webhook may hold at once.

Example:

```json
{
  "error": "secret_limit",
  "message": "The webhook may hold at most 2 live secrets; delete one before creating another.",
  "max": 2
}
```
