Webhooks
Spendaq sends webhook events to your server when transactions are classified or forecast signals change. Webhooks are available on the Growth and Scale tiers.
Setting up a webhook endpoint
Register your webhook URL in your API dashboard. The endpoint must:
- Accept
POSTrequests with JSON content type - Return a
2xxHTTP status to acknowledge receipt - Respond within 10 seconds (or we'll retry)
Event types
| Event | Trigger |
|---|---|
transaction.classified | A transaction classification is complete |
forecast.updated | Cash-flow forecast signal changes |
batch.complete | All transactions in a batch have been classified |
Webhook payload
{
"event": "transaction.classified",
"id": "evt_3kQnwJ7...",
"created_at": "2026-06-18T09:41:07.142Z",
"account_id": "acct_kst_8841",
"data": {
"transaction_id": "txn_001",
"corrected_category": "Office Supplies",
"confidence_score": 0.97,
"original_category": "DEBIT_MISC",
"latency_ms": 142
}
}
Verifying signatures
Each webhook request includes a Spendaq-Signature header. Verify it to confirm the request came from Spendaq:
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from('sha256=' + expected)
);
}
Retry logic
If your endpoint doesn't return a 2xx within 10 seconds, Spendaq retries with exponential backoff:
- Attempt 1: immediately
- Attempt 2: 5 minutes later
- Attempt 3: 30 minutes later
- Attempt 4: 2 hours later
- Attempt 5: 6 hours later
Events that fail all 5 attempts move to a dead-letter queue accessible via the audit API.
Idempotency
Each event has a unique id. Store processed event IDs to handle duplicate deliveries safely — at-least-once delivery means the same event may occasionally arrive more than once.