Concepts
Webhooks — push events vers tes apps tierces
Stripe-style. Tu déclares une URL + une liste d'events qui t'intéressent, ProofIt te POST le JSON HMAC-signé dès qu'une session change d'état. Pas de polling, pas de WebSocket. Retry exponentiel et DLQ après épuisement.
Cas d'usage
- Notifier ton SI métier dès que la session est validée (ex : libérer un dossier RH, déclencher un virement)
- Archiver le
session_root_hashdans ton data lake au moment dusession.closed - Téléversement automatique du bundle dès
bundle.generated— le payload porteanchor_coverage: n'archiver quecomplete(jamais un provisoire) - Devenir témoin de sa propre chaîne via
checkpoint.created: vérifier la consistance puis contre-signer avec SA clé Ed25519 — rend toute réécriture détectable - Re-router vers Slack/Teams pour les refus (
session.refused) avec lareason
Événements supportés
| Event | Émis quand |
|---|---|
session.opened | POST /v1/sessions accepté |
session.validated | OTP correct, session passe en OPEN |
session.plan_committed | Manifest prévisionnel scellé à l'OTP (si planned_operations fourni) |
session.signing_context_attested | Contexte de signature attesté (si signing_context fourni) |
session.action_recorded | VALIDATION_ACTION écrite dans la chaîne |
session.artifact_attached | PDF/cert signature attaché |
session.refused | Refus explicite ou OTP max attempts |
session.plan_resolved | Verdict du manifest calculé à la clôture (MATCH/PARTIAL/DIVERGENT/EMPTY) |
session.closed | CloseSession + session_root_hash calculé |
session.abandoned | Timeout détecté par app:sessions:detect-abandoned |
bundle.generated | ASiC-E construit (manuel ou auto via cron) |
anchor.confirmed | Ancrage confirmé (jeton TSP qualifié reçu ou OTS Bitcoin upgradé) |
checkpoint.created | Tête de chaîne signée émise — le payload porte le canonical exact à contre-signer (témoin, couche 4) |
anchor.failed | Tous les fournisseurs d'ancrage ont échoué pour ce lot |
Format du payload
{
"id": "01938a7a-1f6c-7c00-b1f0-...", // UUID v7, idempotency key
"type": "session.closed",
"occurred_at": "2026-05-22T14:32:01+02:00",
"organisation_id": "019e4f7f-...",
"data": {
"session_id": "...",
"actor_user_id": "...",
"closed_at": "...",
"session_root_hash": "abc123..."
}
}
Vérifier la signature
Chaque POST inclut ces headers :
X-Timestamp: unix timestamp serveur ProofItX-Signature-SHA256:hmac_sha256(secret, "{timestamp}.{raw_body}")X-Signature-Version:v1(schéma de signature, pour migration future)X-ProofIt-Event: le type d'event (ex.session.closed)X-ProofIt-Event-Id: UUID v7 idempotency — refuse les doublons côté receveur
D'où vient le
secret ? C'est le secret HMAC de callback de ton organisation, affiché une seule fois à l'inscription (/register) à côté de ta clé API. Tu peux le régénérer depuis ton espace admin (action « Régénérer le secret de callback ») — les anciens secrets cessent alors immédiatement de fonctionner, pense à mettre à jour ton serveur avant.
Côté organisation tu dois :
- Rejeter si
|now - X-Timestamp| > 300s(replay defense) - Recalculer la signature avec ton
secretstocké - Comparer en timing-safe (
hash_equalsen PHP,crypto.timingSafeEqualen Node) - Si
X-ProofIt-Event-Iddéjà vu → 200 OK sans rejouer la logique métier
Retry & DLQ
- 2xx → SUCCESS, plus de retry
- 5xx, 408, 425, 429, timeout → RETRYING, jusqu'à 5 tentatives (backoff exponentiel ×3, max 1h)
- 4xx (sauf timing) → DLQ direct (config receveur fautive, retry inutile)
- Épuisement → DLQ +
last_status_codesur la subscription
API CRUD
| Endpoint | Scope |
|---|---|
POST /v1/webhook-subscriptions | webhooks:write |
GET /v1/webhook-subscriptions | webhooks:read |
GET /v1/webhook-subscriptions/{id} | webhooks:read |
PATCH /v1/webhook-subscriptions/{id} (pause/resume/disable) | webhooks:write |
DELETE /v1/webhook-subscriptions/{id} | webhooks:write |
GET /v1/webhook-subscriptions/{id}/deliveries | webhooks:read |