# Webhooks

> Podpísané, opakované, tenké udalosti pre zápis zdrojov, zmeny súhlasu a finalizované dokumenty, s overením podpisu v TypeScript, Dart a Python.

Source: https://developers.anpheros.com/sk/guides/webhooks

Platforma Anpheros môže zavolať váš server, keď sa vo vašom projekte niečo stane: bol zapísaný zdroj, osoba udelila alebo odvolala prístup, bol finalizovaný dokument. Udalosti sú **tenké**: id, typ, verzia — nikdy klinický obsah. Prečítajte si zdroj cez API, ak ho potrebujete. Každé doručenie je podpísané; overte podpis pred dôverou.

## Registrácia koncového bodu

```bash
curl -X POST $BASE/v1/webhooks -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://your.app/anpheros/hook", "events": ["resource.created", "consent.revoked"], "description": "prod"}'
```

Odpoveď obsahuje `secret` (`whsec_…`) **raz**. Uložte si ju; budete ju potrebovať na overenie podpisov.
`events` akceptuje `["*"]` alebo ktorúkoľvek z nasledujúcich možností:

| typ | kedy | `data` |
|---|---|---|
| `resource.created` / `resource.updated` / `resource.deleted` | váš projekt zapísal FHIR zdroj (REST v1, FHIR API alebo bundle) — **alebo iný projekt zapísal do záznamu, ku ktorému máte prístup** (vytvorili ste pacienta alebo držíte aktívny grant); potom `data.external` je `true` a `data.source` hovorí, kto to zapísal: `author_type` (pacient / praktikant / zariadenie / import / AI), `organization` (zobrazovaný názov, v mene ktorého pisateľ konal), `practitioner` (názov z adresára, ak je známy), `source_system` | `resource_type`, `id`, `patient` (ID, ktoré vidíte), `version`, `last_updated` a pre externé zápisy `external`, `source` |
| `consent.granted` | osoba udelila vášmu aplikácii prístup zo stránky súhlasu (alebo dôveryhodná aplikácia prepojila svoj záznam) | `grant_id`, `patient`, `application_id`, `scopes`, `purpose`, `expires_at` |
| `consent.revoked` | osoba (alebo platforma) ukončila grant | rovnaké ako vyššie plus `revoked_at`, `reason` |
| `document.finalized` | nahrávanie dokumentu bolo finalizované a skontrolované pomocou checksumu | `id`, `patient`, `content_type`, `size`, `sha256` |
| `proposal.created` | AI agent navrhol zápis do záznamu, ktorý vidíte; čaká na schválenie osoby | `id`, `patient`, `action`, `resource_type` |
| `ping` | požiadali ste o testovacie doručenie | `endpoint` |

Koncové body sú na projekt **a prostredie**: kľúč `sk_test_` spravuje sandbox koncové body,
kľúč `sk_live_` produkčné koncové body. URL adresy musia byť verejné `https://` koncové body. Registrácia (a akákoľvek neskoršia zmena `url`) odmieta lokálne, súkromné, link-local, metadáta a iné rezervované adresy — `localhost`, `127.0.0.1`, `::1`, `10.0.0.0/8`, `192.168.0.0/16`, `fd00::/8` a podobné — a názvy, ktoré na ne odkazujú, s `400` `invalid` na poli `url`; toto platí aj v sandboxe. Každé doručenie znovu skontroluje adresu a pripojí sa iba k IP adresám, ktoré práve skontrolovalo, takže názov, ktorý neskôr ukazuje do súkromnej siete (DNS rebinding), nedostane nič. Ak chcete prijímať udalosti na vlastnom stroji počas vývoja, vystavte ho cez verejný HTTPS tunel a zaregistrujte adresu tunela.
Až 10 koncových bodov na projekt a prostredie.

## Doručenie

```
POST https://your.app/anpheros/hook
Content-Type: application/json
User-Agent: Anpheros-Webhooks/1.0
Anpheros-Event: resource.created
Anpheros-Event-Id: evt_…
Anpheros-Delivery: whd_…
Anpheros-Signature: t=1789800000,v1=5f3a…

{"id":"evt_…","type":"resource.created","created_at":"2026-09-19T12:00:00.000Z","environment":"production",
 "project":"proj_…","data":{"resource_type":"Observation","id":"…","patient":"…","version":1,"last_updated":"…"}}
```

Odpovedzte s akýmikoľvek `2xx` do 10 sekúnd. Čokoľvek iné (alebo časový limit) je neúspech a doručenie sa opakuje s oneskorením až 24 hodín (10 pokusov), potom sa označí ako `exhausted`.
Doručenia sú "aspoň raz" (at-least-once) a môžu prísť v nesprávnom poradí: použite `Anpheros-Event-Id` na
deduplikáciu a `data.version` na ignorovanie zastaraných aktualizácií.

## Overenie podpisu

`v1` je HMAC-SHA256 s vaším tajným kľúčom nad `<t>.<raw body>`; odmietnite, ak je `t` staršie ako 5 minút.

```ts
import { verifyWebhookSignature } from '@anpheros/sdk';
app.post('/anpheros/hook', express.raw({ type: 'application/json' }), async (req, res) => {
  const ok = await verifyWebhookSignature({ secret: process.env.ANPHEROS_WEBHOOK_SECRET!, header: req.header('anpheros-signature'), body: req.body });
  if (!ok) return res.status(400).end();
  const event = JSON.parse(req.body.toString());
  // … enqueue and return quickly
  res.status(204).end();
});
```

```dart
import 'package:anpheros_sdk/anpheros_sdk.dart';
final ok = verifyWebhookSignature(secret: secret, header: request.headers['anpheros-signature'], body: rawBodyBytes);
```

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

## Prevádzka

- `GET /v1/webhooks/{id}/deliveries?status=failed` — denník, najnovšie záznamy prvé, s chybovým kódom a chybou.
- `POST /v1/webhooks/{id}/deliveries/{delivery_id}/redeliver` — odoslať znova teraz.
- `POST /v1/webhooks/{id}/ping` — testovacia udalosť.
- `POST /v1/webhooks/{id}/rotate` — nové tajné kľúče (starý prestane platiť okamžite; ak si nemôžete dovoliť výpadok, aktualizujte svoj server ako prvý: zaregistrujte druhý koncový bod, potom odstráňte starý).
- `PATCH /v1/webhooks/{id}` s `{"active": false}` pozastaví doručovanie; čakajúce sa zahodia, keď odstránite koncový bod.

Oba SDK sprístupňujú toto pod `webhooks` (`create`, `list`, `get`, `update`, `delete`, `rotateSecret`, `ping`, `deliveries`, `redeliver`).
