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 POST requests with JSON content type
  • Return a 2xx HTTP status to acknowledge receipt
  • Respond within 10 seconds (or we'll retry)

Event types

Event Trigger
transaction.classifiedA transaction classification is complete
forecast.updatedCash-flow forecast signal changes
batch.completeAll 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.