# Create or replace the project webhook

> Create or replace the webhook. url must be HTTPS. The signature scheme defaults to v2. A webhook cannot move straight from v1 to v2, or from v2 to v1 (400): a receiver verifying the current scheme would break on the next delivery, so both directions go through v1+v2. Replacing keeps the secret.

`PUT https://api.zavu.dev/v2/webhook`

Create or replace the webhook. `url` must be HTTPS. The signature scheme defaults to `v2`. A webhook cannot move straight from `v1` to `v2`, or from `v2` to `v1` (`400`): a receiver verifying the current scheme would break on the next delivery, so both directions go through `v1+v2`. Replacing keeps the secret.

Pass `disableSenderWebhooks: true` to switch off the /v1 sender webhooks of this project in the same call, so each event arrives once.

## Body

- `url` (string<uri>, required): HTTPS only.
- `events` (string[], required): Any event in `WebhookEvent`. An unknown value is refused with `400` and `details.allowed`. One of: `message.queued`, `message.sent`, `message.delivered`, `message.read`, `message.failed`, `message.inbound`, `message.unsupported`, `broadcast.status_changed`, `conversation.new`, `contact.identity_linked`, `template.status_changed`, `invitation.status_changed`, `call.initiated`, `call.answered`, `call.completed`, `call.failed`, `domain.verified`, `domain.failed`.
- `active` (boolean)
- `signatureVersion` (string): Which `X-Zavu-Signature` scheme this receiver is sent. The header is `t=<unix seconds>` followed by the signatures. - `v1`: `v1=HMAC_SHA256(secret, body)`. - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the default for a new /v2 webhook. It signs the timestamp together with the body. - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver reading either one works. Moving straight between `v1` and `v2`, in either direction, returns `400`. Set `v1+v2` first. See https://www.zavu.dev/docs/guides/receiving-messages/signature-migration One of: `v1`, `v1+v2`, `v2`.
- `disableSenderWebhooks` (boolean): Switch off the /v1 sender webhooks of this project in the same call, so each event arrives once. They are only switched off (`webhookActive: false` on /v1); URL, events and secret stay, so /v1 can turn one back on. Off by default: the /v2 webhook is added next to them and both receive every event until you decide. `senderWebhooks` on the response lists what is still on.

## Responses

### 200

Replaced. `secret` is not returned.

- `webhook` (object, required): The one webhook for everything in the project. Stored apart from the /v1 project webhook (`/v1/invitations/webhook`); neither API reads or writes the other's.
  - `url` (string<uri>, required)
  - `events` (string[], required): Any event in `WebhookEvent`, including `message.*`, `conversation.new` and `call.*`. One of: `message.queued`, `message.sent`, `message.delivered`, `message.read`, `message.failed`, `message.inbound`, `message.unsupported`, `broadcast.status_changed`, `conversation.new`, `contact.identity_linked`, `template.status_changed`, `invitation.status_changed`, `call.initiated`, `call.answered`, `call.completed`, `call.failed`, `domain.verified`, `domain.failed`.
  - `active` (boolean, required)
  - `signatureVersion` (string, required): Which `X-Zavu-Signature` scheme this receiver is sent. The header is `t=<unix seconds>` followed by the signatures. - `v1`: `v1=HMAC_SHA256(secret, body)`. - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the default for a new /v2 webhook. It signs the timestamp together with the body. - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver reading either one works. Moving straight between `v1` and `v2`, in either direction, returns `400`. Set `v1+v2` first. See https://www.zavu.dev/docs/guides/receiving-messages/signature-migration One of: `v1`, `v1+v2`, `v2`.
  - `secret` (string): Returned only by `PUT /v2/webhook` when it creates the webhook, and by `POST /v2/webhook/secret`.
- `senderWebhooks` (object[], required)
  - `address` (string, required, nullable): The sender's first address; null for a sender with no connected transport.
  - `url` (string<uri>, required)
  - `v1SenderId` (string, required): Read-only; the sender to change on /v1.
- `disabledSenderWebhooks` (integer, required): How many /v1 sender webhooks this call switched off.

### 201

Created. `webhook.secret` is returned once.

- `webhook` (object, required): The one webhook for everything in the project. Stored apart from the /v1 project webhook (`/v1/invitations/webhook`); neither API reads or writes the other's.
  - `url` (string<uri>, required)
  - `events` (string[], required): Any event in `WebhookEvent`, including `message.*`, `conversation.new` and `call.*`. One of: `message.queued`, `message.sent`, `message.delivered`, `message.read`, `message.failed`, `message.inbound`, `message.unsupported`, `broadcast.status_changed`, `conversation.new`, `contact.identity_linked`, `template.status_changed`, `invitation.status_changed`, `call.initiated`, `call.answered`, `call.completed`, `call.failed`, `domain.verified`, `domain.failed`.
  - `active` (boolean, required)
  - `signatureVersion` (string, required): Which `X-Zavu-Signature` scheme this receiver is sent. The header is `t=<unix seconds>` followed by the signatures. - `v1`: `v1=HMAC_SHA256(secret, body)`. - `v2`: `v2=HMAC_SHA256(secret, "{t}.{body}")`. The current scheme, and the default for a new /v2 webhook. It signs the timestamp together with the body. - `v1+v2`: both signatures, sharing one `t`. The migration setting: a receiver reading either one works. Moving straight between `v1` and `v2`, in either direction, returns `400`. Set `v1+v2` first. See https://www.zavu.dev/docs/guides/receiving-messages/signature-migration One of: `v1`, `v1+v2`, `v2`.
  - `secret` (string): Returned only by `PUT /v2/webhook` when it creates the webhook, and by `POST /v2/webhook/secret`.
- `senderWebhooks` (object[], required)
  - `address` (string, required, nullable): The sender's first address; null for a sender with no connected transport.
  - `url` (string<uri>, required)
  - `v1SenderId` (string, required): Read-only; the sender to change on /v1.
- `disabledSenderWebhooks` (integer, required): How many /v1 sender webhooks this call switched off.

### 400

Invalid URL, an event that does not exist (`details.unknown`, `details.allowed`, and `details.errors` naming each by index), or `v1` to `v2` / `v2` to `v1` directly (go through `v1+v2`; `details.errors[0].path` is `["signatureVersion"]`).

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

### 401

Unauthorized.

- `code` (string, required)
- `message` (string, required)
- `details` (object): Machine-readable context. Keys that mean the same thing everywhere: - `errors`: present on every `400 invalid_request` for an invalid body or query parameter, `[{path, message, ...}]`, `path` naming the field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`). - `recommendation`: one sentence saying what to do next. - `dashboardUrl`: the dashboard screen that fixes it. - `connect`: on `409 no_channel`, the channels this project can connect to reach the recipient, each `{type, request, note, available, dashboardUrl?}`; `request` is the exact call (`POST /v2/channels` with its body, or `POST /v2/numbers`), or null for a channel connected from the dashboard (WhatsApp, Instagram, Messenger). `available: false` says why in `note` (one-way SMS closed until business verification is approved). - `address` / `from`: the address the error is about, under the name the request used for it.
  - `errors` (object[])
    - `path` (object[], required)
    - `message` (string, required)
    - `code` (string)

## Example

```bash
curl -X PUT "https://api.zavu.dev/v2/webhook" \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "string",
  "events": [
    "message.queued"
  ]
}'
```

```typescript
import Zavudev from "@zavudev/sdk";

const zavu = new Zavudev({ apiKey: process.env.ZAVUDEV_API_KEY });

const result = await zavu.v2.webhook.set({
  url: "string",
  events: [
    "message.queued",
  ],
});

console.log(result);
```
