# Cuentas de retiro

Agrega y consulta las cuentas bancarias y wallets a las que retiras tu saldo.

> [!WARNING]
> El SDK `vexor` de npm (Stripe, MercadoPago, PayPal, Square, Talo) está **deprecado**: ya no se mantiene y no debe usarse. vexorpay es hoy una **API REST de links de pago en USD, sin SDK oficial**. Migración: <https://developers.vexorpay.com/docs/migracion-desde-el-sdk>

Fuente: <https://developers.vexorpay.com/docs/api/payout-methods>

Una **cuenta de retiro** es la cuenta bancaria o la wallet a la que va el saldo cuando
retiras. Por la API puedes ver qué métodos hay
para tu país, qué datos pide cada uno, agregar cuentas y listarlas. El retiro en sí se pide
desde el dashboard (**Retirar**): no se crean retiros por API (ver [Retiros](/docs/api/payouts)).

Hay tres tipos (`type`):

| `type` | Qué es |
| --- | --- |
| `usd_bank` | Transferencia a una cuenta en dólares en EE. UU. (el país es siempre `US`). |
| `crypto` | Una wallet: cada método es una moneda en una red. |
| `local` | El banco de tu país, en su moneda (AR, BR, CL, CO, MX, PE o UY). |

Para gestionar cuentas de retiro hace falta la **identidad verificada** (si no,
`409 payment_account_not_ready`).

> **Seguridad:** los datos que mandas en `fields` viajan cifrados al
> procesador de pagos y vexorpay **no los guarda**: la API sólo devuelve una referencia
> enmascarada (`•••• 4567`). Cada alta le manda un **email de aviso** al dueño de la cuenta,
> se haga por la API o desde el dashboard. Estos endpoints **no aceptan** el header
> `Vexor-Account`: una app de marketplace nunca agrega cuentas de retiro de otro usuario
> (`403 invalid_connected_account`).

El flujo para agregar una cuenta es:

1. [Listar los métodos disponibles](#listar-los-métodos-disponibles) para el `type` y el país.
2. [Ver los campos](#ver-un-método-y-sus-campos) que pide el método elegido.
3. [Agregar la cuenta](#agregar-una-cuenta-de-retiro) con esos campos.
4. [Listar tus cuentas](#listar-cuentas-de-retiro) para confirmarla.

En sandbox (`https://api.sandbox.vexorpay.com`) el procesador de pagos no tiene retiros: el
catálogo de métodos viene vacío y no se pueden agregar cuentas. Las cuentas de retiro se agregan
en producción (`https://api.vexorpay.com`), con tu identidad verificada.

## El objeto PayoutMethod

```json
{
  "id": "potk_1a2b3c",
  "type": "local",
  "country": "AR",
  "name": "Transferencia a tu banco (ARS)",
  "nickname": "Mi cuenta sueldo",
  "reference": "•••• 4567",
  "destinationCurrency": "ars",
  "available": true,
  "isDefault": true,
  "createdAt": "2026-11-08T14:20:00.000Z",
  "lastPaidOutAt": null
}
```

| Campo | Qué es |
| --- | --- |
| `id` | Id de la cuenta de retiro. |
| `type` | `usd_bank`, `crypto` o `local`. |
| `country` | País de la cuenta (`US` en `usd_bank`). |
| `name` | Nombre del método (y del banco, si se conoce). |
| `nickname` | El nombre que le pusiste para reconocerla, o `null`. |
| `reference` | Referencia enmascarada que da el procesador (`•••• 4567`). Nunca trae los datos completos. `null` si todavía no la informó. |
| `destinationCurrency` | Moneda en la que llega el retiro (p. ej. `ars`, `usd`, `usdc`). |
| `available` | `false` si el procesador de pagos la desactivó. |
| `isDefault` | Es la predeterminada de su `type`: la que se usa al retirar desde el dashboard. La primera cuenta de cada `type` queda como predeterminada. |
| `createdAt` | Fecha de alta. |
| `lastPaidOutAt` | Fecha del último retiro a esta cuenta, o `null`. |

## Listar los métodos disponibles

`GET /api/v1/payout-methods/supported`

Devuelve los métodos con los que se puede agregar una cuenta, sin paginación.

| Parámetro | Descripción |
| --- | --- |
| `type` | Obligatorio: `usd_bank`, `crypto` o `local`. |
| `country` | País: `AR`, `BR`, `CL`, `CO`, `MX`, `PE` o `UY`. Obligatorio para `crypto` y `local`; `usd_bank` usa siempre EE. UU. |

**curl**

```bash
curl "https://api.vexorpay.com/api/v1/payout-methods/supported?type=local&country=AR" \
  -H "Authorization: Bearer vxp_live_tu_clave"
```

**Node.js**

```js
const res = await fetch(
  'https://api.vexorpay.com/api/v1/payout-methods/supported?type=local&country=AR',
  {
    headers: { Authorization: 'Bearer vxp_live_tu_clave' },
  },
)

const methods = await res.json()
```

**PHP**

```php
<?php

$client = curl_init('https://api.vexorpay.com/api/v1/payout-methods/supported?type=local&country=AR');
curl_setopt_array($client, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer vxp_live_tu_clave'],
]);

$methods = json_decode(curl_exec($client), true);
```

**Python**

```python

response = requests.get(
    'https://api.vexorpay.com/api/v1/payout-methods/supported',
    params={'type': 'local', 'country': 'AR'},
    headers={'Authorization': 'Bearer vxp_live_tu_clave'},
)
methods = response.json()
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://api.vexorpay.com/api/v1/payout-methods/supported?type=local&country=AR", nil)
req.Header.Set("Authorization", "Bearer vxp_live_tu_clave")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
```

### Respuesta exitosa

`200 OK`

```json
{
  "data": [
    {
      "id": "podst_hWWwr49qe5Wm",
      "type": "local",
      "country": "AR",
      "name": "Transferencia a tu banco (ARS)",
      "destinationCurrency": "ars",
      "feeCents": 150,
      "estimatedArrival": "2026-11-10T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "total_count": 1,
  "url": "/v1/payout-methods/supported?type=local&country=AR"
}
```

| Campo | Qué es |
| --- | --- |
| `id` | Id del método (`podst_…`): es el `supported_method_id` al agregar la cuenta. |
| `destinationCurrency` | Moneda del método: es el `destination_currency` al agregar la cuenta. |
| `feeCents` | Comisión de un retiro de referencia de **USD 100**, en centavos de USD. La comisión real depende del monto de cada retiro. `null` si no se conoce. |
| `estimatedArrival` | Fecha estimada de acreditación de un retiro hecho ahora, o `null`. |

## Ver un método y sus campos

`GET /api/v1/payout-methods/supported/{id}`

Devuelve el método con los `fields` que hay que mandar al agregar la cuenta. El parámetro
`country` es obligatorio: el mismo con el que listaste el método (`US`
para `usd_bank`).

**curl**

```bash
curl "https://api.vexorpay.com/api/v1/payout-methods/supported/podst_hWWwr49qe5Wm?country=AR" \
  -H "Authorization: Bearer vxp_live_tu_clave"
```

**Node.js**

```js
const res = await fetch(
  'https://api.vexorpay.com/api/v1/payout-methods/supported/podst_hWWwr49qe5Wm?country=AR',
  {
    headers: { Authorization: 'Bearer vxp_live_tu_clave' },
  },
)

const method = await res.json()
```

**PHP**

```php
<?php

$client = curl_init('https://api.vexorpay.com/api/v1/payout-methods/supported/podst_hWWwr49qe5Wm?country=AR');
curl_setopt_array($client, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer vxp_live_tu_clave'],
]);

$method = json_decode(curl_exec($client), true);
```

**Python**

```python

response = requests.get(
    'https://api.vexorpay.com/api/v1/payout-methods/supported/podst_hWWwr49qe5Wm',
    params={'country': 'AR'},
    headers={'Authorization': 'Bearer vxp_live_tu_clave'},
)
method = response.json()
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://api.vexorpay.com/api/v1/payout-methods/supported/podst_hWWwr49qe5Wm?country=AR", nil)
req.Header.Set("Authorization", "Bearer vxp_live_tu_clave")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
```

### Respuesta exitosa

`200 OK`

```json
{
  "id": "podst_hWWwr49qe5Wm",
  "type": "local",
  "country": "AR",
  "name": "Transferencia a tu banco (ARS)",
  "destinationCurrency": "ars",
  "feeCents": 150,
  "estimatedArrival": "2026-11-10T12:00:00.000Z",
  "fields": [
    {
      "id": "fld_bank_account_number",
      "label": "CBU",
      "type": "text",
      "required": true,
      "sensitive": true,
      "pattern": "^[0-9]{22}$",
      "options": [],
      "fixed": null
    },
    {
      "id": "fld_identification_type",
      "label": "Tipo de documento",
      "type": "select",
      "required": true,
      "sensitive": false,
      "pattern": null,
      "options": [
        { "value": "Tax Payer Identification Card/Number", "label": "Identificación fiscal (CUIT, RUT, RFC…)" }
      ],
      "fixed": "Tax Payer Identification Card/Number"
    },
    {
      "id": "fld_identification_number",
      "label": "CUIT / CUIL",
      "type": "text",
      "required": true,
      "sensitive": true,
      "pattern": "^(20|23|24|27|30|33|34)[-]*[0-9]{8}[-]*[0-9]$",
      "options": [],
      "fixed": null
    }
  ]
}
```

| Campo de `fields[]` | Qué es |
| --- | --- |
| `id` | Id del campo (`fld_…`): es la clave dentro de `fields` al agregar la cuenta. |
| `label` | Etiqueta en español, para mostrarle al usuario. |
| `type` | `text` (texto libre) o `select` (uno de los `options`). |
| `required` | Si es obligatorio. |
| `sensitive` | Si es un dato sensible (p. ej. un número de cuenta): no lo guardes ni lo muestres en tus logs. |
| `pattern` | Expresión regular con la que lo valida el procesador, o `null`. |
| `options` | En un `select`, los valores posibles (`value`, que es lo que se manda, y `label`). |
| `fixed` | Si no es `null`, el único valor válido: mándalo tal cual sin preguntarle al usuario (p. ej. el tipo de documento cuando el número es un CUIT/CUIL). |

Los campos cambian según el método y el país: léelos siempre de este
endpoint en vez de fijarlos en tu código.

## Agregar una cuenta de retiro

`POST /api/v1/payout-methods`

Agrega una cuenta bancaria o wallet **sin hacer un retiro** y sin necesitar saldo. Responde
`201 Created` con el objeto `PayoutMethod`. Con un token OAuth exige el scope
`payout_methods:write`.

| Campo | Requerido | Descripción |
| --- | --- | --- |
| `type` | Sí | `usd_bank`, `crypto` o `local`. |
| `supported_method_id` | Sí | El `id` del método (`podst_…`), de `GET /payout-methods/supported`. |
| `country` | Para `crypto` y `local` | El país del método. En `usd_bank` es siempre `US`. |
| `destination_currency` | Sí | El `destinationCurrency` del método (p. ej. `ars`). |
| `nickname` | No | Nombre para reconocer la cuenta, hasta 40 caracteres y único entre tus cuentas. Si no viene, se usa el nombre del método. |
| `fields` | Sí | Objeto con el valor (string, hasta 300 caracteres) de cada campo del método, por su id `fld_…`. |

A diferencia del resto de la API, acá un campo desconocido en el body **no se ignora**:
responde `400 validation_error`, para que un typo en datos bancarios no pase en silencio.

**Idempotencia:** manda un header `Idempotency-Key` (p. ej. un UUID por
alta). Un replay con el mismo body devuelve la cuenta ya creada (`200`) y, si el procesador de
pagos no respondió (`502 upstream_error`), reintentar con la **misma** clave no crea una segunda
cuenta. Ver [Idempotencia](/docs/api#idempotencia).

**curl**

```bash
curl -X POST "https://api.vexorpay.com/api/v1/payout-methods" \
  -H "Authorization: Bearer vxp_live_tu_clave" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f14e45f-ceea-4b6a-9f8e-2d1c3b4a5f60" \
  -d '{
    "type": "local",
    "supported_method_id": "podst_hWWwr49qe5Wm",
    "country": "AR",
    "destination_currency": "ars",
    "nickname": "Mi cuenta sueldo",
    "fields": {
      "fld_bank_account_number": "0110599520000001234567",
      "fld_identification_type": "Tax Payer Identification Card/Number",
      "fld_identification_number": "20123456789"
    }
  }'
```

**Node.js**

```js
const res = await fetch('https://api.vexorpay.com/api/v1/payout-methods', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer vxp_live_tu_clave',
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    type: 'local',
    supported_method_id: 'podst_hWWwr49qe5Wm',
    country: 'AR',
    destination_currency: 'ars',
    nickname: 'Mi cuenta sueldo',
    fields: {
      fld_bank_account_number: '0110599520000001234567',
      fld_identification_type: 'Tax Payer Identification Card/Number',
      fld_identification_number: '20123456789',
    },
  }),
})

const payoutMethod = await res.json()
```

**PHP**

```php
<?php

$client = curl_init('https://api.vexorpay.com/api/v1/payout-methods');
curl_setopt_array($client, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer vxp_live_tu_clave',
    'Content-Type: application/json',
    'Idempotency-Key: 8f14e45f-ceea-4b6a-9f8e-2d1c3b4a5f60',
  ],
  CURLOPT_POSTFIELDS => json_encode([
    'type' => 'local',
    'supported_method_id' => 'podst_hWWwr49qe5Wm',
    'country' => 'AR',
    'destination_currency' => 'ars',
    'nickname' => 'Mi cuenta sueldo',
    'fields' => [
      'fld_bank_account_number' => '0110599520000001234567',
      'fld_identification_type' => 'Tax Payer Identification Card/Number',
      'fld_identification_number' => '20123456789',
    ],
  ]),
]);

$payoutMethod = json_decode(curl_exec($client), true);
```

**Python**

```python

response = requests.post(
    'https://api.vexorpay.com/api/v1/payout-methods',
    headers={
        'Authorization': 'Bearer vxp_live_tu_clave',
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={
        'type': 'local',
        'supported_method_id': 'podst_hWWwr49qe5Wm',
        'country': 'AR',
        'destination_currency': 'ars',
        'nickname': 'Mi cuenta sueldo',
        'fields': {
            'fld_bank_account_number': '0110599520000001234567',
            'fld_identification_type': 'Tax Payer Identification Card/Number',
            'fld_identification_number': '20123456789',
        },
    },
)
payout_method = response.json()
```

**Go**

```go
body := strings.NewReader(`{
  "type": "local",
  "supported_method_id": "podst_hWWwr49qe5Wm",
  "country": "AR",
  "destination_currency": "ars",
  "nickname": "Mi cuenta sueldo",
  "fields": {
    "fld_bank_account_number": "0110599520000001234567",
    "fld_identification_type": "Tax Payer Identification Card/Number",
    "fld_identification_number": "20123456789"
  }
}`)

req, _ := http.NewRequest("POST", "https://api.vexorpay.com/api/v1/payout-methods", body)
req.Header.Set("Authorization", "Bearer vxp_live_tu_clave")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "8f14e45f-ceea-4b6a-9f8e-2d1c3b4a5f60")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
```

### Respuesta exitosa

`201 Created` (o `200 OK` en un replay de idempotencia)

```json
{
  "id": "potk_1a2b3c",
  "type": "local",
  "country": "AR",
  "name": "Transferencia a tu banco (ARS)",
  "nickname": "Mi cuenta sueldo",
  "reference": "•••• 4567",
  "destinationCurrency": "ars",
  "available": true,
  "isDefault": true,
  "createdAt": "2026-11-08T14:20:00.000Z",
  "lastPaidOutAt": null
}
```

Si un valor no pasa la validación (la tuya o la del procesador de pagos), la respuesta es
`400 validation_error` y `detail.field` dice cuál fue: `type`, `supported_method_id`, `country`,
`destination_currency`, `nickname`, `fields` o el id `fld_…` rechazado:

```json
{
  "error": "validation_error",
  "message": "The payment processor rejected a value: check that field.",
  "detail": { "field": "fld_bank_account_number" }
}
```

## Listar cuentas de retiro

`GET /api/v1/payout-methods`

Lista tus cuentas de retiro guardadas, incluidas las que agregaste desde el dashboard, con su
referencia enmascarada. Parámetros: `limit`, `starting_after` y `ending_before` (ver
[Paginación](/docs/api#paginación)).

**curl**

```bash
curl "https://api.vexorpay.com/api/v1/payout-methods?limit=10" \
  -H "Authorization: Bearer vxp_live_tu_clave"
```

**Node.js**

```js
const res = await fetch(
  'https://api.vexorpay.com/api/v1/payout-methods?limit=10',
  {
    headers: { Authorization: 'Bearer vxp_live_tu_clave' },
  },
)

const data = await res.json()
```

**PHP**

```php
<?php

$client = curl_init('https://api.vexorpay.com/api/v1/payout-methods?limit=10');
curl_setopt_array($client, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer vxp_live_tu_clave'],
]);

$data = json_decode(curl_exec($client), true);
```

**Python**

```python

response = requests.get(
    'https://api.vexorpay.com/api/v1/payout-methods',
    params={'limit': '10'},
    headers={'Authorization': 'Bearer vxp_live_tu_clave'},
)
data = response.json()
```

**Go**

```go
req, _ := http.NewRequest("GET", "https://api.vexorpay.com/api/v1/payout-methods?limit=10", nil)
req.Header.Set("Authorization", "Bearer vxp_live_tu_clave")

res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
```

### Respuesta exitosa

`200 OK`

```json
{
  "data": [
    {
      "id": "potk_1a2b3c",
      "type": "local",
      "country": "AR",
      "name": "Transferencia a tu banco (ARS)",
      "nickname": "Mi cuenta sueldo",
      "reference": "•••• 4567",
      "destinationCurrency": "ars",
      "available": true,
      "isDefault": true,
      "createdAt": "2026-11-08T14:20:00.000Z",
      "lastPaidOutAt": null
    }
  ],
  "has_more": false,
  "total_count": 1,
  "url": "/v1/payout-methods?limit=10"
}
```

## Errores

| Error | HTTP | Causa |
| --- | --- | --- |
| `validation_error` | 400 | Un parámetro o un campo inválido, un valor rechazado por el procesador de pagos, un `nickname` repetido, un campo desconocido en el body o un cursor de paginación inválido. `detail.field` indica cuál, cuando aplica. |
| `invalid_request` | 400 | El body no es un objeto JSON. |
| `insufficient_scope` | 403 | El token OAuth no tiene el scope `payout_methods:write` (sólo en `POST`). |
| `invalid_connected_account` | 403 | Mandaste el header `Vexor-Account`: las cuentas de retiro no se gestionan en nombre de una cuenta conectada. |
| `not_found` | 404 | No existe ese método para el país (`GET /payout-methods/supported/{id}`). |
| `payment_account_not_ready` | 409 | Falta verificar la identidad (o la cuenta de cobro no está activada). Se resuelve desde el dashboard. |
| `idempotency_conflict` | 409 | La `Idempotency-Key` ya se usó con un body distinto. |
| `upstream_error` | 502 | El procesador de pagos no respondió. En un `POST`, reintenta con la misma `Idempotency-Key`: no se crea una segunda cuenta. |
