Ricevere i webhook
I webhook ti mandano gli eventi in tempo reale, senza doverli chiedere di continuo. Iscrivi un URL, scegli gli eventi che ti interessano e verifica ogni consegna con la sua firma HMAC.
Iscriversi
Crea una sottoscrizione tramite l'API Webhooks con l'URL del tuo endpoint e gli eventTypes che vuoi ricevere. In cambio ricevi un segreto di firma: conservalo, serve a verificare ogni consegna.
curl -H "Authorization: Bearer vokse_sk_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -X POST https://api.vokse.ai/households/current/webhooks/subscriptions \ -d '{"url":"https://example.com/hooks/vokse","eventTypes":["transaction.created","budget.overrun"]}'Tipi di evento
L'elenco completo e sempre aggiornato dei tipi di evento lo trovi su /meta/webhook-events. Il catalogo di oggi:
- transaction.created: è stato registrato un movimento.
- recurrence.materialised: un movimento ricorrente è stato generato in automatico.
- bank.sync.completed: un collegamento bancario ha finito di sincronizzare.
- budget.overrun: una categoria ha sforato il budget del mese.
- spending.anomaly: è stata rilevata una spesa fuori dal solito.
- goal.reached: un obiettivo di risparmio è stato raggiunto.
- insight.generated: è pronta una nuova analisi dell'IA.
Verificare la firma
Ogni consegna porta un header X-Vokse-Signature nella forma t=<timestamp>,v1=<signature>. La firma è un HMAC-SHA256 calcolato con il tuo segreto di firma sul timestamp, un punto e il corpo grezzo della richiesta. Ricostruisci quella stringa dai byte esatti che hai ricevuto, ricalcola l'HMAC, confrontalo con v1 a tempo costante e scarta i timestamp troppo vecchi.
import crypto from 'node:crypto';// X-Vokse-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256>function verify(rawBody, signatureHeader, signingSecret) { const parts = new Map( signatureHeader.split(',').map((kv) => kv.split('=')), ); const t = parts.get('t'); const v1 = parts.get('v1'); const expected = crypto .createHmac('sha256', signingSecret) .update(`${t}.${rawBody}`) // timestamp, a dot, the exact bytes received .digest('hex'); if (!t || !v1 || v1.length !== expected.length) return false; // freshness: reject stale timestamps to block replays if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));}Nuovi tentativi e consegna
Rispondi 2xx entro pochi secondi per confermare. Le risposte 5xx e i timeout vengono ritentati con backoff esponenziale, fino a cinque tentativi; un 4xx è considerato definitivo e non viene mai ritentato. Dopo troppi fallimenti di fila la sottoscrizione va in pausa da sola e il proprietario della famiglia riceve una notifica.