Receber webhooks
Os webhooks enviam-lhe os eventos em tempo real, em vez de andar a sondar a API. Subscreva um URL, escolha os eventos que lhe interessam e verifique cada entrega com a respetiva assinatura HMAC.
Subscrever
Crie uma subscrição através da API de Webhooks, com o URL do seu endpoint e os eventTypes que quer receber. Em troca, recebe um segredo de assinatura. Guarde-o: é ele que verifica todas as entregas.
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"]}'Tipos de evento
A lista completa e sempre atual dos tipos de evento é servida em /meta/webhook-events. O catálogo de hoje:
- transaction.created: foi registado um movimento.
- recurrence.materialised: um movimento periódico gerou o movimento agendado.
- bank.sync.completed: uma ligação bancária terminou a sincronização.
- budget.overrun: uma categoria excedeu o orçamento do mês.
- spending.anomaly: foi detetado um gasto fora do normal.
- goal.reached: um objetivo de poupança atingiu a meta.
- insight.generated: está pronta uma nova análise da IA.
Verificar a assinatura
Cada entrega traz um cabeçalho X-Vokse-Signature no formato t=<timestamp>,v1=<signature>. A assinatura é um HMAC-SHA256 calculado com o seu segredo de assinatura sobre o timestamp, um ponto e o corpo do pedido em bruto. Reconstrua essa string a partir dos bytes exatos que recebeu, volte a calcular o HMAC, compare-o com v1 em tempo constante e rejeite timestamps demasiado antigos.
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));}Repetições e entrega
Responda 2xx em poucos segundos para confirmar a receção. As respostas 5xx e os timeouts são repetidos com backoff exponencial, até cinco tentativas; um 4xx é considerado definitivo e nunca é repetido. Ao fim de várias falhas seguidas, a subscrição é suspensa automaticamente e o proprietário do agregado é notificado.