Skip to content

Webhooks & Polling ​

L'API Convergence ne fournit pas de webhooks natifs. Opcovia comble ce manque avec un systeme de polling intelligent qui detecte les changements et notifie les clients via des webhooks HMAC-SHA256.

Vue d'ensemble ​

Poller : detection des changements ​

Le poller utilise le pipeline existant pour interroger l'OPCO. Il beneficie ainsi du cache, du rate limiting et des transforms.

Flux d'un poll ​

Le poller utilise dateModification (date du dernier poll) pour ne recuperer que les dossiers modifies depuis la derniere verification.

Flux complet : poll → diff → dispatch ​

Retry des webhooks en echec ​

Auto-desactivation ​

Si les webhooks echouent pendant 7 jours consecutifs, le poller se desactive automatiquement pour eviter de consommer du budget rate limit inutilement.

Auto-scheduler (BullMQ) ​

Le scheduler utilise une queue BullMQ dediee (opcovia-poller) avec des jobs repeatable :

  • 1 repeatable job par couple (opcoId, tenantHash)
  • Intervalle configurable par OPCO (defaut : 5 minutes)
  • Concurrence limitee a 1 (pas de polls en parallele pour un meme tenant)
  • Priorite basse (10) : les requetes clients passent devant

Enregistrement automatique via sync ​

Quand un bulk sync se termine avec enablePoller: true, le poller est automatiquement enregistre avec lastPollAt = date de debut du sync. Cela evite de re-scanner les dossiers deja recuperes.

Webhook Dispatcher ​

Signature HMAC-SHA256 ​

Chaque webhook possede un secret genere a la creation. Les payloads sont signes :

signature = HMAC-SHA256(secret, JSON.stringify(payload))

Headers envoyes :

HeaderValeur
Content-Typeapplication/json
Opcovia-SignatureSignature HMAC hex
Opcovia-EventType d'evenement

Retry ​

TentativeDelai avant retry
1-
21 seconde
35 secondes

Timeout par delivery : 10 secondes. Apres 3 echecs, la delivery est marquee failed.

Chaque tentative est loguee dans webhook_deliveries (statusCode, attempts, error).

Garantie : at-least-once ​

Le poller persiste le snapshot (lastPollAt) apres avoir dispatche les webhooks. En cas de crash entre le dispatch et la persistance, le prochain poll re-detectera les memes changements et les re-dispatchera. Le client doit etre idempotent.

Types d'evenements ​

EvenementDeclencheurPayload.data
dossier.updatedPoller detecte des changements{ dossiers, count }
dossier.etat_changedPoller detecte un changement d'etat{ dossier, oldEtat, newEtat }
facture.etat_changedPoller detecte un changement d'etat facture{ facture, oldEtat, newEtat }
job.completedWorker termine un job avec succes{ jobId, type, result }
job.failedWorker echoue apres 3 retries{ jobId, type, error }
job.progressStep d'un composite route terminee{ jobId, step, totalSteps, result }
sync.startedDiscovery sync terminee, batches lances{ syncId, totalEstimate, annees }
sync.batchUn batch de sync recupere{ syncId, batch, totalBatches, dossiers }
sync.completedSync complete{ syncId, total, durationMs }

CRUD Webhooks ​

RouteMethodeDescription
/webhooksPOSTCreer un webhook { callbackUrl, events }
/webhooksGETLister les webhooks du tenant
/webhooks/:idPATCHModifier (callbackUrl, events, active)
/webhooks/:idDELETESupprimer
/webhooks/:id/logsGETHistorique des deliveries

Protection SSRF ​

La callbackUrl est validee contre les attaques SSRF :

  • Protocole HTTPS obligatoire (sauf en dev)
  • Interdiction de localhost, 127.0.0.1, ::1
  • Interdiction des reseaux prives (10.x, 192.168.x, 172.16-31.x)
  • Interdiction des domaines .local, .internal

Bulk Sync ​

Le bulk sync est un processus lourd qui recupere tous les dossiers d'un CFA. Il est orchestre via BullMQ avec deux types de jobs :

POST /sync/dossiers { opcoId, minYear?, enablePoller? }
  → 202 { syncId }

┌──────────────────────────────────────────────┐
│ sync-discovery (priority: low)               │
│                                              │
│ Pass 1 : page 1 de chaque annee             │
│   → totalEstimate connu rapidement           │
│   → webhook sync.started                     │
│                                              │
│ Pass 2 : pages restantes                     │
│   → tous les IDs collectes                   │
│                                              │
│ Enqueue N sync-batch jobs (50 IDs chacun)    │
└──────────────────────────────────────────────┘
         │
         ▼ (x N jobs en parallele)
┌──────────────────────────────────────────────┐
│ sync-batch                                   │
│                                              │
│ GET /dossiers/liste?numerosInternes=...      │
│ UPDATE fetched = fetched + N (atomique SQL)  │
│ webhook sync.batch { dossiers }              │
│                                              │
│ Si dernier batch :                           │
│   webhook sync.completed                     │
│   Si enablePoller : register auto-scheduler  │
└──────────────────────────────────────────────┘

Progress consultable via GET /sync/:syncId (total, fetched, progress %, batchesCompleted).