> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://en.docs.api.corpx.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://en.docs.api.corpx.com/_mcp/server.

# Batch PIX guide

**Batch PIX** is for the case where you already have a spreadsheet — payroll,
supplier payouts, commissions, refunds — and want to pay everything at once
without writing a loop around `POST /pix/out`. You upload the rows, the API
checks keys, duplicates, balance and limits **before any debit**, and only
after your explicit acknowledgement does each row become a regular PIX out,
with the same limits, policies, webhooks and statement entries as
[PIX Out](/baas/guias/pagar/pix-out).

> **Batch vs. single PIX**
>
> Use a batch when the rows already exist together (a file, a closing) and you
> want one summary to approve and one report at the end. Use `POST /pix/out`
> when a payment is born from an isolated event and needs a synchronous
> answer. Both paths produce the same PIX and the same `pix.out.*` webhooks.

## How it works

```mermaid
flowchart LR
  T["GET template"] --> C["POST batches<br />DRAFT"]
  C --> I["POST items/csv<br />PUT items"]
  I --> R["POST review<br />REVIEWING → REVIEWED"]
  R -->|"INVALID items"| X["GET items/export?status=invalid<br />fix and resend"]
  X --> I
  R -->|"acknowledged: true"| D["POST dispatch<br />QUEUED → PROCESSING"]
  D --> P["1 PixOut per item<br />pix.out.completed / failed"]
  P --> F["COMPLETED | PARTIAL_FAILED | FAILED<br />pix.batch.completed"]
  F --> Z["POST receipts (ZIP)<br />GET items/export?status=failed"]
```

Three design decisions apply to the whole surface:

* **API only (M2M).** The routes require a credential with the
  `pix_out.create` scope (or legacy `api2/write`). A human user token cannot
  create or dispatch a batch; the Portal and Internet Banking do not expose
  this surface. A read-only credential (`read`) can **follow** batches
  (`GET`), download the template and receipts, but cannot create, edit or
  dispatch.
* **Two-level enablement.** CorpX turns on the `pix_batch` feature for the
  tenant **and** sets `pixBatch.enabled = true` in the account policy. Without
  the first the API answers `403 feature_disabled`; without the second,
  `403 pix_batch_account_disabled`. Ask your account manager to enable it,
  listing the accounts that will pay in batches.
* **Nothing leaves without review and acknowledgement.** Dispatch requires a
  `REVIEWED` batch, zero invalid items, enough balance and
  `acknowledged: true` in the body. The review summary exists to be shown to
  whoever approves.

### Prerequisites

| Item                                                                                                           | Where                                                           |
| -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| M2M credential with the `pix_out.create` scope, restricted to the paying accounts                              | [Authentication](/baas/guias/autenticacao/oauth2)               |
| `pix_batch` feature on the tenant and `pixBatch.enabled` in the account policy (enabled by CorpX)              | [Policies](/baas/guias/autenticacao/politicas)                  |
| Webhook subscription for `pix.batch.completed` and, for per-row detail, `pix.out.completed` / `pix.out.failed` | [Webhooks](/baas/guias/conta/webhooks)                          |
| If the account uses locks or a transaction PIN, the cash-out headers on dispatch                               | [Account security](/baas/guias/autenticacao/seguranca-da-conta) |

## Quick start

Five calls take a file from zero to paid. In the examples `$BASE` is
`https://tenant.api.corpx.com/v1/accounts/{accountId}` and the
`Authorization: Bearer $TOKEN` and `X-Tenant-Id` headers are implied.

#### Download the template

```bash
curl "$BASE/pix/batches/template" -o pix-lote-modelo.csv
```

#### Create the batch

```bash
curl -X POST "$BASE/pix/batches" \
  -H "Idempotency-Key: payroll-2026-09" \
  -H "Content-Type: application/json" \
  -d '{"name": "September payroll"}'
# 201 → {"batchId": "pxb_…", "status": "DRAFT", …}
```

#### Upload the rows

```bash
curl -X POST "$BASE/pix/batches/$BATCH/items/csv" \
  -H "Content-Type: text/csv" \
  --data-binary @payroll-september.csv
# 200 with the batch and its items; 400 with errors[] if any row is wrong (nothing is saved)
```

#### Review

```bash
curl -X POST "$BASE/pix/batches/$BATCH/review"      # 202, status REVIEWING
curl "$BASE/pix/batches/$BATCH"                      # repeat until status REVIEWED; read "review"
```

#### Approve and dispatch

```bash
curl -X POST "$BASE/pix/batches/$BATCH/dispatch" \
  -H "Content-Type: application/json" \
  -d '{"acknowledged": true}'
# 202, status QUEUED. Wait for the pix.batch.completed webhook.
```

## Routes

All under `/v1/accounts/{accountId}/pix/batches`. The **Scope** column is what
the credential needs; `read` follows, `pix_out.create` operates.

| Method and route                        | Scope                                | What it does                                                                                      |
| --------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------- |
| `GET /template`                         | `read`                               | Downloads `pix-lote-modelo.csv` with the columns and two example rows.                            |
| `POST /`                                | `pix_out.create`                     | Creates a `DRAFT` batch. `Idempotency-Key` required.                                              |
| `GET /`                                 | `read`                               | Lists the account's batches (`status`, `page`, `pageSize`).                                       |
| `GET /{batchId}`                        | `read`                               | Batch, `review`, `statistics` and paginated items (`status`, `reviewStatus`, `page`, `pageSize`). |
| `PUT /{batchId}/items`                  | `pix_out.create`                     | Adds 1 to 50 items as JSON.                                                                       |
| `POST /{batchId}/items/csv`             | `pix_out.create`                     | Adds items from a CSV (raw body, up to 5 MiB).                                                    |
| `DELETE /{batchId}/items/{itemId}`      | `pix_out.create`                     | Removes an item.                                                                                  |
| `POST /{batchId}/review`                | `pix_out.create`                     | Starts the asynchronous review.                                                                   |
| `POST /{batchId}/dispatch`              | `pix_out.create`                     | Dispatches the payments. Requires `acknowledged: true`.                                           |
| `GET /{batchId}/items/export`           | `read`                               | Correction CSV (`status=failed` default, `invalid`, `completed`, `all`; `separator=comma`).       |
| `GET /{batchId}/items/{itemId}/receipt` | `read`                               | PDF receipt of a `COMPLETED` item.                                                                |
| `POST /{batchId}/receipts`              | `pix_out.create` or `exports.create` | Job that builds the ZIP with every receipt.                                                       |

The field-by-field reference is in
[API Reference → Batch PIX](/baas/referencia).

## 1. The file

`GET .../pix/batches/template` returns `pix-lote-modelo.csv`: UTF-8 with BOM
and `;` as separator, which Excel in Portuguese locales opens directly. Its
content is exactly this:

```csv
identifier;amount;description;pixKeyType;pixKey;receiverName;receiverDocument;bankIspb;bankCode;branch;accountNumber;accountType
folha-set-001;1436.85;Pagamento setembro;CPF;12345678909;;;;;;;
folha-set-002;2500.00;Fornecedor;;;EMPRESA EXEMPLO LTDA;12345678000195;60701190;341;0001;123456;CHECKING
```

Each row is **one** PIX and fills **one** of the two receiver blocks:

| Column                                                                                               | Rule                                                                                                                                                                                                              |
| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `identifier`                                                                                         | Required. Your business key, up to 38 characters, unique within the batch **and** among the account's live payments (same rule as the PIX out `identifier`). Becomes the payment's `identifier` in the statement. |
| `amount`                                                                                             | Required and positive. Accepts `1234.56`, `1.234,56` or `R$ 1.234,56`.                                                                                                                                            |
| `description`                                                                                        | Optional, up to 140 characters. Travels with the PIX as its description.                                                                                                                                          |
| `pixKeyType`, `pixKey`                                                                               | **Key mode.** `pixKey` selects the mode; `pixKeyType` (`CPF`, `CNPJ`, `EMAIL`, `PHONE`, `EVP`) is optional and detected from the format. Receiver name, document and bank are resolved in DICT during review.     |
| `receiverName`, `receiverDocument`, `bankIspb`, `bankCode`, `branch`, `accountNumber`, `accountType` | **Bank account mode.** Name, CPF/CNPJ, ISPB **or** COMPE code, branch, account with check digit and type (`CHECKING`, `SAVINGS`, `PAYMENT`, `SALARY`; empty = `CHECKING`).                                        |

> **Parser rules that save rework**
>
> * The header is matched by **name**, regardless of order or case; columns
>   you do not use can be left empty or omitted.
> * `;` and `,` are detected automatically; quoting follows the CSV standard.
> * The `errorCode` and `errorMessage` columns of the correction file are
>   ignored on input: the exported file can be uploaded back as is.
> * A row with `pixKey` **and** bank account data at the same time is
>   rejected — pick one mode per row.

## 2. Create the batch

```bash
curl -X POST "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/batches" \
  -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: tenant-yourcompany" \
  -H "Idempotency-Key: payroll-2026-09" \
  -H "Content-Type: application/json" \
  -d '{"name": "September payroll"}'
```

* `Idempotency-Key` (up to 128 characters) is **required**; repeating the
  same key on the same account returns the existing batch with `200` instead
  of creating another. Use something that identifies the closing
  (`payroll-2026-09`), not a random UUID — that way a retry on your side
  never opens a duplicate batch.
* `name` is optional (up to 120 characters) and shows up in listings and in
  the receipts' `resumo.csv`.

`201` response:

```json
{
  "batchId": "pxb_3f9c2a1b7d4e5f60718293a4b5c6d7e8",
  "name": "September payroll",
  "status": "DRAFT",
  "totalItems": 0,
  "totalAmount": "0.00",
  "completedItems": 0,
  "failedItems": 0,
  "limits": { "maxItems": 50, "source": "default" },
  "statistics": {
    "total": 0, "pending": 0, "processing": 0, "completed": 0, "failed": 0,
    "reviewedOk": 0, "invalid": 0,
    "totalAmount": "0.00", "validAmount": "0.00", "completedAmount": "0.00", "failedAmount": "0.00",
    "progressPercentage": 0
  },
  "items": [],
  "page": 1, "pageSize": 50, "totalPages": 0, "hasNext": false,
  "createdAt": "2026-09-30T13:00:00Z",
  "updatedAt": "2026-09-30T13:00:00Z"
}
```

`limits.maxItems` is the maximum size of this batch **on this account** and
`limits.source` says where it came from (`default` = 50, `tenant` or
`account`, up to 5,000). Monetary values are always **decimal strings** with
two places (`"1436.85"`), never floating-point numbers.

## 3. Upload the items

You may mix both methods in the same batch, across as many calls as you
need, while the batch is `DRAFT` or `REVIEWED`.

#### CSV

```bash
curl -X POST ".../pix/batches/{batchId}/items/csv" \
  -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: tenant-yourcompany" \
  -H "Content-Type: text/csv" \
  --data-binary @payroll-september.csv
```

The body is the raw file (up to 5 MiB; above that, `413`). The upload is
**atomic**: if any row is invalid the response is `400 pix_batch_csv_invalid`
with the list of problems and **nothing is saved**.

```json
{
  "errorCode": "pix_batch_csv_invalid",
  "message": "one or more rows are invalid; nothing was saved",
  "errors": [
    { "line": 3, "field": "amount", "message": "must be a positive amount" },
    { "line": 7, "field": "pixKey", "message": "use either pixKey or the bank account columns, not both" },
    { "line": 9, "field": "identifier", "message": "duplicated identifier (first seen on line 4)" }
  ],
  "docs": "https://docs.api.corpx.com/baas/guias/referencia/erros#pix_batch_csv_invalid"
}
```

`line` counts the header as line 1, so `3` is the second data row — the same
number Excel shows. A file with more rows than `limits.maxItems` is
`400 pix_batch_too_large`.

#### JSON

```bash
curl -X PUT ".../pix/batches/{batchId}/items" \
  -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: tenant-yourcompany" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "identifier": "payroll-sep-001", "amount": "1436.85", "description": "September payroll",
        "key": "12345678909", "keyType": "CPF" },
      { "identifier": "payroll-sep-002", "amount": 2500.00, "description": "Supplier",
        "name": "EMPRESA EXEMPLO LTDA", "documentNumber": "12345678000195",
        "bankIspb": "60701190", "accountBranch": "0001", "accountNumber": "123456",
        "accountType": "CHECKING" }
    ]
  }'
```

**1 to 50 items per call**, with the same vocabulary as `POST /pix/out`:
`identifier`, `amount` (string or number), `description`, and `key`/`keyType`
**or** `name`, `documentNumber`, `bankIspb` or `bankCode`, `accountBranch`,
`accountNumber`, `accountType`. For large batches, send chunks of 50 in
sequence — every call returns the updated batch.

An empty chunk, more than 50 items, or any invalid item is
`400 pix_batch_chunk_invalid` with the same `errors[]` envelope as the CSV
(`line` is the 1-based position in the array) and nothing is saved. An
`identifier` repeated within the chunk itself is flagged too.

### Rules common to both methods

* **Per-row idempotency.** Repeating an `identifier` with the **same**
  content is ignored (no duplicate, no error). Repeating it with
  **different** content is `409 pix_batch_item_conflict` and the whole call
  is refused — fix the file instead of relying on overwrites.
* **Removing.** `DELETE .../items/{itemId}` removes an item; the `itemId`
  (`pxi_…`) comes in the batch's `items[]`.
* **Editable states.** Only `DRAFT` and `REVIEWED` accept items or
  removals. Any change to a `REVIEWED` batch sends it back to `DRAFT` and
  resets the review: you must review again before dispatching. While
  `REVIEWING` or after dispatch, `409 pix_batch_not_editable`.
* **Size.** The batch's item count never exceeds `limits.maxItems`; the call
  that would cross the ceiling is refused as a whole with
  `400 pix_batch_too_large`.

## 4. Review

```
POST /v1/accounts/{accountId}/pix/batches/{batchId}/review   → 202, status REVIEWING
```

The review is asynchronous and runs **before any debit**. Repeating the
`POST` while `REVIEWING` just returns the current state. Per item, in this
order:

| Check                                                                                | `reviewError.errorCode`                            |
| ------------------------------------------------------------------------------------ | -------------------------------------------------- |
| Positive amount                                                                      | `invalid_amount`                                   |
| `identifier` already used by a live payment on the account (including another batch) | `identifier_conflict`                              |
| Amount above the account's per-transaction limit                                     | `limit_exceeded_transaction`                       |
| PIX key missing, inactive or malformed (DICT lookup)                                 | `key_not_found`, `key_inactive`, `invalid_pix_key` |
| DICT unavailable after 3 attempts                                                    | `dict_lookup_failed`                               |

In key mode, DICT resolves the receiver's **name, document and bank**, which
then appear in `items[].receiver` — that is what you show to the approver.
Lookups use the account's 24 h cache and **count against the DICT lookup
window** like `GET /pix/key/{pixKey}`; read
[PIX key lookups](/baas/guias/pagar/consulta-dict) before reviewing
thousands of new keys. Bank-account items are accepted as sent; destination
account validation happens at the settlement bank during execution.

Follow with `GET .../pix/batches/{batchId}`. When `status` becomes
`REVIEWED`, `review` carries the summary:

```json
{
  "reviewedAt": "2026-09-30T13:09:30Z",
  "totalItems": 120, "validItems": 118, "invalidItems": 2,
  "totalAmount": "143685.00", "validAmount": "141200.00",
  "balance": { "available": "150000.00", "sufficient": true, "checked": true },
  "limits": [
    { "window": "daily_out", "enforced": true, "limit": "500000.00", "used": "12000.00", "available": "488000.00", "sufficient": true },
    { "window": "monthly", "enforced": false, "limit": "0.00", "used": "0.00", "available": "0.00", "sufficient": true }
  ],
  "warnings": []
}
```

* `balance` and `limits` are a **snapshot**. Balance is checked again at
  dispatch and every PIX goes through the per-window limits during execution.
  `limits[].window` is `daily_out` (6am–8pm) or `nightly_out` (8pm–6am)
  depending on the review time, plus `monthly`; `enforced: false` means the
  account has no ceiling configured for that window.
* `balance.checked: false` means the settlement bank did not answer; the
  warning goes to `warnings[]` and does not block.
* `warnings[]` **does not block** the acknowledgement (e.g. insufficient
  balance right now — funds may arrive before dispatch). `INVALID` items
  **do block**.
* If the review cannot run, the batch goes back to `DRAFT` and `review`
  carries an `error` field; just call `review` again.

A rejected item looks like this in `items[]`:

```json
{
  "itemId": "pxi_9b1c…", "line": 17, "identifier": "payroll-sep-017",
  "mode": "KEY", "amount": "980.00", "description": "September payroll",
  "status": "PENDING", "reviewStatus": "INVALID",
  "receiver": { "name": "", "documentNumber": "", "keyType": "EMAIL", "key": "former-employee@company.com", "bankCode": "", "bankIspb": "" },
  "reviewError": { "errorCode": "key_not_found", "message": "chave PIX não encontrada no DICT" },
  "createdAt": "2026-09-30T13:02:11Z", "updatedAt": "2026-09-30T13:09:28Z"
}
```

To fix, download only the rejected rows, adjust and upload again (the same
`identifier` with new content is refused — remove the item first with
`DELETE`, or use a new `identifier`), then review once more:

```
GET /v1/accounts/{accountId}/pix/batches/{batchId}/items/export?status=invalid
GET /v1/accounts/{accountId}/pix/batches/{batchId}?reviewStatus=invalid
```

## 5. Dispatch

```bash
curl -X POST ".../pix/batches/{batchId}/dispatch" \
  -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: tenant-yourcompany" \
  -H "X-Acting-Document: 12345678909" \
  -H "X-Transaction-Pin: 123456" \
  -H "Content-Type: application/json" \
  -d '{"acknowledged": true}'
```

`acknowledged: true` is the statement that the review summary was read and
approved — record on your side who approved and when. Without it the API
answers `409 pix_batch_acknowledgement_required`. Dispatch also requires, in
this order:

1. A `REVIEWED` batch (`409 pix_batch_not_reviewed`).
2. At least one item (`409 pix_batch_empty`) and a total within
   `limits.maxItems` (`400 pix_batch_too_large`).
3. No `INVALID` item and every item reviewed
   (`409 pix_batch_has_invalid_items` / `409 pix_batch_not_reviewed`).
4. Available balance **greater than or equal to the batch total** at dispatch
   time (`422 pix_batch_insufficient_balance` — nothing is started).

> **Dispatch moves money**
>
> Dispatch is a cash-out route: the account's outgoing locks, the transaction
> PIN and the `X-Acting-Document` / `X-Transaction-Pin` / `X-Acting-Ip`
> headers apply exactly as in `POST /pix/out` (see
> [Account security](/baas/guias/autenticacao/seguranca-da-conta)). The
> headers are only needed when the account has a PIN or locks configured.
> After the `202` there is **no cancellation**: items already in flight run
> until they settle or fail.

`202` response with `status: QUEUED` and `dispatchedAt`. Re-dispatching an
already dispatched batch is idempotent (`200` with the current state), so a
retry after a network timeout is safe.

## 6. Follow up

Each item becomes its own `PixOut.Workflow` — the same one that serves
`POST /pix/out` — with the row's `identifier` and idempotency
`{batchId}_{itemId}`. It goes through the same policies, per-window limits
and statement entry as a single PIX, and emits the same
`pix.out.completed`, `pix.out.failed` or `pix.out.timeout`. The batch pays
up to 5 items in parallel (CorpX setting) and only moves on when the
settlement bank answers; a slow item does not hold up the others.

### Batch states

| `status`         | Meaning                                                             | Editable? |
| ---------------- | ------------------------------------------------------------------- | --------- |
| `DRAFT`          | Receiving items.                                                    | Yes       |
| `REVIEWING`      | Review in progress.                                                 | No        |
| `REVIEWED`       | Reviewed; ready for acknowledgement. Any edit goes back to `DRAFT`. | Yes       |
| `QUEUED`         | Dispatch accepted; waiting for the processor.                       | No        |
| `PROCESSING`     | Items being paid. `statistics` advances.                            | No        |
| `COMPLETED`      | Every item settled.                                                 | No        |
| `PARTIAL_FAILED` | Some settled, some failed. Download the failures export.            | No        |
| `FAILED`         | No item settled.                                                    | No        |

### Item states

| `status`     | `reviewStatus`   | When                                                                                                                                                    |
| ------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PENDING`    | `PENDING`        | Just added, not reviewed yet.                                                                                                                           |
| `PENDING`    | `OK` / `INVALID` | Reviewed; `INVALID` carries `reviewError`.                                                                                                              |
| `PROCESSING` | `OK`             | PixOut in flight.                                                                                                                                       |
| `COMPLETED`  | `OK`             | Settled: `paymentId`, `endToEndId`, `transactionId`, `completedAt`.                                                                                     |
| `FAILED`     | `OK`             | Rejected or expired: `error.errorCode` and `error.message` with the single-PIX vocabulary (`insufficient_funds`, `limit_exceeded_daily`, `timeout`, …). |

`GET .../pix/batches/{batchId}` returns `statistics` and the paginated items
(`page`, `pageSize` up to 200), filterable by `status`
(`pending`, `processing`, `completed`, `failed`) and `reviewStatus`
(`pending`, `ok`, `invalid`):

```json
{
  "batchId": "pxb_3f9c2a1b7d4e5f60718293a4b5c6d7e8",
  "status": "PROCESSING",
  "totalItems": 120, "totalAmount": "141200.00",
  "completedItems": 87, "failedItems": 2,
  "statistics": {
    "total": 120, "pending": 0, "processing": 31, "completed": 87, "failed": 2,
    "reviewedOk": 120, "invalid": 0,
    "totalAmount": "141200.00", "validAmount": "141200.00",
    "completedAmount": "102340.00", "failedAmount": "1960.00",
    "progressPercentage": 74.17
  },
  "items": [
    {
      "itemId": "pxi_1a2b…", "line": 1, "identifier": "payroll-sep-001",
      "mode": "KEY", "amount": "1436.85", "description": "September payroll",
      "status": "COMPLETED", "reviewStatus": "OK",
      "receiver": { "name": "JOAO ANTONIO DA CONCEICAO", "documentNumber": "12345678909", "keyType": "CPF", "key": "12345678909", "bankCode": "341", "bankIspb": "60701190" },
      "paymentId": "pay_1f2e3d4c", "endToEndId": "E5087192120260930131200abcdef0123",
      "transactionId": "9f8e7d6c-5b4a-4321-8765-43210fedcba9",
      "completedAt": "2026-09-30T13:12:01Z",
      "createdAt": "2026-09-30T13:02:11Z", "updatedAt": "2026-09-30T13:12:01Z"
    }
  ],
  "page": 1, "pageSize": 50, "totalPages": 3, "hasNext": true,
  "reviewedAt": "2026-09-30T13:09:30Z",
  "acknowledgedAt": "2026-09-30T13:10:02Z",
  "dispatchedAt": "2026-09-30T13:10:02Z",
  "createdAt": "2026-09-30T13:00:00Z", "updatedAt": "2026-09-30T13:12:01Z"
}
```

### Closing webhook

At the end the API sends **`pix.batch.completed`**. It closes the batch and
**does not replace** the per-item events: to reconcile row by row use
`pix.out.completed` / `pix.out.failed` (the event's `identifier` is your
row's) or the item list.

```json
{
  "id": "pix-batch-pxb_3f9c2a1b7d4e5f60718293a4b5c6d7e8-completed",
  "type": "pix.batch.completed",
  "occurredAt": "2026-09-30T13:12:40.000Z",
  "schemaVersion": "1.0",
  "environment": "production",
  "tenantId": "tenant-yourcompany",
  "accountId": "acc_123456",
  "data": {
    "batchId": "pxb_3f9c2a1b7d4e5f60718293a4b5c6d7e8",
    "accountId": "acc_123456",
    "status": "PARTIAL_FAILED",
    "statistics": {
      "total": 120, "completed": 118, "failed": 2,
      "totalAmount": "141200.00", "completedAmount": "139240.00", "failedAmount": "1960.00"
    }
  }
}
```

Subscribe with `authType: HMAC`, validate `X-Signature` over the raw bytes
and deduplicate by `id` — details in [Webhooks](/baas/guias/conta/webhooks).

> **Webhook first, polling as a fallback**
>
> Prefer reacting to `pix.batch.completed`. If you need polling (a progress
> bar, for instance), call `GET .../pix/batches/{batchId}` every 10–30 s and
> read only `status` and `statistics`; do not list the items on every cycle.

## 7. Fix and resend

```
GET /v1/accounts/{accountId}/pix/batches/{batchId}/items/export?status=failed
```

Returns, in the **same format as the template**, only the rows that failed
during execution or were marked invalid in the review, with two extra
columns at the end: `errorCode` and `errorMessage`. The `X-Total-Rows` header
tells how many rows came back.

```csv
identifier;amount;description;pixKeyType;pixKey;receiverName;receiverDocument;bankIspb;bankCode;branch;accountNumber;accountType;errorCode;errorMessage
payroll-sep-017;980.00;September payroll;EMAIL;former-employee@company.com;;;;;;;;key_not_found;chave PIX não encontrada no DICT
payroll-sep-044;1200.00;September payroll;;;MARIA DA SILVA;98765432100;;001;1234;56789-0;CHECKING;limit_exceeded_daily;limite diário de saída excedido
```

Fix it and upload the file **as is** into a **new** batch — the parser
ignores the error columns. An `identifier` that failed can be reused; one
that settled cannot (the review would flag it as `identifier_conflict`),
which protects you from paying the same row twice.

| `status=`          | Rows included                                                              |
| ------------------ | -------------------------------------------------------------------------- |
| `failed` (default) | Failed during execution **or** rejected in the review.                     |
| `invalid`          | Only the ones rejected in the review (use while the batch is `REVIEWED`).  |
| `completed`        | Only the settled ones — handy to reconcile by `endToEndId`.                |
| `all`              | Every row, with empty `errorCode`/`errorMessage` where there was no error. |

`separator=comma` swaps `;` for `,` for English-locale tools.

## 8. Receipts

Receipts are CorpX-standard PDFs (tenant logo when configured, payer,
receiver with masked document, amount, description, `identifier`,
`endToEndId`, settlement time in Brasília time).

**One item** — direct `application/pdf` response, only for `COMPLETED` items
(`409 pix_batch_receipt_unavailable`):

```
GET /v1/accounts/{accountId}/pix/batches/{batchId}/items/{itemId}/receipt
```

**Whole batch** — for finished batches (`COMPLETED`, `PARTIAL_FAILED` or
`FAILED` with at least one settled item; otherwise `409 pix_batch_no_receipts`):

```bash
curl -X POST ".../pix/batches/{batchId}/receipts" \
  -H "Authorization: Bearer $TOKEN" -H "X-Tenant-Id: tenant-yourcompany"
```

```json
{
  "exportId": "exp_7c1d2e3f-…",
  "accountId": "acc_123456",
  "batchId": "pxb_3f9c2a1b7d4e5f60718293a4b5c6d7e8",
  "type": "pix_batch_receipts",
  "format": "zip",
  "status": "PENDING",
  "receipts": 118,
  "download": "/v1/accounts/acc_123456/exports/exp_7c1d2e3f-…/download",
  "createdAt": "2026-09-30T13:15:00Z"
}
```

The ZIP is built in the background. Call the route in `download`: while the
job runs it answers `409 not_ready`; when done, it returns a presigned
`downloadUrl` (valid for `expiresIn` seconds) and `checksumMd5`. The archive
holds one PDF per settled item and a `resumo.csv` with the columns `linha`,
`identificador`, `destinatario`, `valor`, `endToEnd`, `data`, `arquivo` and
`erro` (filled only when a PDF could not be rendered).

PDFs follow the pattern
`{YYYY-MM-DD}_{HHMM}_PIX_{amount}_{receiver}_{identifier}_{endToEnd}.pdf`
— for example
`2026-09-30_1312_PIX_1436-85_JOAO-ANTONIO-DA-CONCEICAO_payroll-sep-001_E5087192120260930131200abcdef0123.pdf`
— without accents and with a `-2`, `-3`… suffix on collision.

## Best practices

* **One `Idempotency-Key` per closing.** `payroll-2026-09`, `suppliers-2026-09-30`… A retry of the create call never opens a second batch, and you can find the batch again from your own calendar.
* **Deterministic, per-row unique `identifier`.** Build it from your data (`{payroll}-{employeeId}`), never from an in-memory counter. It is what ties the PIX to your system in the statement, the webhooks and the correction file — and what prevents paying the same row twice.
* **Always review before asking for approval.** Show `review` (totals, balance, limits, invalid rows) to the approver and only then send `acknowledged: true`. Store who approved, when, and the `reviewedAt` that was on screen.
* **Separate who builds from who dispatches.** A `read` credential can follow; only the `pix_out.create` credential dispatches. If the account uses a transaction PIN, dispatch requires the approving operator's PIN.
* **Treat `PARTIAL_FAILED` as routine, not an exception.** Deleted keys and closed accounts happen in any large payroll. Download `items/export?status=failed`, fix, and upload a new batch; do not redo the whole batch.
* **Reconcile by `identifier` and `endToEndId`.** Each item's `pix.out.completed` and its row in `items[]` carry both; the `endToEndId` is the proof of payment with the Central Bank.
* **Respect the limit windows.** `review` shows `daily_out`/`nightly_out` and `monthly`. A batch larger than the day's available limit is not blocked at dispatch — the excess items fail during execution with `limit_exceeded_*`. Split into per-day batches or ask for a limit adjustment.
* **Large batches.** For thousands of rows, upload by CSV (one call) instead of dozens of JSON chunks, and ask CorpX for the right `maxItems` before the closing.

## FAQ

#### Can I cancel a batch after dispatch?

No. After the `202` the items are already executing at the settlement bank.
What you control is the moment of acknowledgement: until `dispatch` the
batch is just a draft and can be edited or simply abandoned in
`DRAFT`/`REVIEWED`.

#### What happens if I edit a REVIEWED batch?

It goes back to `DRAFT` and every item returns to `reviewStatus: PENDING`.
You must call `review` again. This applies to adding, removing or resending
an item — the review always reflects the current content.

#### Balance was enough at review time but dispatch returned 422. Why?

`review.balance` is a snapshot; another payment may have left between the
review and the acknowledgement. Dispatch checks the balance again and refuses
**without starting anything** if it is below the batch total. Top up the
account or remove rows and review again.

#### Why did an item pass review and fail during execution?

The review validates what can be validated without moving money: key,
duplicates, amount, per-transaction limit, balance and windows in
aggregate. Execution adds the settlement bank's and destination bank's rules
(closed account, daily limit consumed by other payments, account policy).
The error code is the same as for a single PIX and comes in
`error.errorCode`.

#### Can I reuse an identifier?

An item `identifier` that **failed** can be used in a new batch. One that
**settled** cannot: the review flags `identifier_conflict`, because the
account has a live payment with that identifier. That is the duplicate
payment protection — do not work around it with random suffixes.

#### Do batch items show up in the statement?

Yes, as individual PIX out entries, each with its row's `identifier`. There
is no aggregated batch entry; `GET .../pix/batches/{batchId}` and the
receipts' `resumo.csv` are the consolidated view.

#### Does the review consume DICT quota?

Yes, one lookup per distinct key not present in the account's 24 h cache,
counted in the same window as `GET /pix/key/{pixKey}`. Keys repeated within
the batch and keys looked up recently do not trigger a new lookup. Bank
account rows involve no DICT lookup.

#### How do I test before production?

Create a small batch, upload two or three rows of token amounts to accounts
of your own, review and dispatch — the flow and webhooks are identical to a
batch of thousands. To validate only the file without paying anything,
stop after `review`: the parser and the review already surface every
format, key and duplicate problem.

## Errors

| Code                                                      | HTTP | When                                                                                   |
| --------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- |
| `feature_disabled`                                        | 403  | Tenant without the `pix_batch` feature.                                                |
| `pix_batch_account_disabled`                              | 403  | Account without `pixBatch.enabled = true` in its policy.                               |
| `missing_idempotency_key`                                 | 400  | `POST /pix/batches` without `Idempotency-Key`.                                         |
| `pix_batch_not_found` / `pix_batch_item_not_found`        | 404  | Batch or item does not exist on this account.                                          |
| `pix_batch_chunk_invalid` / `pix_batch_csv_invalid`       | 400  | Upload rejected; `errors[]` points to row and field. `413` when the CSV exceeds 5 MiB. |
| `pix_batch_too_large`                                     | 400  | Above `limits.maxItems`.                                                               |
| `pix_batch_item_conflict`                                 | 409  | `identifier` repeated with different content.                                          |
| `pix_batch_not_editable`                                  | 409  | Batch is `REVIEWING` or already dispatched.                                            |
| `pix_batch_empty`                                         | 409  | Batch has no items.                                                                    |
| `pix_batch_not_reviewed`                                  | 409  | Dispatch without a valid review.                                                       |
| `pix_batch_has_invalid_items`                             | 409  | Review flagged invalid items.                                                          |
| `pix_batch_acknowledgement_required`                      | 409  | Dispatch without `acknowledged: true`.                                                 |
| `pix_batch_insufficient_balance`                          | 422  | Available balance below the total at dispatch.                                         |
| `pix_batch_no_receipts` / `pix_batch_receipt_unavailable` | 409  | Batch not finished or item not settled.                                                |
| `receipt_render_failed`                                   | 500  | PDF rendering failed; retry the call.                                                  |
| `workflow_unavailable`                                    | 503  | Orchestration unavailable; retry the call.                                             |

The full list, with anchors, is in
[Error handling](/baas/guias/referencia/erros).

## See also

* [PIX Out](/baas/guias/pagar/pix-out) — the single payment every batch row executes.
* [PIX key lookups](/baas/guias/pagar/consulta-dict) — DICT quota and cache used by the review.
* [Account security](/baas/guias/autenticacao/seguranca-da-conta) — locks, transaction PIN and cash-out headers.
* [Idempotency](/baas/guias/comece-aqui/idempotencia) — how `Idempotency-Key` behaves across the API.
* [Webhooks](/baas/guias/conta/webhooks) — `pix.batch.completed` and the per-item `pix.out.*` events.