# Transactions

> Work with bank transactions — syncing, categorization, and reconciliation.

- **Category:** Guides
- HTML version: https://www.ondayzero.com/docs/guides/transactions
- All guides: https://www.ondayzero.com/docs/guides/index.md
- Endpoint catalog: https://www.ondayzero.com/docs/reference/index.md

---

# 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](/docs/guides/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](/docs/guides/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](/docs/guides/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](/docs/guides/invoices) and [Bills](/docs/guides/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.
