---
title: "Create or run companies transactions bulk recategorize"
method: POST
path: "/accountant/companies/{customerId}/transactions/bulk-recategorize"
tags: ["Accountant Portal"]
---

# Create or run companies transactions bulk recategorize

`POST /accountant/companies/{customerId}/transactions/bulk-recategorize`

## Request

- Base URL: `https://api.medianfi.com/api/v1`
- URL: `https://api.medianfi.com/api/v1/accountant/companies/{customerId}/transactions/bulk-recategorize`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Generated minimal contract (#1940): parameters, auth, and status codes are authoritative; see the route handler for the exact body/response shape. Enrichment tracked by the x-median-spec-stage marker.

## Request body

- object
  - `transactionIds` string[], required
  - `categoryGlAccountId` string, uuid, required
  - `reason` string: #3046: why the category was changed. One line of plain text (trimmed; no control, line-separator or text-direction characters, no HTML markup, and not the text '| Recategorization reason:'). Stored as the reasoning of each recategorized row's decision on the transaction timeline and appended to the replacement journal entry's description as ' | Recategorization reason: <reason>'. Absent leaves the decision as before and removes a reason left on the description by an earlier recategorize; a description that never carried one is unchanged. The reason is not sent to QuickBooks.
  - `learnMerchantRule` boolean: #3041: true also writes or repoints the merchant rule for the selected transactions' merchants. Absent or false applies the category to the selected transactions only and the rule is left alone. Stripe revenue transactions (a Stripe charge, invoice or subscription payment, or a Stripe counterparty-history suggestion) are outside this choice: a category change on one always writes its exact company rule, because that rule is what lets the revenue entry post. The bookkeeping engine's feedback run is queued whatever the value; its output is a proposal for review.

## Response `200`

Success envelope; see the route handler for the payload shape

- object

## Other responses

- `401`: Authentication required
- `403`: Insufficient permissions or not assigned to the company. Also returned, before anything is written, when a service API key sends learnMerchantRule: true: the service token cannot create or retrain a merchant rule (#2383, #3039). Also returned to a service API key, before any row is written, when any selected row is a Stripe revenue row that the request would process, that is every such row except one already categorized on the target category (which is skipped): processing it writes the customer's merchant rule whatever learnMerchantRule says, whether or not its category changes. The whole request is refused and `detail` names the transaction ids (the first 20 of the rows whose category would change, and the first 20 of the rows in review already on the target); resend without them and leave those rows to an accountant (#3039, J-413, J-417).
- `404`: Resource not found

---

[API](https://skmtc.dev/medianfi/apis/median-api.md) · [All operations](https://skmtc.dev/medianfi/apis/median-api/llms.txt) · [OpenAPI document](https://skmtc.dev/medianfi/apis/median-api/revisions/20090ea32b0d?raw)
