Webhook#
Invece di interrogare ciclicamente, iscriviti e lascia che le modifiche ti raggiungano.
Registra un endpoint sulla risorsa webhook_endpoints — richiede webhook_endpoints.write; i percorsi esatti sono nella Referenza API. La creazione restituisce una sola volta un segreto di firma (whsec_…). Conservalo subito; non è recuperabile.
Gli eventi sono leggeri#
Il corpo è deliberatamente piccolo:
{
"id": "evt_0194d1c0-…",
"object": "event",
"type": "shift.published",
"created": "2026-07-22T10:04:11Z",
"related_object": {
"id": "0194d1c0-…",
"url": "https://app.shiftavo.com/api/public/v1/shifts/0194d1c0-…/"
}
}
Recupera lo stato attuale da related_object.url invece di affidarti all’evento per averlo. Così resti corretto anche quando più modifiche arrivano ravvicinate.
La consegna è almeno una volta e possibilmente fuori ordine, quindi il tuo handler deve essere idempotente: deduplica sull”id dell’evento.
Verifica ogni consegna#
Ogni POST porta un header Webhook-Signature (più Webhook-Id):
Webhook-Signature: t=1753178651,v1=5f3a…
Per verificare:
Ricalcola
HMAC-SHA256(secret, "{t}.{raw_body}")— sui byte grezzi del corpo, prima di qualsiasi analisi JSON o nuova serializzazione.Confrontalo a tempo costante con un valore
v1=.Rifiuta se
tè fuori da una tolleranza di ~5 minuti.
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(
secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256
).hexdigest()
# A rotation sends several v1= values — accept a match against any of them.
signatures = [v for k, v in (p.split("=", 1) for p in header.split(",")) if k == "v1"]
return any(hmac.compare_digest(expected, sig) for sig in signatures)
Importante
Durante una rigenerazione del segreto l’header porta più firme v1= — vecchia e nuova — per una finestra di sovrapposizione. Accetta una corrispondenza con una qualsiasi di esse, altrimenti le rigenerazioni faranno perdere delle consegne.
Recuperare dopo un’interruzione#
Le consegne non riuscite vengono ripetute con backoff esponenziale e la cronologia degli eventi inviati viene conservata. Se il tuo endpoint era fuori servizio puoi riconciliare da quella cronologia invece di interrogare di nuovo le risorse di dominio.
Iscriviti a tutto il ciclo di vita#
L’errore di integrazione più comune qui è iscriversi solo al percorso ideale e lasciare nella tua copia una pubblicazione morta ancora attiva.
Gli eventi del mercato arrivano in coppie open_shift.* / swap.* — una stessa offerta è pubblicata come due risorse (open_shifts/ per i posti vacanti, swaps/ per cessioni e scambi) e il tipo di evento ti dice quale collezione recuperare. Entrambe portano sia le transizioni finali sia le attribuzioni:
open_shift.claimed,open_shift.awarded,swap.approvedopen_shift.rejected,swap.rejected,*.withdrawn,*.auto_rejected,*.expired
Iscriviti anche alle transizioni finali, non solo alle attribuzioni.
L’elenco completo dei tipi di evento a cui puoi iscriverti è sulla risorsa webhook_endpoints nella Referenza API.