# PATCH /api/v1/transactions/batch

> Edit many transactions, a different change per row

- **Tag:** transactions
- **Operation ID:** `bulk_edit_transactions`

## Description

Apply a different edit to each of up to 500 transactions in one request: per row ``description`` (alias ``memo``), ``ledger_id`` (category), ``counterparty`` and ``review_status``. Every row is validated first; one invalid row rejects the whole request with 400 and nothing changes. Applying is then isolated per row, so a row that fails at write time is reported without undoing the others (``partial``). ``remember`` saves counterparty renames for the business, with the payment-processor guard. ``dry_run`` validates and reports per row without writing.

## Authentication

Bearer token in `Authorization` header.
Required header: `x-business-id: <business uuid>`.

## Request body

Schema: `TransactionBulkEditRequest`

- `rows` (array · TransactionBulkEditRow · required) → `TransactionBulkEditRow` — Rows to edit (1-500), one transaction each.
  - `transaction_id` (string · required) — Transaction UUID to edit.
  - `description` (string) — New description / memo. Stamps the row as user-edited so a later bank sync or cleaner pass keeps the text.
  - `memo` (string) — Alias of description (one stored field).
  - `ledger_id` (string) — Category ledger UUID to book the transaction to.
  - `counterparty` (string) — Counterparty / merchant name to set on this transaction.
  - `review_status` (string) — Set review status: 'unreviewed' or 'reviewed'.
- `remember` (boolean) — Applies to rows that set a counterparty: save each rename for the business (a saved rename) so its other transactions under the same name show it too. Payment processors that pay many parties (Gusto, ADP, PayPal, Stripe, ...) are still renamed on that row only. Default false: only the listed rows change.
- `dry_run` (boolean) — Validate every row and report what would change without writing anything.
- `plan_hash` (string) — The plan_hash a dry run of these same rows returned. When present, the edit is refused (VALIDATION_640) if the transactions or the rows no longer match that plan.

## Responses

### 200 — Successful Response

Schema: `SuccessEnvelope_TransactionBulkEditResponse_`

- `success` (boolean)
- `message` (string)
- `code` (string)
- `data` (TransactionBulkEditResponse)

### 400 — Bad Request - Invalid input

Schema: `ErrorResponse`

- `error` (string · required) — Error category: `validation_error`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited`, `server_error`, or `service_unavailable`.
- `message` (string · required) — Human-readable explanation, safe to show to end users.
- `code` (string · required) — Stable machine-readable code in `CATEGORY_NNN` form (e.g. `NOT_FOUND_006`, `AUTH_010`, `GEN_002`).
- `request_id` (string) — Correlation id for support requests. Echoes the `x-request-id` request header when one was supplied.
- `errors` (object) — Field-level validation messages keyed by field name. Present on 400/422 validation failures only.

### 401 — Unauthorized - Missing or expired token, an MCP OAuth token used against the REST API, or no access to this business

Schema: `ErrorResponse`

- `error` (string · required) — Error category: `validation_error`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited`, `server_error`, or `service_unavailable`.
- `message` (string · required) — Human-readable explanation, safe to show to end users.
- `code` (string · required) — Stable machine-readable code in `CATEGORY_NNN` form (e.g. `NOT_FOUND_006`, `AUTH_010`, `GEN_002`).
- `request_id` (string) — Correlation id for support requests. Echoes the `x-request-id` request header when one was supplied.
- `errors` (object) — Field-level validation messages keyed by field name. Present on 400/422 validation failures only.

### 403 — Forbidden - Insufficient permissions

Schema: `ErrorResponse`

- `error` (string · required) — Error category: `validation_error`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited`, `server_error`, or `service_unavailable`.
- `message` (string · required) — Human-readable explanation, safe to show to end users.
- `code` (string · required) — Stable machine-readable code in `CATEGORY_NNN` form (e.g. `NOT_FOUND_006`, `AUTH_010`, `GEN_002`).
- `request_id` (string) — Correlation id for support requests. Echoes the `x-request-id` request header when one was supplied.
- `errors` (object) — Field-level validation messages keyed by field name. Present on 400/422 validation failures only.

### 422 — Validation Error

Schema: `ErrorResponse`

- `error` (string · required) — Error category: `validation_error`, `unauthorized`, `forbidden`, `not_found`, `conflict`, `rate_limited`, `server_error`, or `service_unavailable`.
- `message` (string · required) — Human-readable explanation, safe to show to end users.
- `code` (string · required) — Stable machine-readable code in `CATEGORY_NNN` form (e.g. `NOT_FOUND_006`, `AUTH_010`, `GEN_002`).
- `request_id` (string) — Correlation id for support requests. Echoes the `x-request-id` request header when one was supplied.
- `errors` (object) — Field-level validation messages keyed by field name. Present on 400/422 validation failures only.

## Code samples

### cURL

```bash
curl -X PATCH 'https://api.ondayzero.com/api/v1/transactions/batch' \
  -H 'Authorization: Bearer dz_your_token' \
  -H 'x-business-id: YOUR_BUSINESS_ID' \
  -H 'Content-Type: application/json' \
  -d '{
  "rows": []
}'
```

### JavaScript

```javascript
const response = await fetch('https://api.ondayzero.com/api/v1/transactions/batch', {
  method: 'PATCH',
  headers: {
    Authorization: 'Bearer dz_your_token',
    'x-business-id': 'YOUR_BUSINESS_ID',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
  "rows": []
}),
});
const data = await response.json();
```

### Python

```python
import httpx

headers = {
    "Authorization": "Bearer dz_your_token",
    "x-business-id": "YOUR_BUSINESS_ID",
}

payload = {'rows': []}

response = httpx.patch("https://api.ondayzero.com/api/v1/transactions/batch", headers=headers, json=payload)
data = response.json()
```

## See also

- HTML version: https://www.ondayzero.com/docs/reference/transactions/bulk-edit-transactions
- OpenAPI slice: https://www.ondayzero.com/docs/reference/transactions/bulk-edit-transactions/openapi.json
- Other endpoints in **transactions**: https://www.ondayzero.com/docs/reference/transactions (markdown bundle: https://www.ondayzero.com/docs/reference/transactions.md, OpenAPI: https://www.ondayzero.com/docs/reference/transactions/openapi.json)
- Endpoint catalog: https://www.ondayzero.com/docs/reference/index.md
