API v1

Categorize transactions

Recommended agent workflow using stable category codes, counterparties, and evidence.

Markdown version · llms.txt

Always discover the chart before proposing changes. Prefer categoryCode (e.g. 5300) over free-text labels. Include confidence, reason, and structured evidence for human review. Keep list batches small (limit≤50).

Ontology

See Reference → Ontology for chart codes, synonyms, and seed counterparties (AWS → 5300). Public JSON-LD: GET /api/v1/ontology.jsonld

Workflow

  1. GET /categories — load codes, names, synonyms, parentCode
  2. GET /transactions?status=uncategorized&limit=50 — find work items
  3. Match description to counterparties (AWS, Figma, …) or category synonyms
  4. GET /transactions/:id — read notes and receipts when unsure
  5. POST /proposals/categorization with categoryCode, confidence, reason, evidence
  6. GET /proposals — verify proposals are pending
  7. Tell the user: pending in Ledger → Approvals; durable trail in Log (/log)
GET/api/v1/categoriesread

list_categories

Chart of accounts with stable codes, synonyms, and parent links. Call before proposing categorizations. Prefer categoryCode over display names.

Response

json
{
  "data": [
    {
      "code": "5100",
      "name": "Software & tools",
      "type": "expense",
      "parentCode": "5000",
      "synonyms": ["Software", "SaaS", "Subscriptions"],
      "inUse": true
    }
  ]
}
GET/api/v1/transactionsread

list_transactions

List ledger transactions for the token's organization. Results are ordered by date descending. Agents should keep limit≤50 unless exporting.

Parameters

NameTypeDescription
statusenumuncategorized, matched, receipt_missing, agent_suggested, reconciled
sourceenumnordea, stripe, manual_csv
datedateExact date (YYYY-MM-DD)
dateFromdateInclusive start date
dateTodateInclusive end date
categorystringExact category name, or __uncategorized__
descriptionstringCase-insensitive substring on description
limitintegerDefault 50 (agent-lean), max 500
offsetintegerDefault 0

Response

json
{
  "data": [ { "id": "…", "date": "2026-08-28", "amount": -842, "currency": "EUR", … } ],
  "meta": { "totalCount": 42, "limit": 50, "offset": 0 }
}
POST/api/v1/proposals/categorizationpropose-write

propose_categorization

Suggest a category for a transaction. Prefer categoryCode. Creates a pending proposal — the ledger is not updated until a human approves in Ledger → Approvals.

Request body

json
{
  "transactionId": "uuid",
  "categoryCode": "5300",
  "confidence": 0.91,
  "reason": "Description matches counterparty AWS",
  "evidence": [
    { "type": "counterparty_match", "value": "aws" }
  ]
}

Response

json
{ "data": { "id": "…", "status": "pending", "type": "categorization", … } }
Example proposal body
{
  "transactionId": "550e8400-e29b-41d4-a716-446655440000",
  "categoryCode": "5300",
  "confidence": 0.91,
  "reason": "Description matches counterparty AWS",
  "evidence": [
    { "type": "counterparty_match", "value": "aws" }
  ]
}

On approval, the transaction receives the canonical category name, status matched, and your proposedBy, confidence, and reason metadata. The propose and approve/reject events both appear in Log.