Transactions

Transactions represent bank transactions synced from Plaid, imported from Ramp, Xero or QuickBooks, uploaded from CSV, or created manually. They are the core data type for bank feeds, categorization, and reconciliation.

Transaction Lifecycle

  1. Sync — Transactions arrive from a connected bank (source: plaid), a card program (ramp), a migration (xero, quickbooks), a CSV upload (bulk_upload) or the API (manual)
  2. Clean — Merchant names are normalized into counterparty automatically
  3. Categorize — AI, bank rules, or manual assignment to an expense/revenue ledger via the transaction's journal entry
  4. Review — A user confirms the categorization (review_status: reviewed)
  5. Reconcile — Matched against bank statements (reconciled: true) — see Reconciliation

List Transactions

bash
curl "https://api.ondayzero.com/api/v1/transactions?limit=25&only_uncategorized=true&start_date=2026-03-01" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"

Filter Parameters

Parameter Type Description
start_date / end_date date Transaction date range
ledger_id UUID Bank account ledger the transaction belongs to
opposing_ledger_id UUID Category ledger (the expense/revenue side of the journal entry)
source_account_id UUID[] Plaid source account(s)
search string Case-insensitive match on description or counterparty
amount_direction string inflow (money in) or outflow (money out)
only_uncategorized boolean Only transactions with no category yet
missing_counterparty boolean Only transactions with no counterparty
review_status string unreviewed, reviewed, or comma-separated
reconciled boolean true only reconciled, false only unreconciled
posted_status string posted (cleared) or pending (unsettled authorizations)
source string plaid, ramp, bulk_upload, manual, xero, quickbooks, or comma-separated
tag_id / journal_entry_id / id UUID Exact-match filters

Plus the standard pagination parameters.

Key Fields

Field Type Description
amount integer Amount in cents (positive = money in, negative = money out)
currency string ISO currency code
datetime datetime When the transaction occurred
description string Raw bank description
counterparty string Cleaned merchant / payee name
ledger_id / ledger_name UUID / string The bank account ledger
journal_entry_id / journal_entry UUID / object The double-entry record that carries the categorization
opposing_line_entries array The category side(s) of that journal entry — where the money was booked
posted_status string not_posted, posted, deleted
review_status string unreviewed, reviewed
reconciled boolean Matched to a bank statement
categorization_method string ai, rules, similarity, transfer_between_accounts, user, historical, global_pattern, uncategorized
source string Where the transaction came from (see filters)
invoice_ids / bill_ids UUID[] Invoices or bills this transaction pays

Create a Transaction

For bank accounts that aren't connected to a feed:

bash
curl -X POST "https://api.ondayzero.com/api/v1/transactions" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": -5000,
    "currency": "USD",
    "datetime": "2026-03-15T00:00:00Z",
    "description": "Office supplies",
    "source_account_id": "SOURCE_ACCOUNT_UUID"
  }'

POST /api/v1/transactions/bulk creates many at once. For CSV/Excel statements use the upload flow: POST /api/v1/transactions/bulk-upload/headers (discover the columns), …/bulk-upload/preview, then …/bulk-upload with a column_mapping.

Categorization

Every transaction is categorized through its journal entry — the bank ledger on one side, the category ledger on the other. You rarely build that entry yourself:

  • Recategorize one or many: PATCH /api/v1/transactions/batch/category with {"transaction_ids": [...], "ledger_id": "CATEGORY_LEDGER_UUID"} moves the opposing line entries to the new ledger.
  • Everything for a merchant: POST /api/v1/transactions/recategorize-by-counterparty with {"counterparty": "Uber", "ledger_id": "..."}.
  • Let AI do it: POST /api/v1/transactions/categorize-all runs the full categorization workflow (bank rules → history → LLM) over all uncategorized transactions, auto-applies high-confidence results, and returns a workflow ID immediately. POST /api/v1/transactions/suggest-categories returns suggestions without applying them.
  • Split across categories: PATCH /api/v1/transactions/{transaction_id}/split:
bash
curl -X PATCH "https://api.ondayzero.com/api/v1/transactions/{transaction_id}/split" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "split": [
      { "ledger_id": "MEALS_LEDGER_UUID", "allocation_type": "percent", "value": 7000 },
      { "ledger_id": "TRAVEL_LEDGER_UUID", "allocation_type": "remainder" }
    ]
  }'

allocation_type is percent (value in basis points — 7000 = 70%), amount (cents) or remainder. Each leg may carry project_id, class_id or location_id dimensions.

If you do need a hand-built journal entry (for a transaction with no bank feed, an accrual, a correction), see Journal Entries.

Review and Reconcile

  • PATCH /api/v1/transactions/batch/review-status marks transactions reviewed; POST /api/v1/transactions/batch/mark-reviewed-by-period does it for a whole period.
  • PATCH /api/v1/transactions/batch/reconciliation-status sets reconciled in bulk.
  • PUT /api/v1/transactions/{transaction_id} updates a single transaction's counterparty, description, review_status, reconciled, personal flag, and so on.
  • GET /api/v1/transactions/{transaction_id}/suggested-matches lists invoices and bills this transaction probably pays; link them from the Invoices and Bills payment endpoints.

Delete and Restore

DELETE /api/v1/transactions/{transaction_id} hides a bank-fed transaction (soft delete; POST …/restore brings it back) and removes a manual one. DELETE /api/v1/transactions/by-date-range?start_date=…&end_date=… clears an imported range — preview with GET …/by-date-range/preview first.