> ## Documentation Index
> Fetch the complete documentation index at: https://developers.bloobank.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pix cash-out

> Build a complete outbound PIX flow — create, approve (or cancel), submit, reconcile via webhook.

Outbound flows differ from inbound in one important way: they are **two-step**. `POST` creates the order in `AWAITING_APPROVAL`; a subsequent `PUT .../approve` submits it to the PIX network. The pause between the two is where you implement dual-control, balance checks, or anti-fraud rules.

For other instrument types, see [Instrument types](/get-started/payments/instrument-types). For the underlying state machine, see [Payment lifecycle](/get-started/payments/lifecycle).

## The flow

<Steps>
  <Step title="Generate an idempotencyKey for the operation">
    Tie it to the business intent — typically the payout id, prefixed unambiguously.

    ```text theme={"dark"}
    idempotencyKey = "payout-2026-0309"
    ```

    Persist locally **before** the API call.
  </Step>

  <Step title="Pre-flight balance check">
    Outbound orders are denied if `available < amount`. Check first to fail fast.

    ```http theme={"dark"}
    GET /wallets/production-main/balance HTTP/1.1
    ```

    Compare `available.value` to your intended `amount`. If insufficient, do not even attempt the create — surface the shortage to the operator.
  </Step>

  <Step title="Create the order (AWAITING_APPROVAL)">
    ```http theme={"dark"}
    POST /wallets/production-main/paymentOrders HTTP/1.1
    Content-Type: application/json

    {
      "direction":      "OUT",
      "network":        "br.gov.bcb.pix",
      "idempotencyKey": "payout-2026-0309",
      "amount":         15000,
      "currency":       "BRL",
      "instrument":     {
        "type":     "PIX_CASH_OUT_KEY",
        "keyType":  "CPF",
        "keyValue": "12345678901"
      },
      "metadata": {
        "payoutId": "2026-0309"
      }
    }
    ```

    Response: `201 Created` with `status: "AWAITING_APPROVAL"`. **No money has moved yet** — the wallet's `available` is unchanged.

    ```json theme={"dark"}
    {
      "id":              "ord_4LqGwCxZzOrNyB8fHcSeK",
      "status":          "AWAITING_APPROVAL",
      "direction":       "OUT",
      "network":         "br.gov.bcb.pix",
      "amount":          15000,
      "currency":        "BRL",
      "idempotencyKey":  "payout-2026-0309",
      "instrument": {
        "type":     "PIX_CASH_OUT_KEY",
        "keyType":  "CPF",
        "keyValue": "12345678901"
      },
      "createdAt":  "2026-01-15T11:00:00.000Z"
    }
    ```
  </Step>

  <Step title="Approve to submit (or cancel to drop)">
    **To approve and submit:**

    ```http theme={"dark"}
    PUT /wallets/production-main/paymentOrders/ord_4Lq.../approve HTTP/1.1
    ```

    Response: `200 OK` with `status: "PENDING"`. The PIX network is now processing. The wallet's `locked` increases by the order amount.

    **To cancel before submission:**

    ```http theme={"dark"}
    PUT /wallets/production-main/paymentOrders/ord_4Lq.../cancel HTTP/1.1
    ```

    Response: `200 OK` with `status: "CANCELED"`. The order never reaches the PIX network. `available` and `locked` are unchanged.

    Both transitions require the order to be in `AWAITING_APPROVAL` — any other state returns `PAYMENT_ORDER_NOT_AWAITING_APPROVAL` (for approve) or `PAYMENT_ORDER_INVALID_STATE` (for cancel).
  </Step>

  <Step title="Wait for settlement via webhook">
    On settlement:

    ```json theme={"dark"}
    {
      "event":      "payment_order.success",
      "messageId":  "msg_9I3lQrS0aU5yWn",
      "occurredAt": "2026-01-15T11:00:03.214Z",
      "data": {
        "id":              "ord_4LqGwCxZzOrNyB8fHcSeK",
        "status":          "SUCCESS",
        "direction":       "OUT",
        "amount":          15000,
        "currency":        "BRL",
        "idempotencyKey":  "payout-2026-0309",
        "instrument": {
          "type":       "PIX_CASH_OUT_KEY",
          "keyType":    "CPF",
          "keyValue":   "12345678901",
          "endToEndId": "E60746948202601151100001B2C3D4E",
          "creditorAccount": {
            "ispb":                 "60746948",
            "bankCode":             "237",
            "bankName":             "Banco Bradesco S.A.",
            "agencyNumber":         "1234",
            "accountType":          "CHECKING_ACCOUNT",
            "accountNumber":        "987654-3",
            "accountOwnerName":     "Recipient Name",
            "accountOwnerDocument": "12345678901"
          }
        },
        "processedAt": "2026-01-15T11:00:03.214Z"
      }
    }
    ```

    `locked` decreases; `amount` decreases by the order amount.

    On failure (`payment_order.failed` event), `locked` decreases and `amount` is unchanged — funds are released:

    ```json theme={"dark"}
    {
      "event": "payment_order.failed",
      "data": {
        "id": "ord_4Lq...",
        "status": "FAILED",
        "errorCode":    "INSUFFICIENT_FUNDS",
        "errorMessage": "Insufficient available balance to complete this transfer."
      }
    }
    ```
  </Step>
</Steps>

## Dual control — common patterns

`AWAITING_APPROVAL` is the design hook for human or automated approval gates. Two common patterns:

### Pattern A — operator review

Operators see all `AWAITING_APPROVAL` orders in a dashboard, click "approve" or "cancel" individually. Use this when each payment carries enough variance that human judgment is valuable.

```http theme={"dark"}
GET /wallets/production-main/paymentOrders?filter=status%3DAWAITING_APPROVAL&order_by=createdAt&page_size=50 HTTP/1.1
```

### Pattern B — automated rule engine

A separate service consumes new orders (via webhook on `payment_order.created`), applies business rules (limits, anti-fraud, beneficiary checks), and calls `approve` or `cancel` automatically. Use this for high-volume, low-variance flows like payroll.

The benefit of separating `create` from `approve`: the approver service can use a **different credential** with stricter RBAC than the creator service. The first service can create payouts at scale; only the second can submit them.

## Recipient resolution

For `PIX_CASH_OUT_KEY`, the recipient account is **resolved by the provider** after submission. You do not need to know the bank or account number — just the PIX key. The resolved `creditorAccount` appears in the order response after settlement.

If the key cannot be resolved (recipient does not exist, key is malformed), the order finalizes as `FAILED` with `errorCode: "RECIPIENT_INVALID"`. The funds are never debited from your wallet.

## Cancel after approve

Once you call `approve`, the order moves to `PENDING` and you can no longer cancel it. The order will reach a final state (`SUCCESS` or `FAILED`) on the PIX network's timeline — typically seconds.

If you must reverse an outbound payment after it settled, you need a separate refund flow (request via the PIX network), not a cancel.

## Reconciliation

Per outbound order, your local record should accumulate:

| Field                     | Source                                                 |
| ------------------------- | ------------------------------------------------------ |
| Order id                  | API response on create                                 |
| Idempotency key           | Generated by you, stored locally                       |
| Status timeline           | `AWAITING_APPROVAL` → `PENDING` → `PROCESSING` → final |
| End-to-end id             | Webhook `SUCCESS` payload                              |
| Resolved creditor account | Webhook `SUCCESS` payload                              |
| Error code                | Webhook `FAILED` payload                               |

Match `payout-2026-0309` (your local id) to `ord_4Lq...` (BlooBank id) to `E60746948...` (PIX network id) — three layers of identity that collectively define the reconciliation chain.

## Common errors

| Reason                                                    | Cause                                                    | Fix                                                                    |
| --------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------- |
| `INSUFFICIENT_FUNDS` (in order `errorCode`)               | `available` was less than `amount` at submit time.       | Check balance first; do not bypass the gate.                           |
| `RECIPIENT_INVALID` (in order `errorCode`)                | PIX key could not be resolved or account is closed.      | Verify the key value with the recipient.                               |
| `PAYMENT_ORDER_NOT_AWAITING_APPROVAL` (HTTP 422)          | `approve` called on an order not in `AWAITING_APPROVAL`. | Verify state via `GET` first.                                          |
| `PAYMENT_ORDER_INVALID_STATE` (HTTP 422)                  | `cancel` called on an order not in `AWAITING_APPROVAL`.  | Once approved, the order cannot be canceled — wait for terminal state. |
| `IDEMPOTENCY_KEY_IN_USE_WITH_DIFFERENT_PARAMS` (HTTP 422) | The key was already used with a different body.          | Pick a new key.                                                        |

See [Error catalog](/get-started/errors/error-catalog) for the full list.

## Next

<CardGroup cols={3}>
  <Card title="Pix cash-in" icon="arrow-down-to-line" href="/get-started/payments/pix-cash-in">
    The inbound counterpart.
  </Card>

  <Card title="Instrument types" icon="layer-group" href="/get-started/payments/instrument-types">
    KEY, EMV, ACCOUNT — when to use each.
  </Card>

  <Card title="Webhooks overview" icon="webhook" href="/get-started/webhooks/overview">
    Receiving lifecycle events.
  </Card>
</CardGroup>
