> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating to v2 signatures

> Move a webhook to the current signature scheme without downtime

Zavu signs webhooks with HMAC-SHA256. There are two schemes, and each receiver
is on one of them:

| Scheme | What is signed                                             |
| ------ | ---------------------------------------------------------- |
| `v1`   | the request body                                           |
| `v2`   | the timestamp and the request body, joined as `{t}.{body}` |

`v2` is the current scheme and covers more of the request. Webhooks created
from now on use it. Anything created earlier stays on `v1` until you move it,
and there is no deadline.

## Check where you are

```sh theme={null}
curl https://api.zavu.dev/v1/senders/$SENDER_ID \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY"
```

```json theme={null}
{
  "webhook": {
    "url": "https://api.example.com/webhooks/zavu",
    "active": true,
    "signatureVersion": "v1"
  }
}
```

In the dashboard, every sender webhook and its scheme is listed under
**Webhooks**.

## The three steps

Moving is deliberately not one switch. The middle step is where both signatures
arrive at once, so you can deploy and confirm your new code before anything
depends on it.

### 1. Turn on both

```sh theme={null}
curl -X PATCH https://api.zavu.dev/v1/senders/$SENDER_ID \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhookSignatureVersion": "v1+v2"}'
```

The header now carries both, sharing one timestamp:

```
X-Zavu-Signature: t=1786113454,v1=52a50333…,v2=b4b2b61c…
```

Your current receiver reads `v1` and is unaffected. Nothing about your
deliveries changes.

### 2. Verify v2, and confirm it in your own logs

Prefer `v2` when it is present, fall back to `v1`. The same code then works
before, during, and after the move:

```javascript theme={null}
const parts = {};
for (const piece of header.split(",")) {
  const i = piece.indexOf("=");
  if (i > 0) parts[piece.slice(0, i)] = piece.slice(i + 1);
}

const received = parts.v2 ?? parts.v1;
const signedPayload = parts.v2 ? `${parts.t}.${rawBody}` : rawBody;

const expected = crypto
  .createHmac("sha256", secret)
  .update(signedPayload)
  .digest("hex");
```

Full examples in five languages are in
[Security](/guides/receiving-messages/security).

Deploy it, then wait for real deliveries and confirm they are being accepted.
**This is the step that matters.** Do not skip to step 3 on the strength of a
test request: a receiver that returns `200` before it verifies looks identical
to a working one from our side.

### 3. Turn v1 off

```sh theme={null}
curl -X PATCH https://api.zavu.dev/v1/senders/$SENDER_ID \
  -d '{"webhookSignatureVersion": "v2"}'
```

From the next delivery the header carries `v2` only.

<Note>
  Going from `v1` straight to `v2` returns `400`. Set `v1+v2` first. The overlap
  costs nothing and can last as long as you want.
</Note>

## Rolling back

Set `v1+v2` again. Both signatures return immediately, so a receiver that reads
either one keeps working while you sort things out.

```sh theme={null}
curl -X PATCH https://api.zavu.dev/v1/senders/$SENDER_ID \
  -d '{"webhookSignatureVersion": "v1+v2"}'
```

## Several senders, one endpoint

If one endpoint serves more than one sender, move them one at a time and keep
the tolerant verifier from step 2. It accepts both schemes, so senders on
different settings can share a receiver for as long as you need.

When you create a new sender that points at an endpoint still verifying `v1`,
create it on `v1+v2` explicitly:

```sh theme={null}
curl -X POST https://api.zavu.dev/v1/senders \
  -d '{
    "name": "Support",
    "enableSmsOneway": true,
    "webhookUrl": "https://api.example.com/webhooks/zavu",
    "webhookSignatureVersion": "v1+v2"
  }'
```

Otherwise it defaults to `v2` and that endpoint will reject its deliveries.

## Next Steps

* [Security](/guides/receiving-messages/security) - Full verification examples
* [Webhooks](/guides/receiving-messages/webhooks) - Configure your endpoints
