# PIX Cash-Out Copy and Paste

Performs a PIX transfer using the EMV code (copy and paste) extracted from a PIX QR Code.

## Endpoint

```
POST /api/external/pix/cash-out
```

## Headers

| Header | Type | Required | Description |
|--------|------|----------|-------------|
| `Authorization` | String | Yes | `ApiKey {client_id}:{client_secret}` |
| `Content-Type` | String | Yes | `application/json` |
| `hmac` | String | Yes | HMAC-SHA512 signature of the body (hex) |
| `Idempotency-Key` | String | No | Unique key to prevent duplicate processing (max 256 chars). Behavior identical to what is documented in [PIX Cash-Out by Key](/en/pix-cashout-key#headers) |

::: danger Required permission
The API Key **must** have the `transfer:write` permission to send PIX. Without it, the request returns `403 Forbidden`. See [how to configure permissions](/en/auth-token#permissions).
:::

## Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `amount` | Integer | Yes | Amount in **centavos**. R$ 30.00 = `3000`. **On dynamic QR, this value is overridden by the lookup to the QR URL** - see below |
| `emv` | String | Yes | EMV copy-and-paste code from the PIX QR Code. The alias `codigo_copia_cola` is also accepted but `emv` is canonical - prefer it |
| `description` | String | No | Transfer description (max 140 characters) |
| `external_id` | String | No | Your system identifier for tracking. Max 128 chars after `trim`. Only `a-zA-Z0-9._:-` characters. Returned in responses and webhooks. **Invalid values are silently discarded** (the transaction proceeds with `external_id: null`). See [PIX Cash-Out by Key](/en/pix-cashout-key#request-body) for details. |
| `end_to_end_id` | String | No | End-to-End ID in BACEN format. **Recommended to omit** - the backend generates a deterministic one. Only send in coordinated reprocessing scenarios |
| `recipient_document` | String | No* | CPF (11 digits) or CNPJ (14 digits) of the recipient; formatted input is accepted and normalized to digits. **Optional by default**; becomes **conditionally required** when the **opt-in** "block PIX-OUT to CNPJ" control is enabled on the account and the key extracted from the EMV is not a CPF/CNPJ. Same behavior and errors (`cnpj_out_document_required`, `cnpj_out_blocked`) as [PIX Cash-Out by Key](/en/pix-cashout-key#opt-in-control-block-pix-out-to-cnpj). |

::: tip Copy-and-Paste code
The `emv` field accepts the full EMV code copied from a PIX QR Code. The API automatically extracts the PIX key, recipient data, and the original charge amount from the EMV payload. The backend validates CRC-16/CCITT-FFFF and the TLV structure before proceeding.
:::

::: info Opt-in control - no default impact
`recipient_document` is only relevant when the **optional, per-account control that is OFF by default** "block PIX-OUT to CNPJ" is enabled on the paying account. While it is disabled (default), the field is ignored and no existing integration is affected. Full behavior and error codes in [PIX Cash-Out by Key](/en/pix-cashout-key#opt-in-control-block-pix-out-to-cnpj) and [Account Controls](/en/account-controls).
:::

### Static QR vs Dynamic QR

The API treats the two types differently:

**Static QR** (PoIM=11, no URL):
- The PIX key and metadata come from the EMV payload itself
- The `amount` sent in the request is **used directly** as the transfer value
- No external lookup - immediate settlement after DICT validation

**Dynamic QR** (PoIM=12, with URL in tag 26/25):
- The backend calls `Provider.consult_qrcode` on the URL embedded in the EMV payload and receives a JWS (BACEN Manual 2.9.0)
- The authoritative value comes from the JWS response (`valor.original`) and **overrides** the `amount` sent by the client
- If the URL lookup fails (timeout, invalid JWS, QR expired at issuer), returns HTTP 422 with `detail: "QR Code dinamico nao pode ser resolvido"`
- Use the overridden value as the source of truth - `net_amount` in the response reflects the actual charged value

::: warning Do not rely on the value sent in dynamic QR
On dynamic QR, always check the POST response to get the effective `amount`. The backend ignores any divergence between the value sent and the value returned by the QR issuer - this is a BACEN requirement.
:::

::: danger Monetary values
**Request** values are in centavos (R$ 1.00 = `100`). **Response** values are in base units (R$ 1.00 = `10000`). To convert the response to BRL, divide by 10,000. **Never use floating point.**
:::

## Example

```bash
curl -X POST https://api.monetarie.com/api/external/pix/cash-out \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "hmac: $HMAC" \
  -d '{
    "amount": 3000,
    "emv": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d5204000053039865802BR5913NOME RECEBEDOR6008BRASILIA62070503***6304ABCD",
    "description": "Pagamento via QR Code",
    "external_id": "invoice-4521"
  }'
```

## Success Response -- 200 / 202

```json
{
  "worked": true,
  "final": false,
  "transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
  "end_to_end_id": "E46026562202603091530abcdef01",
  "external_id": "invoice-4521",
  "amount": 300000,
  "fee_amount": 350,
  "net_amount": 300350,
  "status": "accepted",
  "detail": "PIX enviado para processamento"
}
```

::: tip HTTP 200 vs 202
- **HTTP 200**: Transaction already settled (`final: true`, `status: "settled"`).
- **HTTP 202**: Transaction accepted for processing (`final: false`). `status` can be `"accepted"` (normal flow), `"queued"` (DICT limit, with automatic reprocessing) or `"pending_approval"` (awaiting approval). Track the status via polling or webhook.
:::

| Field | Type | Description |
|-------|------|-------------|
| `worked` | Boolean | `true` indicates the request was accepted |
| `final` | Boolean | `true` when the transaction reached a terminal state (settled or rejected). `false` when still processing |
| `transaction_id` | String | Unique transaction identifier |
| `end_to_end_id` | String | End-to-End identifier in SPI/BACEN |
| `external_id` | String | Your identifier, returned as sent. `null` if not provided |
| `amount` | Integer | Transfer amount in **base units** (÷ 10,000 for BRL). `300000` = R$ 30.00. **On dynamic QR, reflects the authoritative value returned by the JWS lookup - may diverge from the `amount` sent in the request** |
| `fee_amount` | Integer | Fee charged in base units (÷ 10,000 for BRL) |
| `net_amount` | Integer | **Gross amount debited** from the paying account (`amount + fee_amount`) - this is not what the recipient receives. See the note in [PIX Cash-Out by Key](/en/pix-cashout-key#success-response-200-202) on the semantic asymmetry between cash-out and cash-in |
| `status` | String | One of: `accepted`, `settled`, `queued`, `pending_approval`. See details in [PIX Cash-Out by Key](/en/pix-cashout-key#success-response-200-202) |
| `detail` | String | Descriptive message |

## Rejection Codes

Cash-out-by-EMV rejections follow exactly the same pattern as [PIX Cash-Out by Key](/en/pix-cashout-key#rejection-codes) - two body formats (Format A `{status: "failed", errors: [...]}` and Format B `{errors: {bad_request: "..."}}`), three error classes (validation / integration / queued rate-limit), and the same UPPERCASE × lowercase vocabularies. The items below highlight only the codes **specific to the EMV path**.

### EMV-specific validation errors (HTTP 400 / 422)

| HTTP | Format | Field with code | Meaning |
|---|---|---|---|
| 400 | B | `errors.bad_request: "invalid emv payload"` | Malformed EMV payload (invalid CRC-16, incomplete TLV, missing mandatory tag) - detected by the parser before the Orchestrator |
| 400 | B | `errors.bad_request: "invalid or missing amount"` | `amount` missing, zero, negative, or non-integer (static QR) |
| **422** | **A** | `errors[0].code: "same_institution_transfer"` | QR generated by an Monetarie account - intra-institutional PIX not supported (use TEF). **Note**: HTTP 422 (not 400), with shape `{status: "failed", errors: [{code: "same_institution_transfer", params: []}]}` |
| 422 | - | `detail: "QR Code dinamico nao pode ser resolvido"` | Failure in the dynamic QR URL lookup (timeout, invalid JWS, QR expired at issuer) - this path is handled by the controller before the Orchestrator, returns legacy shape `{errors: {unprocessable_entity: "..."}}` |
| 422 | A | `errors[0].code: "insufficient_balance"` | Available balance less than `amount + fee_amount` |

### Other errors

For the codes `dict_key_not_found`, `dict_key_blocked`, `dict_rate_limited` (synchronous), `dict_bucket_exhausted` (synchronous), `dict_lookup_failed`, `provider_rejected`, `provider_schema_error`, `provider_unknown_error` - **all return HTTP 400 in Format A** (not 429 or 503). Details in [PIX Cash-Out by Key - Integration errors](/en/pix-cashout-key#integration-errors-with-provider-dict-http-400).

### Rate-limit with automatic retry (HTTP 202 `queued`)

When Monetarie detects a DICT limit before reaching the provider, the cash-out remains in processing, enters automatic retry, and returns HTTP 202 with `status: "queued"`. Full details in [PIX Cash-Out by Key - Rate-limit with automatic retry](/en/pix-cashout-key#rate-limit-with-automatic-retry-http-202-queued).

### Asynchronous BACEN rejections

After initial acceptance (HTTP 202), the SPI/BACEN may reject via PACS.002 RJCT. This rejection:

- **Does not change the original HTTP response** (already sent as 202)
- Appears via `GET /transactions/:id` with `status: "failed"` + `reason_code` (AC03, AB03, ED05 etc.) + `reason_description` in English
- Fires webhook `pix.payout.rejected` with the same structured fields

See the full list of BACEN codes in [Query Cash-Out by ID](/en/pix-cashout-status).

## Next Steps

After creating the transfer, track the status via:

- [Query by ID](/en/pix-cashout-status)
- [Query by E2E ID](/en/pix-cashout-e2e)
- [Query by Tag](/en/pix-cashout-tag)
- [Query by External ID](/en/pix-query-external-id) -- `GET /api/external/transactions/ref/{external_id}`

Or receive confirmation automatically via [Webhook](/en/webhooks).
