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

# Sandbox Confirm

> Approve, reject or expire pending PayIn and PayOut transactions in staging and UAT.

# Sandbox Confirm

Use the sandbox confirm endpoints to close a **pending** PayIn or PayOut without waiting for a real bank or cash-network webhook. TumiPay updates the transaction, then sends the same [merchant webhook](/api-reference/webhooks) it would send in a live flow.

Typical cases:

* Approve a PayIn that is waiting for confirmation (`BREB`, `EFECTY`, `SPEI`, `QR`, and others)
* Approve, reject or expire a PayOut that is waiting for the bank
* Exercise your `ipn_url` without waiting for an external webhook

<Warning>
  These endpoints exist only in **staging** and **UAT**. They are not available in production.
</Warning>

Sandbox confirm **does not create** the transaction. Create it first with [PayIn](/api-reference/payin) or [PayOut](/api-reference/payout), then confirm it once it is pending.

```mermaid theme={null}
sequenceDiagram
    participant M as Merchant
    participant T as TumiPay Sandbox
    participant W as Merchant ipn_url

    M->>T: 1. Create PayIn or PayOut
    T->>M: 2. ticket + PENDING
    M->>T: 3. POST /sandbox/.../confirm
    T->>T: 4. Simulate payment result
    T->>W: 5. Webhook with final status
    T->>M: 6. 200 SUCCESS
```

## Availability

|                    |                                                                   |
| ------------------ | ----------------------------------------------------------------- |
| **Environments**   | Staging and UAT only                                              |
| **Production**     | `404 NOT_FOUND` — no status change                                |
| **Authentication** | Same as other merchant APIs: `Authorization: Basic` + `Token-Top` |

**Base URL (staging):** `https://api-empresas.staging.topup.com.co/production`

**Base URL (UAT):** `https://api-empresas.uat.topup.com.co/production`.

## Endpoints

| Operation      | Path                                  |
| -------------- | ------------------------------------- |
| Confirm PayIn  | `POST /api/v1/sandbox/payin/confirm`  |
| Confirm PayOut | `POST /api/v1/sandbox/payout/confirm` |

<CardGroup cols={2}>
  <Card title="POST Confirm PayIn" icon="arrow-down-to-line" href="/api-reference/sandbox/post-confirm-payin">
    Close a pending PayIn and fire the merchant webhook.
  </Card>

  <Card title="POST Confirm PayOut" icon="arrow-up-from-line" href="/api-reference/sandbox/post-confirm-payout">
    Close a pending PayOut and fire the merchant webhook.
  </Card>
</CardGroup>

Use the PayIn endpoint only on PayIn transactions and the PayOut endpoint only on PayOut transactions. Mixing them returns `422 TYPE_MISMATCH`.

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Basic authentication: `Basic base64(username:password)`
</ParamField>

<ParamField header="Token-Top" type="string" required>
  Merchant token. The transaction must belong to this merchant.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

### Body

The body is the same for PayIn and PayOut.

<ParamField body="transaction_id" type="string" required>
  Identifier of the transaction. Accepts the TumiPay **ticket**, your merchant **reference**, or an **external reference** (Bre-B key, cash payment reference, SPEI CLABE, payout tracking id, and similar). See [Identifiers](#identifiers).
</ParamField>

<ParamField body="payment_method" type="string" required>
  Enum name in **uppercase**, not the portal label. Examples: `SPEI`, `BREB`, `BANK_TRANSFER`. Do not send `"Transferencia SPEI"`.
</ParamField>

<ParamField body="status" type="string" required>
  Target status: `APPROVED`, `REJECTED`, or `EXPIRED`. Not every method supports every value — see [Supported statuses](#supported-statuses).
</ParamField>

```json theme={null}
{
  "transaction_id": "xiK9LSjwFyBYYRG",
  "payment_method": "SPEI",
  "status": "APPROVED"
}
```

## Response

### Success (`200`)

Repeating the same final status is **idempotent**: if the transaction is already in the requested status, the API still returns `200 SUCCESS`.

```json theme={null}
{
  "code": "01",
  "status": "SUCCESS",
  "message": "Transacción confirmada",
  "data": {
    "transaction_id": "xiK9LSjwFyBYYRG",
    "payment_method": "SPEI",
    "status": "APPROVED"
  }
}
```

### Errors

| HTTP  | `error`                | When                                                                                                         |
| ----- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
| `404` | `NOT_FOUND`            | Unknown `transaction_id`, another merchant's transaction, or the endpoint is not enabled in this environment |
| `422` | `VALIDATION_ERROR`     | Invalid body                                                                                                 |
| `422` | `TYPE_MISMATCH`        | PayIn confirm used on a PayOut (or the reverse)                                                              |
| `422` | `ALREADY_FINAL`        | Transaction is already in a **different** final status                                                       |
| `422` | `STATUS_NOT_SUPPORTED` | That `status` is not valid for the payment method                                                            |
| `500` | `CONFIRM_FAILED`       | Status update failed                                                                                         |

## Identifiers

`transaction_id` must belong to the merchant authenticated with `Authorization` + `Token-Top`. Any of these usually work:

| What to send       | Where it comes from                                                   | Example                                                  |
| ------------------ | --------------------------------------------------------------------- | -------------------------------------------------------- |
| Ticket             | `data.ticket` from the create response                                | `xiK9LSjwFyBYYRG`                                        |
| Merchant reference | `reference` you sent when creating the transaction                    | `XCL-2gyCSMzK2ditM`                                      |
| External reference | Value returned in `payment_information` or assigned during processing | `@TUMI3785752657908`, cash payment reference, SPEI CLABE |

## Prerequisites

| Flow                     | Before you call confirm                                                                                                                                                                           |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **PayIn**                | Transaction exists and is still pending (not `APPROVED`, `REJECTED`, or `EXPIRED`)                                                                                                                |
| **PayOut**               | Transaction was submitted to the bank and is waiting for a response. A PayOut that was just created and not yet sent cannot be confirmed                                                          |
| **PayOut Peru**          | Destination bank is in the sandbox catalog (BCP, Interbank, BBVA, Scotiabank, Yape, Plin, and similar). Banks outside that catalog — for example Santander — are often rejected at creation       |
| **PayOut Mexico (SPEI)** | `account_type` is `CLABE`, both `account_number` and `clabe_number` use a [test CLABE](#mexico-spei-payout--test-clabes) with a valid check digit, and the merchant has sandbox rates and balance |

<Note>
  **Not supported by sandbox confirm:**

  * **CARD** — the result is fixed by the [test card number](/api-reference/payin#sandbox-test-cards).
  * **PSE** — create the PayIn with bank **Banka** and complete the normal PSE flow. See [Testing](/api-reference/testing#transaction-approval).
  * **NEQUI\_PUSH** — sandbox automatically simulates the push through the normal create flow (`payment_url`). See [Colombia PayIn — NEQUI\_PUSH](/docs/co/payin#nequi_push--hybrid).
</Note>

## Supported statuses

### PayIn

| `payment_method` | Country | `APPROVED` | `REJECTED` | `EXPIRED` |
| ---------------- | ------- | ---------- | ---------- | --------- |
| `BREB`           | CO      | ✅          | ✅          | ❌         |
| `BREB_QR`        | CO      | ✅          | ✅          | ❌         |
| `EFECTY`         | CO      | ✅          | ❌          | ❌         |
| `QR`             | PE      | ✅          | ✅          | ✅         |
| `SPEI`           | MX      | ✅          | ❌          | ❌         |

Unsupported combinations return `422 STATUS_NOT_SUPPORTED`.

### PayOut

| `payment_method` | Country | `APPROVED` | `REJECTED` | `EXPIRED` |
| ---------------- | ------- | ---------- | ---------- | --------- |
| `BANK_TRANSFER`  | PE      | ✅          | ✅          | ✅         |
| `BANK_TRANSFER`  | CO      | ✅          | ✅          | ✅         |
| `SPEI`           | MX      | ✅          | ✅          | ✅         |

## Method notes

<AccordionGroup>
  <Accordion title="Colombia PayIn — BREB and BREB QR">
    Use `payment_method: "BREB"` or `"BREB_QR"`. Confirm while the PayIn is pending. `transaction_id` can be the ticket, your reference, or the Bre-B key from `payment_information` (for example `@TUMI...` / `@TUMIQR...`).

    Supported statuses: `APPROVED`, `REJECTED`.
  </Accordion>

  <Accordion title="Colombia PayIn — EFECTY">
    Use `payment_method: "EFECTY"`. Confirm while the PayIn is pending. The cash payment reference from `payment_information` works as `transaction_id`.

    Only `APPROVED` is supported.
  </Accordion>

  <Accordion title="Peru PayIn — QR">
    Use `payment_method: "QR"`. Ticket, merchant reference, or external reference are all valid.

    Supported statuses: `APPROVED`, `REJECTED`, `EXPIRED`.
  </Accordion>

  <Accordion title="Mexico PayIn — SPEI">
    Use `payment_method: "SPEI"`. Confirm while the PayIn is pending. Ticket, merchant reference, or the CLABE assigned to the transaction work as `transaction_id`.

    Only `APPROVED` is supported.
  </Accordion>

  <Accordion title="Peru PayOut — BANK_TRANSFER">
    Use `payment_method: "BANK_TRANSFER"`. The PayOut must already be waiting for the bank — not merely created.

    `APPROVED` completes the transfer. `REJECTED` and `EXPIRED` reject it (`EXPIRED` is treated as rejected) and restore sandbox balance.

    Create the PayOut with a bank from the sandbox catalog (BCP, Interbank, BBVA, Scotiabank, Yape, Plin, and similar). See [Peru PayOut](/docs/pe/payout).
  </Accordion>

  <Accordion title="Colombia PayOut — BANK_TRANSFER">
    Use `payment_method: "BANK_TRANSFER"` with `country: "CO"`. The PayOut must already be waiting for the bank.

    `APPROVED` completes the disbursement. `REJECTED` and `EXPIRED` reject it and restore sandbox balance.
  </Accordion>

  <Accordion title="Mexico PayOut — SPEI">
    Use `payment_method: "SPEI"` with `account_type: "CLABE"` and a valid [test CLABE](#mexico-spei-payout--test-clabes) in both `account_number` and `clabe_number`.

    `AHORRO`, `CORRIENTE`, or an invented CLABE (for example `12345678…`) usually reject the PayOut at creation, so sandbox confirm cannot run.

    Confirm only after the PayOut is waiting for the bank. See [Mexico PayOut](/docs/mx/payout).
  </Accordion>
</AccordionGroup>

### Mexico SPEI PayOut — test CLABEs (examples)

The following are **example** CLABEs for sandbox testing — not an exhaustive list. Use the same value in `account_number` and `clabe_number`:

| `bank`        | CLABE                |
| ------------- | -------------------- |
| `BBVA_MEXICO` | `012180015012345672` |
| `BANAMEX`     | `002180015012345679` |
| `SANTANDER`   | `014180015012345670` |
| `BANCOPPEL`   | `137180015012345670` |

## What happens after confirm

| Action                 | Effect                                                                                    |
| ---------------------- | ----------------------------------------------------------------------------------------- |
| Approve PayIn          | Status `APPROVED`, webhook to `ipn_url`, sandbox balance credited according to the method |
| Approve PayOut         | Status `APPROVED`, webhook to `ipn_url`                                                   |
| Reject PayOut          | Status `REJECTED`, reserved sandbox balance restored, webhook to `ipn_url`                |
| Same status again      | `200 SUCCESS` (idempotent)                                                                |
| Different final status | `422 ALREADY_FINAL`                                                                       |

## Examples

### Approve a Mexico SPEI PayIn

```bash theme={null}
curl --request POST 'https://api-empresas.staging.topup.com.co/production/api/v1/sandbox/payin/confirm' \
  --header 'Authorization: Basic your_auth_key' \
  --header 'Token-Top: your_auth_token' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "transaction_id": "7tt7nNt7Wf96qlz",
    "payment_method": "SPEI",
    "status": "APPROVED"
  }'
```

### Approve a Peru PayOut

```bash theme={null}
curl --request POST 'https://api-empresas.staging.topup.com.co/production/api/v1/sandbox/payout/confirm' \
  --header 'Authorization: Basic your_auth_key' \
  --header 'Token-Top: your_auth_token' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "transaction_id": "XCL-2gyCSMzK2ditM",
    "payment_method": "BANK_TRANSFER",
    "status": "APPROVED"
  }'
```

### Approve a Mexico SPEI PayOut

```bash theme={null}
curl --request POST 'https://api-empresas.staging.topup.com.co/production/api/v1/sandbox/payout/confirm' \
  --header 'Authorization: Basic your_auth_key' \
  --header 'Token-Top: your_auth_token' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "transaction_id": "A3abF5q3Bu15neI",
    "payment_method": "SPEI",
    "status": "APPROVED"
  }'
```

### Reject a Mexico SPEI PayOut

```json theme={null}
{
  "transaction_id": "XCL-3jknH0UrmbksJ",
  "payment_method": "SPEI",
  "status": "REJECTED"
}
```

### Approve a Colombia BREB PayIn

```json theme={null}
{
  "transaction_id": "@TUMI3785752657908",
  "payment_method": "BREB",
  "status": "APPROVED"
}
```

## Quick reference by country

| Country  | PayIn sandbox confirm       | PayOut sandbox confirm |
| -------- | --------------------------- | ---------------------- |
| Colombia | `BREB`, `BREB_QR`, `EFECTY` | `BANK_TRANSFER`        |
| Peru     | `QR`                        | `BANK_TRANSFER`        |
| Mexico   | `SPEI`                      | `SPEI`                 |

## Troubleshooting

| Symptom                                | Likely cause                                                                                                                                 |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `404 NOT_FOUND`                        | Wrong `transaction_id`, another merchant's token, or called against production                                                               |
| `TYPE_MISMATCH`                        | PayIn vs PayOut endpoint does not match the transaction                                                                                      |
| `STATUS_NOT_SUPPORTED`                 | For example `EXPIRED` on BREB or EFECTY PayIn, or `REJECTED` on SPEI PayIn                                                                   |
| `ALREADY_FINAL`                        | The transaction was already closed by another flow or a previous confirm. Confirm only while it is still pending                             |
| Peru PayOut rejected at creation       | Bank is outside the sandbox catalog (for example Santander)                                                                                  |
| Mexico PayOut rejected immediately     | Invalid CLABE or `account_type` is not `CLABE`                                                                                               |
| PayOut confirm has no effect           | Transaction is still in created state and has not reached "waiting for bank"                                                                 |
| Card approved or declined unexpectedly | PAN is not from the [test card table](/api-reference/payin#sandbox-test-cards); sandbox confirm does not apply                               |
| PSE confirm fails or has no effect     | PSE is not supported by sandbox confirm — use bank **Banka** in the normal PSE flow ([Testing](/api-reference/testing#transaction-approval)) |
