# Receive webhooks

> Subscribe an address to events, verify every delivery, answer fast and handle retries.

A webhook saves polling: NessFlow calls your server when a crawl finishes, an export is ready, recommendations are generated or a monitoring signal is detected. Webhooks are reserved to team owners and admins.

## Subscribe

```bash expect=201
curl -X POST "https://api.nessflow.com/v1/webhooks" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://hooks.exemple.fr/nessflow", "events": ["crawl.completed", "export.ready"]}'
```

The address must be HTTPS and public. The response carries the signing `secret` (`whsec_…`), shown this one time only. Read-only plans manage their webhooks from the team's **API** screen.

## Verify every delivery

Every delivery carries the `NessFlow-Signature` header: a timestamp `t` and one or more `v1` signatures, each the HMAC-SHA256 of `t.raw body` with your secret. Compute the signature on the RAW body, before any JSON decoding, compare in constant time, and refuse a timestamp older than 5 minutes (replay). The events page gives the verification function in PHP, Node and Python.

## Answer fast

Answer with a `2xx` code within 10 seconds, then process the event in the background. Any other outcome (error, timeout, redirect) is retried, 8 attempts in total over 24 hours. After 5 consecutive exhausted deliveries, the subscription is disabled and the admins are told by e-mail.

## Deduplicate

The same event may arrive twice (a response lost after a successful processing is indistinguishable from a failure). Deduplicate on the `NessFlow-Event-Id` header, identical across attempts.

## Test, then enable again

The test delivery goes even to a disabled subscription, never counts as a failure, and its result is read in the log:

```bash expect=202
curl -X POST "https://api.nessflow.com/v1/webhooks/$WEBHOOK_ID/test" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

```bash
curl "https://api.nessflow.com/v1/webhooks/$WEBHOOK_ID/deliveries" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

## Change secret without losing anything

After a rotation, every delivery is signed with both the old and the new secret for 24 hours: deploy the new one without a gap.

```bash
curl -X POST "https://api.nessflow.com/v1/webhooks/$WEBHOOK_ID/rotate-secret" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```
