Analyses & transactions
Read an analysis
/api/analyses/{id}Returns the analysis + up to 1000 transactions. For statements with >1000 transactions, paginate via ?offset=0&limit=1000 (max 1000 per call).
Full response shape
{
"data": {
"id": "ana_01HXY...",
"status": "completed",
"parseMethod": "bca_native | mandiri_native | ... | gemini_vision | manual_review",
"detectedBank": "BCA",
"rawCustomerName": "BUDI SANTOSO",
"accountHolder": "Budi Santoso",
"accountNumber": "1234567890",
"accountNumberMasked": "******7890",
"statementPeriod": "01 May 2026 - 31 May 2026",
"openingBalance": 12500000,
"closingBalance": 18750000,
"totalInflow": 25000000,
"totalOutflow": 18750000,
"netMovement": 6250000,
"averageBalance": 14375000,
"minBalance": 8200000,
"maxBalance": 24500000,
"currency": "IDR",
"confidenceScore": 99,
"confidenceLevel": "high | medium | low",
"affordabilityScore": 78,
"authenticityScore": 96,
"authenticityStatus": "trusted | suspicious | unknown",
"manualReviewRequired": false,
"riskLevel": "low | medium | high",
"transactions": [...],
"createdAt": "...",
"completedAt": "...",
"customer": { "id": "cus_01HXY...", "fullName": "Budi Santoso" },
"transactionCount": 47
}
}Field meanings
| Field | Notes |
|---|---|
parseMethod | {bank}_native for native parsers, gemini_vision for AI parser, manual_review if analyst sign-off was the canonical extraction. |
rawCustomerName | Name exactly as on the statement. May be ALL CAPS or have weird punctuation. |
accountHolder | Normalized name; used for cross-statement reconciliation in consolidations. |
accountNumberMasked | Display version; last 4 digits visible. |
confidenceLevel | high (>= 90) · medium (70–89) · low (< 70). Used by the analyst-review trigger. |
affordabilityScore | 0–100 heuristic: monthly average balance / monthly outflow + stability adjustment. |
authenticityScore | 0–100. PDF metadata consistency + transaction-balance math + sudden-row checks. |
authenticityStatus | trusted (>= 80) · suspicious (60–79) · unknown (< 60). |
manualReviewRequired | True when confidence below threshold OR authenticity below threshold. |
riskLevel | Convenience rollup: low (auth >= 80, conf >= 90) · medium · high. |
Transaction shape
{
"id": "txn_01HXY...",
"sequenceNo": 1,
"occurredAt": "2026-05-02T00:00:00Z",
"rawTxnDate": "02/05",
"description": "SALARY PT XYZ",
"amount": 12000000,
"direction": "in",
"balanceAfter": 24500000,
"balanceMismatch": false,
"channel": "transfer | atm | qris | card | cash | fee | interest | tax",
"categoryId": "cat_salary",
"categoryConfidence": 0.98
}| Field | Notes |
|---|---|
sequenceNo | 1-based position in the statement (preserved from source). |
occurredAt | ISO-8601 with timezone. Inferred from rawTxnDate + statementPeriod. |
rawTxnDate | As printed: "02/05" or "15/05/2026". Some banks omit the year. |
description | Normalized: trimmed, repeated spaces collapsed. |
amount | Absolute value (always positive). Use direction to know sign. |
direction | in (credit) · out (debit). |
balanceAfter | Running balance. null if not extractable. |
balanceMismatch | true when prev.balanceAfter + signedAmount !== this.balanceAfter. |
channel | Inferred from description heuristics. transfer is the default catch-all. |
categoryId | Pre-categorized — see categories below. |
categoryConfidence | 0–1 confidence of the categorisation. |
Categories
Built-in categories (each has a stable categoryId):
| Category | Examples |
|---|---|
salary | Payroll credits, contractor invoices paid |
transfer_internal | Self-transfer between own accounts |
transfer_external | Transfers to/from third parties |
food | Restaurants, food delivery (GoFood, GrabFood, Shopee Food) |
transport | Gojek, Grab, taxi, parking |
ecommerce | Tokopedia, Shopee, Lazada |
bills_utilities | PLN, PDAM, Indihome, Telkomsel postpaid |
loan_repayment | KKB, KPR, KTA, BNPL repayments |
cash_withdrawal | ATM cash, cardless withdrawal |
cash_deposit | ATM/CRM cash deposits |
interest_credit | Bank-paid interest |
fee_debit | Admin fees, transfer fees, RTGS fees |
tax_debit | PPh23, BPHTB withholding |
investment | Reksadana, saham, deposito |
insurance | Premi (life, health, motor) |
other | Catch-all |
Total of 38 categories. Full list available via the dashboard's Category Library page.
Correcting a category
/api/transactions/{id}/category{
"categoryId": "cat_loan_repayment",
"createRule": true,
"applyToSimilar": true
}createRule: true— turns this correction into a permanent rule for your org. Description-pattern-based.applyToSimilar: true— applies the rule retrospectively to all matching transactions in your org. ReturnsbulkAffectedcount.
Common pattern: analyst sees a string of "PEMBAYARAN KKB BCA" transactions categorized as transfer_external. They correct one with createRule: true, applyToSimilar: true, and all of them flip to loan_repayment.
List analyses
/api/analysesFilters: status, bank (code), customerId, externalRef, from, to, page, limit. Default page size 50, max 200.
{
"data": [{ /* analysis row, no transactions */ }],
"total": 1287,
"page": 1,
"limit": 50
}The list endpoint returns analysis metadata + summary fields (totalInflow, confidenceScore, etc.) but not transactions — fetch the detail endpoint per analysis when you need transaction-level data.
Delete
/api/analysesBulk delete by query string:
curl -X DELETE ".../api/analyses?ids=ana_1,ana_2,ana_3" \
-H "Authorization: Bearer $QE_API_KEY"Response:
{
"data": {
"deleted": ["ana_1", "ana_2"],
"skipped": [{ "id": "ana_3", "reason": "currently parsing — cancel job first" }],
"missing": []
}
}Delete is destructive but auditable
Deletions are logged with actor + timestamp. The original PDF (if still in retention window) and the analysis row are both removed. To purge just the PDF but keep the analysis (for audit), use the dashboard's "Strip PDF" action instead.