POST che dentalspace manda a un tuo indirizzo HTTPS quando succede qualcosa nella clinica: un appuntamento prenotato, una fattura pagata, un messaggio WhatsApp ricevuto. Così il tuo programma non deve chiedere ogni minuto se è cambiato qualcosa.
Destinazioni
Una destinazione è un indirizzo più l’elenco degli eventi che vuole ricevere. Si crea con l’API:secret, il segreto di firma (whsec_…). Si vede solo lì: conservalo subito.
Per creare o modificare una destinazione servono
webhooks:write e lo scope di ogni evento scelto: senza, 403 insufficient_scope con gli scope mancanti in details.requiredScopes. Se poi la chiave viene revocata o scade, la destinazione smette di ricevere.
Richiesta
La consegna riesce se rispondi
2xx entro 10 secondi. Un redirect (3xx) è un fallimento: non viene seguito. Del tuo indirizzo si registra solo lo status HTTP, mai il corpo della risposta.
Busta
data non contiene mai dati sanitari, e le risorse collegate sono solo id. Per i dettagli rileggi la risorsa con la tua chiave.
Catalogo degli eventi
Per ricevere qualunque evento la chiave deve avere anche
webhooks:read.
Due tipi non si sottoscrivono:
webhook.test: arriva solo quando lo chiedi conPOST /webhook-endpoints/{id}/test, condatavuoto;events.skipped: vedi sotto.
Eventi non nati
Se un evento non può nascere, al suo posto ne nasce unoevents.skipped, così sai che ti manca qualcosa invece di non accorgertene. data.reason dice perché:
data.type è il tipo dell’evento mancato e data.scope lo scope che serve per vederlo. Una destinazione riceve events.skipped se è iscritta a data.type. Quando lo ricevi, risincronizza con updatedSince sugli elenchi. Vedi Paginazione.
Firma
La firma è un HMAC-SHA256 calcolato con il segreto su questa stringa, quattro righe separate da\n:
X-DentalSpace-Signature con il prefisso v1=. Il timestamp è dentro la firma: una richiesta ripetuta con un timestamp diverso non verifica.
Verifica
- Leggi il corpo come byte grezzi, prima di qualsiasi parsing JSON.
- Rifiuta la richiesta se
X-DentalSpace-Timestampdista più di 5 minuti dall’ora attuale. - Calcola la firma attesa sulla stringa
v1\n<timestamp>\n<idempotency-key>\n<corpo>. - Confrontala con ciascuna firma dell’header, a tempo costante. Ne basta una valida.
- Se
X-DentalSpace-Idempotency-Keyl’hai già elaborata, rispondi200senza rifare il lavoro.
Rotazione del segreto
POST /webhook-endpoints/{id}/rotate-secret genera un segreto nuovo e lo mostra una volta sola. Il vecchio resta valido per overlapMinutes minuti, da 0 a 10080 (una settimana), predefinito 1440 (un giorno). In quel periodo ogni webhook porta due firme: v1=<nuova>,v1=<vecchia>. Aggiorna il segreto nel tuo programma prima di previousSecretExpiresAt. Con overlapMinutes: 0 il vecchio smette subito.
Ritentativi
Una consegna fallita (rete, timeout, risposta diversa da2xx) viene ritentata. Corpo e X-DentalSpace-Idempotency-Key restano uguali; cambiano timestamp e firma.
Le attese variano del 10% in più o in meno, perché i ritentativi di molte consegne non arrivino tutti insieme. Dopo il quinto fallimento la consegna diventa
discarded.
Dopo 50 consegne scartate di fila la destinazione si spegne da sola: enabled: false, disabledReason: "too_many_failures". Una consegna riuscita azzera il conteggio. Per riaccenderla: PATCH /webhook-endpoints/{id} con enabled: true.
Ogni clinica ha al massimo 5 consegne in corso alla volta, e le cliniche sono servite a turno: un tuo indirizzo lento rallenta solo le tue consegne.
Storico e consegne
Con una chiave di prova (
dsk_test_) il test non spedisce niente e risponde skipped: "test_mode".
Cancellata una destinazione, le sue consegne restano leggibili per 30 giorni.