---
title: "Categorize or split transactions"
method: POST
path: "/books/transactions/categorize"
tags: ["Books"]
---

# Categorize or split transactions

`POST /books/transactions/categorize`

## Request

- Base URL: `https://api.mercury.com/api/v1`
- URL: `https://api.mercury.com/api/v1/books/transactions/categorize`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/mercurytechnologies/apis/mercury-api/docs/books/transactions/categorize/post.ts> (a function with typed inputs; its response is checked against the schema)

Sets the categorization of one or more posted bank transactions, all or nothing. Each item names a transaction by its `journalEntryId` and gives its new categorization as `splits`: one split per category, each with the `ledgerId` of the ledger, i.e. general ledger (GL) account, to categorize to (such as an expense or revenue account) and the signed `amount` of the transaction that goes to it, as a JSON number of dollars with at most two decimal places. One split categorizes the whole transaction to one ledger; several split it across ledgers. Splits use the same sign convention as the transaction's `amount` from `GET /books/transactions` (getBooksTransactions), must be nonzero, and must add up to exactly that `amount`; a split with the opposite sign, such as a refund netted against a purchase, posts on the other side. A split may set the line's `description` and `tagIds`; omit them to keep those of the line it replaces. The splits replace every current categorization line of the transaction; its bank line on the source account's ledger is never changed. The last write wins: if the transaction was recategorized after you read it, this request overwrites that change. A split's ledger must belong to the Books instance and be editable, and must not be an uncategorized income or expense ledger or a source account's ledger. Only journal entries of bank transactions can be categorized, and a transaction matched to a transfer between accounts must be unmatched first. Send from 1 to 100 items, each transaction at most once; across all items, the splits may name at most 100 different ledgers. If a `journalEntryId`, split `ledgerId`, or tag ID doesn't exist, the whole request fails with 404, and if one belongs to another Books instance, or a split's ledger is read-only (linked to a Mercury account), with 403; no transaction is changed. A few problems fail the whole request with 422 and a `message` saying why, such as two tags from the same tag group on one split; no transaction is changed. Every item is checked before anything changes: if any item is invalid, no transaction is changed, and the 400 response's `errors.errorCode` is `invalidCategorizationItems` with `errors.itemErrors` listing every problem, each with its 0-based `itemIndex` into `items`, a `code`, and a `message`. The codes are `duplicateJournalEntry`, `journalEntryNotFound` (the journal entry was deleted while the request ran), `notTransactionSourced`, `transferMatched`, `splitsDoNotSumToTransactionAmount`, `invalidSplitAmount`, `ledgerNotFound` (the ledger was deleted while the request ran), `ledgerNotEditable`, `ineligibleLedger` (a split's ledger can't take a transaction's categorization, such as the opening balances ledger or an accounts receivable or payable ledger), `uncategorizedLedger`, `sourceAccountLedger`, and `unknownError` (any other problem, described by its `message`); more may be added, so treat an unrecognized code as `unknownError` and show its `message`. On success, every transaction has the requested categorization, and the response lists each one's new `categorizationLines`, in request order. A 500 response doesn't mean nothing changed: Books may have applied the items, so read the transactions again before retrying. To find transactions and their current categorization, use `GET /books/transactions` (getBooksTransactions); to find ledger IDs, use `GET /books/ledgers` (getBooksLedgers).

## Query parameters

- `booksId` string, uuid: The Books instance to act on. Must be the token organization's own instance, which it defaults to.

## Request body

- CategorizeBooksAPITransactionsRequest: Request to categorize or split one or more transactions, all or nothing.
  - `items` BooksAPICategorizeTransactionItem[], required: The transactions to categorize, from 1 to 100, each at most once. Across all items, the splits may name at most 100 distinct ledgers.
    - `journalEntryId` string, base58 (max length 22), required: The `journalEntryId` of the transaction to categorize.
    - `splits` BooksAPICategorizationSplit[], required: The new categorization: one split per category. The splits replace every current categorization line.
      - `amount` number, double, required: The signed, nonzero part of the transaction's amount: a number of dollars with at most two decimal places, with the same sign convention as the transaction's `amount`. The splits of a transaction must add up to exactly its `amount`. A split with the opposite sign to the transaction, such as a refund netted against a purchase, posts on the other side.
      - `description` string, size < 4001, nullable: The description of the categorization line. Omit to keep the description of the line it replaces, or for a new line, to use the transaction's description.
      - `ledgerId` string, base58 (max length 22), required: The ledger (general ledger account) to categorize this part of the transaction to, such as an expense or revenue account. It must be editable, and must not be an uncategorized ledger or a source account's ledger.
      - `tagIds` string[], nullable: The tags of the categorization line. Omit to keep the tags of the line it replaces; a new line then has no tags.

## Response `200`

- CategorizeBooksAPITransactionsResponse: The categorized transactions, in request order.
  - `items` BooksAPICategorizedTransaction[], required: One per request item, in request order.
    - `categorizationLines` BooksAPITransactionCategorizationLine[], required: The transaction's categorization lines, as the transaction's `categorizationLines` now list them.
      - `amount` number, double, required: The unsigned amount of the line, in dollars; `debitCredit` gives its direction.
      - `debitCredit` 'debit' | 'credit', required: Whether the amount is a debit or a credit to the ledger.
      - `ledgerId` string, base58 (max length 22), required: The identifier of the ledger (general ledger account) the line posts to, i.e. the category.
      - `ledgerName` string, required: The display name of the ledger the line posts to.
      - `lineEntryId` string, base58 (max length 22), required: The identifier of the categorization line entry.
    - `journalEntryId` string, base58 (max length 22), required: The `journalEntryId` of the transaction.

## Other responses

- `400`: Invalid `body` or `booksId`

## Changes

- **2026-11-03** `95c77a617769`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/mercurytechnologies/apis/mercury-api/changes/books/transactions/categorize/post.md)

---

[API](https://skmtc.dev/mercurytechnologies/apis/mercury-api.md) - [All operations](https://skmtc.dev/mercurytechnologies/apis/mercury-api/llms.txt) - [OpenAPI document](https://skmtc.dev/mercurytechnologies/apis/mercury-api/revisions/95c77a617769?raw)
