# Change feeds and watches

## The change feed

`GET /v1/companies/changes` lists every change of the register, oldest
first: which company and which top-level fields changed (`names`, `status`,
`addresses`, …). Start with `since`, then keep the `next_cursor`:

```python
cursor = None
while True:
    params = {"cursor": cursor} if cursor else {"since": "2026-10-19T00:00:00Z"}
    page = api.get("/v1/companies/changes", params=params).json()
    for change in page["items"]:
        print(change["seq"], change["company_id"], change["kind"], change["fields"])
    cursor = page["next_cursor"]
    if not page["items"]:
        break
```

`kind` is `created`, `updated`, `merged` (the record was merged into another
one; its id redirects) or `split`. Read the record for the new values and
`GET /v1/companies/{id}/history` for the old ones.

## Watches (webhooks)

Register the companies you care about and an HTTPS endpoint:

```sh
curl -s -X POST https://api.cormorant.ch/v1/companies/watches \
  -H "Authorization: Bearer $CORMORANT_API_KEY" -H "Content-Type: application/json" \
  -d '{"companies": ["lei:BFM8T61CT2L1QCEMIK50", "ch_uid:CHE-116.281.710"], "url": "https://example.org/hooks/companies"}'
```

The answer has the watch `id` and its `secret` (shown only once). Every
minute, changes of the watched companies are POSTed as one
`company.changed` delivery (`event`, `id`, `watch_id`, `changes`). Verify
`X-Cormorant-Signature: t=<unix>,v1=<hex>`, the HMAC-SHA256 of
`"<t>.<raw body>"` under the secret, exactly as for
[usage alerts](/docs/usage.md):

```python
import hashlib, hmac, time


def verify(secret: str, body: bytes, header: str, tolerance_s: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts["t"])
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return abs(time.time() - t) <= tolerance_s and hmac.compare_digest(expected, parts["v1"])
```

Answer any 2xx to acknowledge. Failed deliveries are retried after 1, 5, 15,
60 and 180 minutes, then hourly, and no change is skipped; use the delivery
`id` as idempotency key. A watched company merged into another is followed
automatically. `GET /v1/companies/watches` lists your watches,
`DELETE /v1/companies/watches/{id}` removes one (at most 20 per project,
1000 companies each).
