# Webhooks

> Evenimente semnate, reîncercate, subțiri pentru scrieri de resurse, modificări de consimțământ și documente finalizate, cu verificare a semnăturii în TypeScript, Dart și Python.

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

Platforma Anpheros poate apela serverul dvs. atunci când se întâmplă ceva în proiectul dvs.: o resursă a fost scrisă, o persoană a acordat sau a revocat accesul, un document a fost finalizat. Evenimentele sunt **subțiri**: id-uri, tip, versiune — niciodată conținut clinic. Citiți resursa prin API dacă aveți nevoie de ea. Fiecare livrare este semnată; verificați semnătura înainte de a avea încredere în ea.

## Înregistrați un endpoint

```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"}'
```

Răspunsul conține `secret` (`whsec_…`) **o singură dată**. Stocați-l; aveți nevoie de el pentru a verifica semnăturile.
`events` acceptă `["*"]` sau oricare dintre următoarele:

| tip | când | `data` |
|---|---|---|
| `resource.created` / `resource.updated` / `resource.deleted` | proiectul dvs. a scris o resursă FHIR (REST v1, FHIR API sau un bundle) — **sau un alt proiect a scris într-un înregistrare la care aveți acces** (ați creat pacientul sau dețineți o autorizație activă); apoi `data.external` este `true` și `data.source` indică cine a scris: `author_type` (pacient / practician / dispozitiv / import / ai), `organization` (numele afișat în numele căruia a acționat scriitorul), `practitioner` (numele din director, când este cunoscut), `source_system` | `resource_type`, `id`, `patient` (ID-ul pe care îl vedeți), `version`, `last_updated` și pentru scrieri externe `external`, `source` |
| `consent.granted` | o persoană a acordat aplicației dvs. acces de pe pagina de consimțământ (sau o aplicație de încredere și-a legat înregistrarea) | `grant_id`, `patient`, `application_id`, `scopes`, `purpose`, `expires_at` |
| `consent.revoked` | persoana (sau platforma) a încheiat o autorizație | același ca mai sus plus `revoked_at`, `reason` |
| `document.finalized` | o încărcare de document a fost finalizată și verificată prin checksum | `id`, `patient`, `content_type`, `size`, `sha256` |
| `proposal.created` | un agent AI a propus o scriere într-o înregistrare pe care o puteți vedea; așteaptă aprobarea persoanei | `id`, `patient`, `action`, `resource_type` |
| `ping` | ați solicitat o livrare de test | `endpoint` |

Endpoint-urile sunt per proiect **și mediu**: o cheie `sk_test_` gestionează endpoint-urile sandbox,
o cheie `sk_live_` pe cele de producție. URL-urile trebuie să fie endpoint-uri publice `https://`. Înregistrarea (și orice modificare ulterioară a `url`) refuză adrese locale, private, link-local, metadata și alte adrese rezervate — `localhost`, `127.0.0.1`, `::1`, `10.0.0.0/8`, `192.168.0.0/16`, `fd00::/8` și altele similare — și nume care se rezolvă la acestea, cu `400` `invalid` pe câmpul `url`; acest lucru se aplică și în sandbox. Fiecare livrare verifică din nou adresa și se conectează doar la adresele IP pe care tocmai le-a verificat, astfel încât un nume care ulterior indică o rețea privată (DNS rebinding) nu va primi nimic. Pentru a primi evenimente pe propria mașină în timpul dezvoltării, expuneți-o printr-un tunel HTTPS public și înregistrați adresa tunelului.
Până la 10 endpoint-uri per proiect și mediu.

## Livrarea

```
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":"…"}}
```

Răspundeți cu orice `2xx` în decurs de 10 secunde. Orice altceva (sau o expirare a timpului) este un eșec, iar livrarea este reîncercată cu backoff timp de până la 24 de ore (10 încercări), apoi marcată `exhausted`. Livrările sunt cel puțin o dată și pot ajunge în afara ordinii: utilizați `Anpheros-Event-Id` pentru a elimina duplicatele și `data.version` pentru a ignora actualizările învechite.

## Verificarea semnăturii

`v1` este HMAC-SHA256 cu secretul dumneavoastră peste `<t>.<raw body>`; respingeți dacă `t` este mai vechi de 5 minute.

```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"])
```

## Operare

- `GET /v1/webhooks/{id}/deliveries?status=failed` — jurnalul, cel mai nou primul, cu codul de stare și eroarea.
- `POST /v1/webhooks/{id}/deliveries/{delivery_id}/redeliver` — trimite-l din nou acum.
- `POST /v1/webhooks/{id}/ping` — un eveniment de test.
- `POST /v1/webhooks/{id}/rotate` — secret nou (cel vechi se oprește imediat; actualizează-ți serverul mai întâi dacă nu îți permiți o întrerupere: înregistrează un al doilea endpoint, apoi șterge-l pe cel vechi).
- `PATCH /v1/webhooks/{id}` cu `{"active": false}` întrerupe livrările; cele în așteptare sunt eliminate când ștergi endpoint-ul.

Ambele SDK-uri expun acest lucru sub `webhooks` (`create`, `list`, `get`, `update`, `delete`, `rotateSecret`, `ping`, `deliveries`, `redeliver`).
