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

# Cenários de teste

> Force comportamentos específicos via header X-Sandbox-Scenario.

## Como usar

Adicione o header `X-Sandbox-Scenario: <cenário>` a qualquer chamada de cash-in ou cash-out. Sem o header, o sandbox usa o cenário `success` por padrão.

```bash theme={"system"}
curl -X POST https://sandbox.mx.ntxpay.com/api/spei/cash-out \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Sandbox-Scenario: error:insufficient-funds" \
  -H "Content-Type: application/json" \
  -d '{
    "amountCentavos": 15000,
    "destinationClabe": "012180001234567890",
    "beneficiaryName": "Maria Lopez",
    "externalId": "test-error-001"
  }'
```

<Warning>
  O header controla **apenas o webhook assíncrono**. A resposta HTTP é sempre `201 Created` com `status: PENDING`, independentemente do cenário. O resultado final (`CONFIRMED`, `FAILED` ou `EXPIRED`) chega no webhook \~1 segundo depois.
</Warning>

## Cenários disponíveis

### Cenários de sucesso

| Header Value        | Comportamento síncrono | Webhook             |
| ------------------- | ---------------------- | ------------------- |
| `success` (default) | `201 PENDING`          | `CONFIRMED` em \~1s |
| *(sem header)*      | `201 PENDING`          | `CONFIRMED` em \~1s |

### Cenários de erro

| Header Value                  | Comportamento síncrono | Webhook                                         |
| ----------------------------- | ---------------------- | ----------------------------------------------- |
| `error:insufficient-funds`    | `201 PENDING`          | `FAILED` com `errorCode: INSUFFICIENT_FUNDS`    |
| `error:invalid-clabe`         | `201 PENDING`          | `FAILED` com `errorCode: INVALID_CLABE`         |
| `error:account-not-found`     | `201 PENDING`          | `FAILED` com `errorCode: ACCOUNT_NOT_FOUND`     |
| `error:account-blocked`       | `201 PENDING`          | `FAILED` com `errorCode: ACCOUNT_BLOCKED`       |
| `error:duplicate-external-id` | `201 PENDING`          | `FAILED` com `errorCode: DUPLICATE_EXTERNAL_ID` |
| `error:bank-rejected`         | `201 PENDING`          | `FAILED` com `errorCode: BANK_REJECTED`         |

### Cenários de atraso

| Header Value  | Comportamento síncrono | Webhook               |
| ------------- | ---------------------- | --------------------- |
| `delayed:5s`  | `201 PENDING`          | `CONFIRMED` após +5s  |
| `delayed:30s` | `201 PENDING`          | `CONFIRMED` após +30s |
| `delayed:60s` | `201 PENDING`          | `CONFIRMED` após +60s |

<Info>
  O delay máximo permitido é de **120 segundos** — valores acima são truncados automaticamente.
</Info>

## Exemplo: webhook de sucesso

```json theme={"system"}
{
  "event": "cash_out",
  "deliveryId": "8e2c5b6f-3a12-4b9c-9a18-77a2b3c4d5e6",
  "createdAt": "2026-03-26T10:00:00.000Z",
  "transaction": {
    "id": 12345,
    "externalId": "test-success-001",
    "paymentMethod": "SPEI",
    "direction": "out",
    "type": "cash_out",
    "status": "CONFIRMED",
    "provider": "sandbox",
    "amountCentavos": 15000,
    "clabe": "012180001234567890",
    "referenceNumerical": "9876543",
    "createdAt": "2026-03-26T09:59:59.000Z",
    "confirmedAt": "2026-03-26T10:00:00.000Z",
    "counterpart": {
      "name": "Maria Lopez",
      "taxId": null,
      "bank": {}
    }
  },
  "errorCode": null,
  "errorMessage": null,
  "metadata": {}
}
```

## Exemplo: webhook de falha

```json theme={"system"}
{
  "event": "cash_out",
  "deliveryId": "1a3f9e8d-2c47-4b9c-aa18-77a2b3c4d5e6",
  "createdAt": "2026-03-26T10:01:00.000Z",
  "transaction": {
    "id": 12346,
    "externalId": "test-error-001",
    "paymentMethod": "SPEI",
    "direction": "out",
    "type": "cash_out",
    "status": "FAILED",
    "provider": "sandbox",
    "amountCentavos": 15000,
    "clabe": "012180001234567890",
    "referenceNumerical": null,
    "confirmedAt": null
  },
  "errorCode": "INSUFFICIENT_FUNDS",
  "errorMessage": "Conta sem saldo suficiente",
  "metadata": {}
}
```

Notas:

* Em `status: FAILED`, `referenceNumerical` e `confirmedAt` são `null` — a rede SPEI nunca confirmou a transação.
* `errorCode` e `errorMessage` descrevem o motivo da falha.

## Restrições

* O header `X-Sandbox-Scenario` funciona **exclusivamente** em contas sandbox.
* Contas de produção que enviem o header recebem:

```json theme={"system"}
{
  "statusCode": 400,
  "message": "X-Sandbox-Scenario header is only supported in sandbox mode."
}
```

## Boas práticas

1. **Teste todos os cenários** antes de ir ao ar — implemente o tratamento de `CONFIRMED`, `PENDING`, `FAILED` e `EXPIRED`.
2. **Valide os campos de erro** — use `errorCode` para decisões automáticas; reserve `errorMessage` para logs/usuários.
3. **Teste com delay** — verifique que seu sistema lida bem com entrega lenta do webhook.
4. **Idempotência** — use `transaction.id` como chave de idempotência; o mesmo webhook pode ser reenviado.
