📘 Public beta · Endpoints are stable; OpenAPI specs and SDKs ship monthly. See changelog →
Products
Bank Statement
Webhooks

Bank Statement · Webhooks

See Webhooks (global) → for verification, retries, idempotency.

Event catalog

EventFires whendata keys
analysis.uploadedPDF accepted into queueanalysisId · filename · sha256 · externalRef
analysis.parsedParser finished (before categorisation/scoring)analysisId · detectedBank · transactionCount
analysis.completedFull pipeline done — status: completedanalysisId · status · detectedBank · totalInflow · transactionCount · confidenceScore
analysis.requires_reviewCompleted but low-confidence; flagged for analystanalysisId · confidenceScore · reasons
analysis.failedParsing failedanalysisId · errorMessage · stage
analysis.password_requiredEncrypted PDF; password neededanalysisId
categorization.correctedUser manually re-categorized a transactiontransactionId · categoryId · ruleCreated · bulkAffected
anomaly.highHigh-severity anomaly raisedanalysisId · transactionId · type · severity · description

Signature

Bank Statement uses the X-BankStatement-Signature header (legacy product prefix). Same HMAC-SHA256 scheme as the global webhook recipe.

X-BankStatement-Event:     analysis.completed
X-BankStatement-Delivery:  whd_01HXY...
X-BankStatement-Signature: sha256=8b2c4d7e...
X-BankStatement-Timestamp: 1716552000
X-BankStatement-Attempt:   1

Example: analysis.completed

{
  "id": "evt_01HXY...",
  "event": "analysis.completed",
  "occurredAt": "2026-05-24T08:14:22.481Z",
  "orgId": "org_01HXY...",
  "data": {
    "analysisId": "ana_01HXY...",
    "status": "completed",
    "detectedBank": "BCA",
    "transactionCount": 45,
    "accountHolder": "Budi Santoso",
    "currency": "IDR",
    "totalInflow": 25000000,
    "confidenceScore": 95,
    "consolidationGroupId": "cgr_01HXY...",
    "externalRef": "loan-app-2026-05-24-001"
  }
}

consolidationGroupId lets you correlate parallel statement parses for the same loan application. If you uploaded 5 PDFs, you'll get 5 analysis.completed events sharing the same consolidationGroupId — bucket them in your receiver and trigger your downstream pipeline once you've seen one per uploaded analysis. A first-class consolidation.completed event that fires once when the last sibling finishes is on the 2026 Q3 roadmap.

Example: anomaly.high

{
  "id": "evt_01HXY...",
  "event": "anomaly.high",
  "occurredAt": "...",
  "orgId": "...",
  "data": {
    "analysisId": "ana_01HXY...",
    "transactionId": "txn_01HXY...",
    "type": "doctored_row",
    "severity": "high",
    "description": "Row appears edited: balance-after column has different font metrics from surrounding rows."
  }
}

Subscribe to anomaly.high and surface them in your underwriting UI — they're the canonical signal of "you might be looking at a fabricated statement."

Register

POST/api/webhooks
Auth · API keyScope · webhook:write
{
  "url": "https://your-app.example.com/webhooks/bank-statement",
  "events": ["analysis.completed", "analysis.requires_review", "analysis.failed", "anomaly.high"],
  "active": true
}

Response includes the signing secret (shown once):

{
  "data": {
    "id": "whk_01HXY...",
    "url": "...",
    "events": [...],
    "secret": "qe_whsec_..."
  }
}

Delivery history

GET/api/webhooks/{id}/deliveries
Auth · API keyScope · webhook:read

Returns the last 200 deliveries with status, response code, attempt count, and signed-bytes hash. Use this for debugging delivery failures from your end.

Rotating the secret

POST/api/webhooks/{id}/rotate-secret
Auth · API keyScope · webhook:write

Returns new secret. The old secret stays valid for a 24-hour grace window so you can roll the new value through your verifier without an outage.