Skip to main content

The transaction object

{
  "id": "k1730s...",
  "date": "2026-07-05",
  "amount": "-84.20",
  "account_id": "k4859a...",
  "account_name": "Chase Checking",
  "merchant_id": "k9921m...",
  "merchant_name": "Costco",
  "category_id": null,
  "category_name": null,
  "ready_to_assign": false,
  "notes": "Weekly groceries + household",
  "status": "cleared",
  "review_status": "reviewed",
  "type": "regular",
  "transfer_account_id": null,
  "splits": [
    {
      "amount": "-64.20",
      "category_id": "k5511c...",
      "category_name": "Groceries",
      "ready_to_assign": false,
      "notes": null
    },
    {
      "amount": "-20.00",
      "category_id": "k5512c...",
      "category_name": "Household",
      "ready_to_assign": false,
      "notes": null
    }
  ],
  "deleted": false,
  "created_at": "2026-07-05T18:22:10.000Z",
  "updated_at": "2026-07-05T18:22:10.000Z"
}
  • amount is the transaction total. Negative = money out, positive = money in.
  • Single-category transactions have category_id/category_name set and splits: null. Split transactions have splits and a null top-level category.
  • ready_to_assign: true marks income that flows straight to Ready to Assign instead of a category.
  • status is pending, cleared, or reconciled. review_status is reviewed or needs_review.
  • Transfers come in pairs: each side is a type: "transfer" transaction whose transfer_account_id points at the other account.

List transactions

GET /v1/workspaces/{workspace_id}/transactions
Query parameterTypeDescription
since_datedateOnly transactions on or after this date (YYYY-MM-DD)
until_datedateOnly transactions on or before this date
account_idstringOnly transactions in this account
category_idstringOnly transactions with a split in this category
statusstringpending, cleared, or reconciled
review_statusstringreviewed or needs_review
limitintPage size, 1–500 (default 100)
cursorstringOpaque pagination cursor from a previous response
Results are ordered newest-first. The response includes next_cursor; pass it back as cursor to fetch the next page, and stop when it is null.
Pages can contain fewer than limit items even when more results remain — always follow next_cursor rather than counting rows.

Get a transaction

GET /v1/workspaces/{workspace_id}/transactions/{transaction_id}
Deleted transactions are returned with "deleted": true.

Create transactions

POST /v1/workspaces/{workspace_id}/transactions
Send a single object under transaction, or up to 100 under transactions. The batch is atomic — if any row is invalid, nothing is created.
curl -X POST https://api.kualia.com/v1/workspaces/WORKSPACE_ID/transactions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction": {
      "account_id": "k4859a...",
      "date": "2026-07-05",
      "amount": "-12.34",
      "merchant_name": "Starbucks",
      "category_id": "k5511c..."
    }
  }'
FieldTypeDescription
account_idstringRequired. Account to book the transaction in
datedateRequired. YYYY-MM-DD
amountstringRequired (unless splits). Signed decimal total
category_idstringCategory for the whole amount. Optional — see auto-categorization below
ready_to_assignbooleantrue books the amount as income to Ready to Assign (instead of a category)
splitsarraySplit lines { amount, category_id?, ready_to_assign?, notes? } — replaces amount/category_id
merchant_idstringExisting merchant id
merchant_namestringMerchant by name — matched case-insensitively or created if new
notesstringFree-text memo
statusstringpending or cleared (default cleared)
review_statusstringreviewed or needs_review (default needs_review)
Auto-categorization. If you omit category_id (and don’t set ready_to_assign or splits) on a transaction that has a merchant, Kualia categorizes it automatically — the same way imported and AI-chat transactions are handled: your active rules, then your past categorizations for that merchant, learned merchant→category mappings, reviewed history, and finally an AI prediction for merchants it hasn’t seen before. Anything it can’t confidently categorize is left uncategorized. Auto-categorized rows still default to needs_review so you can confirm them in the app. Pass an explicit category_id to skip this entirely.

Transfers

Set "type": "transfer" to move money between two accounts. Both sides of the pair are created and returned.
{
  "transaction": {
    "type": "transfer",
    "from_account_id": "k4859a...",
    "to_account_id": "k7731b...",
    "amount": "500.00",
    "date": "2026-07-05"
  }
}
amount must be positive for transfers. An optional category_id categorizes the outflow side (useful for credit-card payments).

Update a transaction

PATCH /v1/workspaces/{workspace_id}/transactions/{transaction_id}
Send only the fields you want to change: date, amount, category_id, ready_to_assign, splits, merchant_id (or merchant_name), notes (string or null), account_id, status, review_status.
  • Changing amount on a single-category transaction keeps its current category; changing category_id keeps the amount.
  • To edit a split transaction’s amounts or categories, send the full splits array.
  • review_status never changes unless you send it explicitly.

Delete & restore

DELETE /v1/workspaces/{workspace_id}/transactions/{transaction_id}
POST   /v1/workspaces/{workspace_id}/transactions/{transaction_id}/restore
Deletes are soft: balances and category activity are rolled back, but the transaction can be restored. Deleting one side of a transfer deletes both sides.