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

# Integración Payments

> Emite facturas, paga beneficiarios y lee un extracto unificado desde cualquier sistema usando la API Payments de SalesOS y API Keys.

# Integración Payments

<Tip>
  Prueba peticiones firmadas en el navegador en el [Sandbox de la API](https://play2sellsa.github.io/api-sandbox/) — pega tu API key y secret, y el playground firma las peticiones automáticamente.
</Tip>

<Note>
  Los ejemplos siguientes muestran el header en formato wire `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE`. Para calcular la firma en tu código, usa el helper `signedRequest` en [Autenticación](/es/api/authentication).
</Note>

La integración Payments es una API REST agnóstica de proveedor para las operaciones financieras esenciales que todo backoffice necesita:

1. **Emitir Invoice (cobro)** — `POST /payments/invoices`. Una *Invoice* aquí es una solicitud de pago — un cobro recolectado vía PIX, boleto, tarjeta, ACH o SEPA según la región.
2. **Cancelar Invoice** — `POST /payments/invoices/{id}/cancel` (anula cobros no pagados).
3. **Emitir documento fiscal (NFS-e/NF-e/PEPPOL)** — `POST /payments/invoices/{id}/tax-documents`. Opcional, específico por región (Brasil hoy, Europa después). También puede ser auto-emitido al crear la Invoice.
4. **Pagar a beneficiario (premio o cualquier payout)** — `POST /payments/payouts`.
5. **Leer un extracto bancario** — `GET /payments/balance-transactions`.

Toda transacción lleva campos de control de primer nivel usados por tu equipo financiero para conciliación: `cost_center` (centro de costos) y `contractor_reference` (referencia del contratante) los envías en el request; `our_number` (nuestro número) lo genera SalesOS y se devuelve en el response (y en cada movimiento del extracto). Por debajo, SalesOS rutea la solicitud al proveedor correcto — no necesitas elegir el proveedor a menos que quieras.

<Note>
  **Cobro ≠ documento fiscal.** Una *Invoice* es la solicitud de pago (boleto, PIX, tarjeta). El documento fiscal (NFS-e en BR, PEPPOL en UE) es un recurso separado y opcional adjunto a la Invoice. Esta separación mantiene la API portable entre BR / US / UE.
</Note>

<Note>
  La API sigue convenciones de mercado: modelo REST por recurso, montos en unidades menores enteras, timestamps ISO-8601, RFC 9457 Problem Details para errores, header `Idempotency-Key` para reintentos seguros, paginación por cursor y webhooks firmados. Si ya integraste con cualquier API moderna de pagos, te sentirás como en casa.
</Note>

## Ruteo por Región

SalesOS elige el backend correcto automáticamente a partir de `customer.country` y `currency`. No necesitas escoger.

| País del cliente    | Moneda      | Estado         | Métodos                                          |
| ------------------- | ----------- | -------------- | ------------------------------------------------ |
| BR                  | BRL         | activo         | `pix`, `boleto`, `bolepix`, `card` (card-as-PIX) |
| US                  | USD         | *próximamente* | `card`, `ach_debit`, `bank_transfer`             |
| Estados miembros UE | EUR / local | *próximamente* | `card`, `sepa_debit`, `bank_transfer`            |

| Región | Tipo de documento fiscal | Estado                                                       |
| ------ | ------------------------ | ------------------------------------------------------------ |
| BR     | `nfse` (servicios)       | activo                                                       |
| BR     | `nfe` (productos)        | *próximamente*                                               |
| UE     | `peppol` (e-invoice)     | *próximamente*                                               |
| US     | n/a                      | sales tax via `metadata` de la Invoice; sin documento fiscal |

Los payouts rutean por destino: BRL → PIX cashout; otras monedas → transferencia internacional.

***

## Cómo Funciona

1. **Obtienes una API Key** en el Dashboard de SalesOS (Admin > Integraciones > API Keys) con los scopes correctos (`payments:write`, `payments:read`).
2. **Emites Invoices** (cobros). SalesOS crea la solicitud de pago en la región del cliente (BR activo; US/UE próximamente). Recibes un artefacto pagable: QR-code/copia-pega PIX, código de barras de boleto, URL de checkout de tarjeta.
3. **(Opcional) Se adjunta un documento fiscal.** Cuando `currency = BRL` y configuras `tax_document.auto_issue: true`, SalesOS emite una NFS-e automáticamente tras la confirmación del pago. También puedes llamar a `POST /invoices/{id}/tax-documents` después para emitir o reintentar.
4. **Pagas a beneficiarios** (empleados, partners, ganadores de premios). SalesOS rutea pagos BRL via PIX y otras monedas via transferencia internacional.
5. **Lees un extracto unificado** — toda factura, payout, comisión y fee de documento fiscal en un único libro mayor, filtrable por `cost_center`, `our_number`, `contractor_reference`, período y proveedor.
6. **Escuchas webhooks** para eventos terminales (`invoice.paid`, `invoice.canceled`, `tax_document.issued`, `payout.paid`, …) en lugar de hacer polling.

***

## Inicio Rápido

<Steps>
  <Step title="Obtener tu API Key">
    Ve a **Admin > Integraciones > API Keys** en el Dashboard de SalesOS. Crea una nueva clave con los scopes `payments:write` y `payments:read`. Copia la clave — solo se mostrará una vez.

    Tu clave luce así: `sk_live_a1b2c3d4e5f6g7h8i9j0...`
  </Step>

  <Step title="Emite tu primera Invoice (BR — cobro PIX con NFS-e automática)">
    Crea un cobro PIX para un cliente brasileño. El bloque opcional `tax_document` indica a SalesOS que emita la NFS-e automáticamente tras el pago.

    ```bash theme={null}
    curl -X POST https://api.play2sell.com/functions/v1/payments/invoices \
      -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
      -H "Idempotency-Key: inv-2026-04-27-001" \
      -H "Content-Type: application/json" \
      -d '{
        "customer": {
          "country": "BR",
          "tax_id": "12345678000190",
          "name": "Acme Corp Ltda",
          "email": "billing@acme.com.br"
        },
        "amount": 12345,
        "currency": "BRL",
        "description": "Consultoría — Abril/2026",
        "payment_method": { "type": "pix", "expires_in_seconds": 3600 },
        "tax_document": {
          "auto_issue": true,
          "type": "nfse",
          "service_code": "01.05"
        },
        "cost_center": "CC-OPERATIONS",
        "contractor_reference": "PO-9981",
        "metadata": { "campaign": "spring-2026", "owner_user_id": "usr-7782" }
      }'
    ```

    **Respuesta (201 Created):**

    ```json theme={null}
    {
      "id": "inv_01HW1Z3K8C7G6Y9PQ4M5R2X0NA",
      "status": "open",
      "amount": 12345,
      "currency": "BRL",
      "region": "br",
      "payment_method": {
        "type": "pix",
        "pix": {
          "qr_code": "00020126580014br.gov.bcb.pix...",
          "qr_code_image_url": "https://api.play2sell.com/.../qr.png",
          "expires_at": "2026-04-27T15:00:00Z"
        }
      },
      "tax_document": {
        "id": "txd_01HW1Z3K8C7G6Y9PQ4M5R2X0NX",
        "type": "nfse",
        "status": "pending",
        "auto_issue": true
      },
      "cost_center": "CC-OPERATIONS",
      "our_number": "INV-2026-000123",
      "contractor_reference": "PO-9981",
      "metadata": { "campaign": "spring-2026", "owner_user_id": "usr-7782" },
      "created": "2026-04-27T14:00:00Z"
    }
    ```

    El status pasa por `open` → `paid` (en la confirmación PIX, \~segundos) → `tax_document.status: issued` (emisión de NFS-e, segundos a minutos). Escucha los webhooks `invoice.paid` y `tax_document.issued` en vez de hacer polling.
  </Step>

  <Step title="(Opcional) Emite una Invoice para cliente US (próximamente)">
    Para clientes US/UE SalesOS rutea el cobro hacia el backend internacional. Esta ruta queda en cola tras el onboarding de Play2Sell LLC — las llamadas hoy devuelven `501 provider_not_configured`.

    ```bash theme={null}
    curl -X POST https://api.play2sell.com/functions/v1/payments/invoices \
      -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
      -H "Idempotency-Key: inv-us-2026-04-27-001" \
      -H "Content-Type: application/json" \
      -d '{
        "customer": {
          "country": "US",
          "name": "Acme Inc",
          "email": "billing@acme.com"
        },
        "amount": 12345,
        "currency": "USD",
        "description": "Consulting services — April/2026",
        "payment_method": { "type": "card" },
        "cost_center": "CC-OPERATIONS",
        "contractor_reference": "PO-9981"
      }'
    ```

    Sin bloque `tax_document` — US no tiene documento fiscal nacional. Sales tax va en `metadata` o futuros `line_items`. **El SSN del cliente no se recoge** en este endpoint: los cobros B2C/B2B regulares en EE.UU. no lo requieren. El manejo de SSN/EIN para reporting 1099 (cuando pagas a contractors US) es un flujo separado en `Payouts`, no en `Invoices`.
  </Step>

  <Step title="Pagar a un beneficiario">
    Paga a un ganador de premio via PIX (BRL — ruteo automático):

    ```bash theme={null}
    curl -X POST https://api.play2sell.com/functions/v1/payments/payouts \
      -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
      -H "Idempotency-Key: payout-2026-04-27-001" \
      -H "Content-Type: application/json" \
      -d '{
        "beneficiary": {
          "name": "Maria Santos",
          "document": "12345678901",
          "phone": "+5511999000111",
          "email": "maria@example.com",
          "bank_account": { "type": "pix", "key_type": "cpf", "key": "12345678901" }
        },
        "amount": 50000,
        "currency": "BRL",
        "purpose": "prize",
        "cost_center": "CC-AWARDS",
        "contractor_reference": "EVENT-Q2-WINNER-12",
        "metadata": { "event_id": "evt-2026-q2", "user_id": "usr-1101" }
      }'
    ```

    **Respuesta (201 Created):**

    ```json theme={null}
    {
      "id": "po_01HW1Z9P7B5F2X8KQ4M5R2X0NB",
      "status": "processing",
      "amount": 50000,
      "currency": "BRL",
      "region": "br",
      "cost_center": "CC-AWARDS",
      "our_number": "PO-2026-000045",
      "contractor_reference": "EVENT-Q2-WINNER-12",
      "created": "2026-04-27T14:05:00Z"
    }
    ```

    Recibirás un webhook `payout.paid` en segundos en sandbox o en menos de un minuto en producción (PIX).
  </Step>

  <Step title="Leer tu extracto">
    Lista cada movimiento etiquetado con `cost_center=CC-AWARDS` del mes:

    ```bash theme={null}
    curl -X GET "https://api.play2sell.com/functions/v1/payments/balance-transactions?cost_center=CC-AWARDS&created[gte]=2026-04-01&created[lte]=2026-04-30&limit=50" \
      -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE"
    ```

    **Respuesta (200 OK):**

    ```json theme={null}
    {
      "data": [
        {
          "id": "btxn_01HW1Z9P7B5F2X8KQ4M5R2X0NC",
          "type": "payout",
          "amount": -50000,
          "currency": "BRL",
          "net": -50100,
          "fee": 100,
          "available_on": "2026-04-27",
          "created": "2026-04-27T14:05:30Z",
          "source": { "resource": "payout", "id": "po_01HW1Z9P7B5F2X8KQ4M5R2X0NB" },
          "cost_center": "CC-AWARDS",
          "our_number": "PO-2026-000045",
          "contractor_reference": "EVENT-Q2-WINNER-12"
        }
      ],
      "has_more": false
    }
    ```
  </Step>
</Steps>

***

## Autenticación

Todas las solicitudes requieren una API Key en el header `Authorization`:

```
Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE
```

Consulta la [página de Autenticación](/es/api/authentication) para detalles sobre cómo crear y administrar API Keys.

| Propiedad                 | Detalles                                                              |
| ------------------------- | --------------------------------------------------------------------- |
| **Header**                | `Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE`              |
| **Scopes**                | `payments:write` (crear/cancelar), `payments:read` (listar/consultar) |
| **Límite de solicitudes** | Configurable por clave (por defecto: 1000 solicitudes/hora)           |
| **Formato de clave**      | `sk_live_` (producción) o `sk_test_` (pruebas)                        |

***

## Entornos

<Tabs>
  <Tab title="Production">
    **URL Base:** `https://api.play2sell.com`

    **Dashboard:** `https://dashboard.play2sell.com`
  </Tab>

  <Tab title="Staging">
    **URL Base:** `https://api-staging.play2sell.com`

    **Dashboard:** `https://dashboard-staging.play2sell.com`

    Staging dirige los payouts a sandboxes de proveedor. No se mueve dinero real.
  </Tab>
</Tabs>

***

## Referencia de Endpoints

Path base: `/functions/v1/payments`. Todos los endpoints aceptan y devuelven JSON.

### Invoices (Cobros)

Una *Invoice* es un cobro — una solicitud de pago que se recolectará de tu cliente vía PIX, boleto, tarjeta u otro método según la región. La Invoice **no** incluye un documento fiscal por defecto; ver [Tax Documents](#tax-documents) abajo.

#### `POST /functions/v1/payments/invoices` — crear un cobro

<ParamField body="customer" type="object" required>
  Cliente que será cobrado.
</ParamField>

<ParamField body="customer.country" type="string" required>
  ISO-3166-1 alpha-2 (`BR`, `US`, `DE`, …). Decide el ruteo de proveedor.
</ParamField>

<ParamField body="customer.tax_id" type="string">
  CPF (11 dígitos) o CNPJ (14 dígitos) para BR (obligatorio cuando `tax_document` está presente). Para cobros US/UE este campo **no** es obligatorio — déjalo fuera salvo que tu flujo contable lo necesite. SSN/EIN no se recolectan aquí en cobros normales.
</ParamField>

<ParamField body="customer.name" type="string" required>
  Razón social o nombre completo (max 255 caracteres).
</ParamField>

<ParamField body="customer.email" type="string" required>
  Email válido — se usa para recibos y (cuando aplique) la entrega del documento fiscal.
</ParamField>

<ParamField body="customer.phone" type="string">
  Teléfono opcional (max 20 caracteres). Formato E.164 recomendado (p. ej. `+5511999000111`). Algunos proveedores lo usan para emparejamiento AML/KYC y notificaciones transaccionales.
</ParamField>

<ParamField body="amount" type="integer" required>
  Monto total en **unidades menores** (ej: `12345` = R\$ 123,45). Debe ser positivo.
</ParamField>

<ParamField body="currency" type="string" required>
  Código ISO-4217. Decide el ruteo de proveedor junto con `customer.country`.
</ParamField>

<ParamField body="description" type="string" required>
  Descripción que aparece en el artefacto de pago y (cuando se emite) en la discriminación del documento fiscal (max 1000 caracteres).
</ParamField>

<Note>
  **El campo `description` se muestra al pagador.** En cobros PIX aparece en la app bancaria/billetera del pagador junto al código QR; en boleto se imprime en el comprobante; en NFS-e va en la discriminación del servicio. Elige un valor que tenga sentido en los tres contextos (ej: `"Consultoría — Abril/2026"`, no `"INV-2026-000123 fila-billing-interna"`).
</Note>

<ParamField body="payment_method" type="object" required>
  Cómo se recolectará el cobro.
</ParamField>

<ParamField body="payment_method.type" type="string" required>
  Uno de `pix`, `boleto`, `bolepix`, `card` (BR); `card`, `ach_debit`, `bank_transfer` (US — próximamente); `card`, `sepa_debit`, `bank_transfer` (UE — próximamente).
</ParamField>

<ParamField body="payment_method.expires_in_seconds" type="integer">
  Para `pix` / `boleto` / `bolepix`: tiempo de validez del artefacto. Por defecto 3600 (PIX) / 3 días (boleto).
</ParamField>

<ParamField body="payment_method.return_url" type="string">
  Para `card`: a dónde redirigir al cliente tras el checkout.
</ParamField>

<ParamField body="tax_document" type="object">
  Opcional. Cuando está presente, SalesOS emitirá un documento fiscal automáticamente tras el pago. Hoy solo BR.
</ParamField>

<ParamField body="tax_document.auto_issue" type="boolean">
  Cuando `true`, el documento fiscal se emite al transitar a `paid`. Cuando `false` (u omitido), usa `POST /v1/invoices/{id}/tax-documents` después.
</ParamField>

<ParamField body="tax_document.type" type="string">
  `"nfse"` (servicios BR — activo), `"nfe"` (productos BR — próximamente), `"peppol"` (UE — próximamente).
</ParamField>

<ParamField body="tax_document.service_code" type="string">
  Código municipal de servicio, obligatorio cuando `type="nfse"` (ej: `"01.05"` para consultoría en SP).
</ParamField>

<ParamField body="cost_center" type="string">
  Código de centro de costos interno (max 64 caracteres). Opcional — cuando se omite, el cobro se clasifica en el centro de costos por defecto del tenant. Enviar un código no registrado retorna `422 unknown_cost_center`. Ver [Campos de Control Comunes](#campos-de-control-comunes).
</ParamField>

<ParamField body="contractor_reference" type="string">
  Referencia del contratante — referencia externa del cliente/contrato (max 64 caracteres).
</ParamField>

<ParamField body="metadata" type="object">
  Pares clave-valor libres (max 50 claves, valor max 500 caracteres).
</ParamField>

<Note>
  `our_number` (nuestro número) **no** va en el request — SalesOS lo genera al emitir y lo devuelve en el response (formato: `INV-<AAAA>-<secuencia>`). Almacénalo para conciliación.
</Note>

#### `GET /functions/v1/payments/invoices/{id}` — consultar

```bash theme={null}
curl -X GET https://api.play2sell.com/functions/v1/payments/invoices/inv_01HW1Z3K8C7G6Y9PQ4M5R2X0NA \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE"
```

La respuesta siempre incluye el artefacto actual de `payment_method` (QR PIX, código de barras de boleto, URL de checkout) y el resumen inline de `tax_document` cuando existe.

#### `GET /functions/v1/payments/invoices` — listar

Filtros: `status` (`open|paid|canceled|failed`), `payment_method.type`, `cost_center`, `our_number`, `contractor_reference`, `created[gte]`, `created[lte]`. Paginación por cursor mediante `limit` (≤ 100), `starting_after`, `ending_before`.

```bash theme={null}
curl -X GET "https://api.play2sell.com/functions/v1/payments/invoices?status=paid&cost_center=CC-OPERATIONS&limit=50" \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE"
```

#### `POST /functions/v1/payments/invoices/{id}/cancel` — anular cobro no pagado

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/payments/invoices/inv_01HW1Z3K8C7G6Y9PQ4M5R2X0NA/cancel \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Idempotency-Key: cancel-inv-001" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "customer_request" }'
```

Si la Invoice tiene un `tax_document` adjunto en estado `issued`, el cancelamiento se cascada al documento fiscal cuando el municipio aún está dentro de la ventana de cancelación. De lo contrario devuelve `cancellation_window_expired`.

<Note>
  **El reembolso de cobros pagados no está expuesto en la v1.** Si necesitas revertir un pago ya liquidado, contacta al soporte — el equipo puede procesarlo manualmente. Una versión futura de la API podría exponer un flujo de reembolso programático conforme los casos de uso maduren.
</Note>

***

### Tax Documents (Documentos Fiscales)

Un *TaxDocument* es el artefacto fiscal adjunto a una Invoice — hoy NFS-e (servicios BR). Tiene su propio ciclo de vida y puede emitirse, consultarse o cancelarse independientemente del cobro subyacente. Usa este recurso cuando optaste por no usar `auto_issue` al crear la Invoice, cuando un intento de auto-emisión falló y quieres reintentar, o cuando necesitas cancelar solo el documento fiscal.

#### `POST /functions/v1/payments/invoices/{id}/tax-documents` — emitir/reintentar

<ParamField body="type" type="string" required>
  `"nfse"` hoy. Futuro: `"nfe"`, `"peppol"`.
</ParamField>

<ParamField body="service_code" type="string">
  Obligatorio cuando `type="nfse"`.
</ParamField>

<ParamField body="metadata" type="object">
  Metadata libre opcional, persistida en el documento.
</ParamField>

```bash theme={null}
curl -X POST https://api.play2sell.com/functions/v1/payments/invoices/inv_01HW1Z3K8C7G6Y9PQ4M5R2X0NA/tax-documents \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Idempotency-Key: txd-2026-04-27-001" \
  -H "Content-Type: application/json" \
  -d '{ "type": "nfse", "service_code": "01.05" }'
```

**Respuesta (202 Accepted):**

```json theme={null}
{
  "id": "txd_01HW1Z3K8C7G6Y9PQ4M5R2X0NX",
  "invoice_id": "inv_01HW1Z3K8C7G6Y9PQ4M5R2X0NA",
  "type": "nfse",
  "status": "pending",
  "created": "2026-04-27T14:10:00Z"
}
```

La emisión es asíncrona. Escucha los webhooks `tax_document.issued` (o `tax_document.failed`). En éxito el documento trae `document_number`, `xml_url`, `pdf_url` e `issued_at`.

#### `GET /functions/v1/payments/invoices/{id}/tax-documents/{doc_id}` — consultar

#### `POST /functions/v1/payments/invoices/{id}/tax-documents/{doc_id}/cancel` — cancelar

```bash theme={null}
curl -X POST "https://api.play2sell.com/functions/v1/payments/invoices/inv_.../tax-documents/txd_.../cancel" \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Idempotency-Key: cancel-txd-001" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "billing_correction" }'
```

Sujeto a la ventana de cancelación del municipio (típicamente el día de la emisión para NFS-e). `cancellation_window_expired` se devuelve fuera de la ventana.

***

### Payouts (Pagos a Beneficiarios)

#### `POST /functions/v1/payments/payouts` — pagar a un beneficiario

Ruteo automático: `currency = "BRL"` → PIX cashout; otras monedas → transferencia internacional.

<ParamField body="beneficiary" type="object" required>
  Destinatario del payout.
</ParamField>

<ParamField body="beneficiary.name" type="string" required>
  Nombre completo o razón social (max 255 caracteres).
</ParamField>

<ParamField body="beneficiary.document" type="string" required>
  Documento de identificación fiscal del beneficiario. CPF (11 dígitos) o CNPJ (14 dígitos) para BR; pasaporte o ID fiscal local para internacional. **Obligatorio.**
</ParamField>

<ParamField body="beneficiary.phone" type="string" required>
  Teléfono en formato E.164 (ej: `"+5511999000111"`). **Obligatorio** — usado para AML/KYC y notificaciones de estado del payout.
</ParamField>

<ParamField body="beneficiary.email" type="string">
  Email opcional. Cuando se provee, los recibos del payout se envían a esta dirección.
</ParamField>

<ParamField body="beneficiary.bank_account" type="object" required>
  Cuenta destino. Para PIX usa `type: "pix"` con `key_type` (`cpf|cnpj|email|phone|evp`) y `key`. Para internacional usa `type: "bank_transfer"` con `country`, `iban` o `account_number` + `routing_number` según los requisitos bancarios del país de destino.
</ParamField>

<ParamField body="amount" type="integer" required>
  Monto en unidades menores. Debe ser positivo.
</ParamField>

<ParamField body="currency" type="string" required>
  ISO-4217. Determina el ruteo de proveedor.
</ParamField>

<ParamField body="purpose" type="string" required>
  Uno de `"prize"`, `"commission"`, `"vendor_payment"`, `"other"`.
</ParamField>

<ParamField body="cost_center" type="string">
  Código de centro de costos (max 64 caracteres). Opcional — cuando se omite, el payout se clasifica en el centro de costos por defecto del tenant. Enviar un código no registrado retorna `422 unknown_cost_center`.
</ParamField>

<ParamField body="contractor_reference" type="string">
  Referencia del contratante — referencia externa (max 64 caracteres).
</ParamField>

<ParamField body="metadata" type="object">
  Pares clave-valor libres (max 50 claves, valor max 500 caracteres).
</ParamField>

<Note>
  `our_number` (nuestro número) **no** va en el request — SalesOS lo genera al emitir y lo devuelve en el response (formato: `PO-<AAAA>-<secuencia>`). Almacénalo para conciliación.
</Note>

#### `GET /functions/v1/payments/payouts/{id}` — consultar

#### `GET /functions/v1/payments/payouts` — listar

Filtros: `status`, `provider`, `cost_center`, `our_number`, `contractor_reference`, `created[gte]`, `created[lte]`. Paginación por cursor.

#### `POST /functions/v1/payments/payouts/{id}/cancel` — cancelar

Disponible solo para transferencias internacionales en estado `created` / `incoming_payment_waiting`. PIX es síncrono y final — una vez aceptado no se cancela; emite un payout en sentido opuesto como reverso.

***

### Balance Transactions (Extracto Bancario)

Libro mayor unificado entre proveedores. Solo lectura.

#### `GET /functions/v1/payments/balance-transactions` — listar

Filtros:

| Filtro                          | Valores                                 |
| ------------------------------- | --------------------------------------- |
| `account`                       | `omie`, `rinne`, `wise`                 |
| `type`                          | `charge`, `payout`, `fee`, `adjustment` |
| `cost_center`                   | cualquier string                        |
| `our_number`                    | cualquier string                        |
| `contractor_reference`          | cualquier string                        |
| `currency`                      | código ISO-4217                         |
| `created[gte]` / `created[lte]` | timestamps ISO-8601                     |

```bash theme={null}
curl -X GET "https://api.play2sell.com/functions/v1/payments/balance-transactions?type=payout&created[gte]=2026-04-01&limit=100" \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE"
```

#### `GET /functions/v1/payments/balance-transactions/{id}` — consultar

Cada movimiento expone:

<ResponseField name="id" type="string">
  ID estable del movimiento (`btxn_...`).
</ResponseField>

<ResponseField name="type" type="string">
  `charge | payout | fee | adjustment`.
</ResponseField>

<ResponseField name="amount" type="integer">
  Monto en unidades menores con signo. Negativo = salida, positivo = entrada.
</ResponseField>

<ResponseField name="currency" type="string">
  ISO-4217.
</ResponseField>

<ResponseField name="net" type="integer">
  `amount - fee` para salidas; `amount` para entradas.
</ResponseField>

<ResponseField name="fee" type="integer">
  Comisión del proveedor en unidades menores, siempre positiva.
</ResponseField>

<ResponseField name="available_on" type="string">
  Fecha ISO-8601 de liquidación de los fondos.
</ResponseField>

<ResponseField name="created" type="string">
  Timestamp ISO-8601 del movimiento.
</ResponseField>

<ResponseField name="source" type="object">
  `{ resource, id }` — apunta a la factura/payout origen.
</ResponseField>

<ResponseField name="cost_center" type="string">
  Heredado del recurso de origen.
</ResponseField>

<ResponseField name="our_number" type="string">
  Heredado del recurso de origen.
</ResponseField>

<ResponseField name="contractor_reference" type="string">
  Heredado del recurso de origen.
</ResponseField>

***

## Campos de Control Comunes

Toda Invoice, Payout y Balance Transaction lleva los mismos campos de control. Hacen round-trip en `GET` y son filtrables en `LIST`.

| Campo                  | Dirección        | Formato                                        | Max                             | Indexado | Uso                                                                                                                                                                                                                            |
| ---------------------- | ---------------- | ---------------------------------------------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cost_center`          | input (opcional) | string, FK a tu tabla de centros de costo      | 64                              | sí       | Clasifica cada transacción contra una línea de presupuesto. Cuando se omite, usa el centro de costos por defecto del tenant.                                                                                                   |
| `contractor_reference` | input (opcional) | string                                         | 64                              | sí       | Referencia del contratante — referencia externa cliente/contrato (PO, ID de contrato, ID de evento).                                                                                                                           |
| `metadata`             | input (opcional) | `Record<string, string>`                       | 50 claves, valor 500 caracteres | no       | Libre. Usar con moderación; no indexable.                                                                                                                                                                                      |
| `our_number`           | **solo output**  | string, formato `<PREFIJO>-<AAAA>-<secuencia>` | 64                              | sí       | Nuestro número — generado por SalesOS al emitir y devuelto en el response. Distinto de `id` (ULID para uso en la API). Estable, secuencial por tipo de recurso por tenant. Úsalo como clave de conciliación del lado bancario. |

<Note>
  **Por qué `our_number` lo genera el servidor.** En el banking brasileño el *cedente* asigna el "nuestro número" — pero en esta API SalesOS es el gateway de emisión, así que SalesOS asigna el valor y lo devuelve. Si necesitas llevar tu identificador interno, usa `contractor_reference` (top-level, indexado) o `metadata.*` (libre).
</Note>

***

## Idempotencia

Todos los endpoints `POST` **requieren** el header `Idempotency-Key` — una cadena única a tu elección (max 255 caracteres; recomendamos UUIDv4 o una clave determinística derivada de tu dominio).

* La primera llamada con la clave procesa normalmente y la respuesta se almacena por 24 h.
* Reintentos con la misma clave y el mismo body devuelven la **misma respuesta**, con el header `Idempotent-Replay: true`.
* Reintentos con la misma clave pero body **distinto** devuelven `409 idempotency_key_reused`.

```bash theme={null}
# Primera llamada — procesa
curl -X POST https://api.play2sell.com/functions/v1/payments/invoices \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Idempotency-Key: inv-2026-04-27-001" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 12345, "currency": "BRL", "payment_method": { "type": "pix" }, "...": "..." }'

# Segunda llamada (reintento de red) — misma respuesta, sin duplicación
curl -X POST https://api.play2sell.com/functions/v1/payments/invoices \
  -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
  -H "Idempotency-Key: inv-2026-04-27-001" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 12345, "currency": "BRL", "payment_method": { "type": "pix" }, "...": "..." }'
```

***

## Montos y Moneda

Los montos son enteros en **unidades menores** para evitar errores de punto flotante:

| Moneda | Unidad menor | `12345` significa |
| ------ | ------------ | ----------------- |
| `BRL`  | centavo      | R\$ 123,45        |
| `USD`  | cent         | US\$ 123.45       |
| `EUR`  | cent         | €123,45           |

Las monedas siguen ISO-4217 (3 letras mayúsculas). Siempre acompaña `amount` con `currency`. Rechaza en el servidor cualquier payload que mezcle escalas (ej: enviar decimales).

***

## Manejo de Errores

Todos los errores siguen [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457):

```json theme={null}
{
  "type": "https://docs.play2sell.com/errors/validation_error",
  "title": "Invalid request",
  "status": 422,
  "detail": "amount must be a positive integer",
  "instance": "req_01HW1Z3K8C7G6Y9PQ4M5R2X0NA",
  "code": "validation_error",
  "errors": [
    { "field": "amount", "message": "must be a positive integer" }
  ]
}
```

<AccordionGroup>
  <Accordion title="400 — bad_request">
    JSON malformado o headers obligatorios ausentes. Corrige la solicitud y reenvía.
  </Accordion>

  <Accordion title="401 — unauthorized">
    API key faltante, inválida o expirada. Verifica el header `Authorization`. Genera una clave nueva si expiró.
  </Accordion>

  <Accordion title="403 — insufficient_scope">
    Clave válida pero sin el scope requerido (`payments:write` o `payments:read`). Edita la clave en el Dashboard.
  </Accordion>

  <Accordion title="404 — not_found">
    El ID del recurso no existe o pertenece a otro tenant.
  </Accordion>

  <Accordion title="409 — idempotency_key_reused">
    Misma `Idempotency-Key` usada con body distinto. Usa una clave nueva o reenvía el body original.
  </Accordion>

  <Accordion title="422 — validation_error y relacionados">
    La solicitud fue entendida pero no puede procesarse. Inspecciona el campo `code` para desambiguar:

    * **`validation_error`** — la validación del body falló; el array `errors[]` lista los problemas a nivel de campo. Corrige los datos y reenvía con `Idempotency-Key` nueva.
    * **`unsupported_region`** — la combinación `country` / `currency` del cliente aún no está habilitada (hoy solo `BR` / `BRL` está activo). Espera a que la región esté disponible, o cobra a un cliente en una región soportada.
    * **`unknown_cost_center`** — el código `cost_center` enviado no está registrado para tu tenant. Créalo en **Admin > Integraciones > Centros de Costo** antes de reenviar, u omite el campo para usar el centro de costo por defecto del tenant.
    * **`merchant_not_provisioned`** — tu tenant no tiene un merchant de pago activo para el centro de costo resuelto. Contacta al soporte para finalizar el provisionamiento antes de reintentar.
  </Accordion>

  <Accordion title="429 — rate_limited">
    Demasiadas solicitudes. El header `Retry-After` indica los segundos a esperar.
  </Accordion>

  <Accordion title="502 — provider_error">
    El backend downstream devolvió error. El campo `detail` contiene un mensaje sanitizado. Reenvía con la misma `Idempotency-Key`.
  </Accordion>

  <Accordion title="500 — server_error">
    Error interno. Reenvía con backoff exponencial (2s, 4s, 8s). Contacta al soporte si persiste.
  </Accordion>
</AccordionGroup>

***

## Webhooks

Configura un endpoint de webhook por entorno en **Admin > Integraciones > Webhooks**. SalesOS hará POST con JSON firmado para cada evento terminal:

| Evento                  | Recurso       | Disparado cuando                                                     |
| ----------------------- | ------------- | -------------------------------------------------------------------- |
| `invoice.paid`          | invoice       | Cobro confirmado pagado por el proveedor                             |
| `invoice.canceled`      | invoice       | Cobro no pagado anulado                                              |
| `invoice.failed`        | invoice       | Cobro falló definitivamente (ej: tarjeta rechazada, boleto expirado) |
| `tax_document.issued`   | tax\_document | Documento fiscal aceptado por el municipio                           |
| `tax_document.canceled` | tax\_document | Cancelamiento del documento fiscal aceptado                          |
| `tax_document.failed`   | tax\_document | Emisión falló definitivamente                                        |
| `payout.paid`           | payout        | Fondos confirmados como entregados                                   |
| `payout.failed`         | payout        | Proveedor rechazó la transferencia                                   |
| `payout.canceled`       | payout        | Cancelamiento aceptado (solo transferencias internacionales)         |

Cada solicitud incluye:

* `X-Pay-Event` — tipo del evento (ej: `payout.paid`).
* `X-Pay-Signature` — `t=<unix>,v1=<hex-hmac-sha256>`. Verifica con el secreto configurado en el Dashboard.
* `X-Pay-Delivery` — ID único de entrega, útil para deduplicación.

Verificación de firma (Node.js):

```javascript theme={null}
import crypto from 'node:crypto';

function verifyWebhook(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map(kv => kv.split('=')),
  );
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');
  const ok = crypto.timingSafeEqual(
    Buffer.from(parts.v1, 'hex'),
    Buffer.from(expected, 'hex'),
  );
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // 5 min
  return ok && fresh;
}
```

Payload de ejemplo:

```json theme={null}
{
  "id": "evt_01HW1Z9P7B5F2X8KQ4M5R2X0ND",
  "type": "payout.paid",
  "created": "2026-04-27T14:05:30Z",
  "data": {
    "id": "po_01HW1Z9P7B5F2X8KQ4M5R2X0NB",
    "status": "paid",
    "amount": 50000,
    "currency": "BRL",
    "cost_center": "CC-AWARDS",
    "our_number": "PO-2026-000045",
    "contractor_reference": "EVENT-Q2-WINNER-12"
  }
}
```

***

## Ejemplos Completos de Código

<CodeGroup>
  ```bash cURL theme={null}
  # Define las credenciales (ver /es/api/authentication para el helper de firma)
  export SALESOS_API_KEY="sk_live_YOUR_API_KEY"
  export SALESOS_API_SECRET="YOUR_API_KEY_SECRET"
  export SALESOS_URL="https://api.play2sell.com/functions/v1/payments"

  # 1. Issue an invoice (BR PIX charge with auto NFS-e)
  curl -s -X POST "$SALESOS_URL/invoices" \
    -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "customer": { "country": "BR", "tax_id": "12345678000190", "name": "Acme Corp", "email": "billing@acme.com" },
      "amount": 12345, "currency": "BRL",
      "description": "Consulting — April/2026",
      "payment_method": { "type": "pix", "expires_in_seconds": 3600 },
      "tax_document": { "auto_issue": true, "type": "nfse", "service_code": "01.05" },
      "cost_center": "CC-OPERATIONS", "contractor_reference": "PO-9981"
    }' | jq .

  # 2. Pay a prize via PIX
  curl -s -X POST "$SALESOS_URL/payouts" \
    -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" \
    -H "Idempotency-Key: $(uuidgen)" \
    -H "Content-Type: application/json" \
    -d '{
      "beneficiary": {
        "name": "Maria Santos", "document": "12345678901", "phone": "+5511999000111", "email": "maria@example.com",
        "bank_account": { "type": "pix", "key_type": "cpf", "key": "12345678901" }
      },
      "amount": 50000, "currency": "BRL", "purpose": "prize",
      "cost_center": "CC-AWARDS", "contractor_reference": "EVENT-Q2-WINNER-12"
    }' | jq .

  # 3. Read the statement for the cost center
  curl -s -X GET "$SALESOS_URL/balance-transactions?cost_center=CC-AWARDS&created[gte]=2026-04-01&limit=100" \
    -H "Authorization: P2S-SIGN-V1 API_KEY:TIMESTAMP:SIGNATURE" | jq .
  ```

  ```javascript Node.js theme={null}
  import { randomUUID } from 'node:crypto';

  const API_URL = 'https://api.play2sell.com/functions/v1/payments';
  const API_KEY = process.env.SALESOS_API_KEY;

  async function call(path, { method = 'GET', body } = {}) {
    const headers = {
      // P2S-SIGN-V1 — ver signedRequest() en /es/api/authentication
      'Authorization': await signedHeader(method, path, body),
      'Content-Type': 'application/json',
    };
    if (method === 'POST') headers['Idempotency-Key'] = randomUUID();

    const res = await fetch(`${API_URL}${path}`, {
      method,
      headers,
      body: body ? JSON.stringify(body) : undefined,
    });

    if (!res.ok) {
      const problem = await res.json();
      throw new Error(`${problem.code}: ${problem.detail || problem.title}`);
    }
    return res.json();
  }

  const invoice = await call('/invoices', {
    method: 'POST',
    body: {
      customer: { country: 'BR', tax_id: '12345678000190', name: 'Acme Corp', email: 'billing@acme.com' },
      amount: 12345,
      currency: 'BRL',
      description: 'Consulting — April/2026',
      payment_method: { type: 'pix', expires_in_seconds: 3600 },
      tax_document: { auto_issue: true, type: 'nfse', service_code: '01.05' },
      cost_center: 'CC-OPERATIONS',
      contractor_reference: 'PO-9981',
    },
  });
  console.log(`Invoice issued: ${invoice.id} (our_number: ${invoice.our_number}, pix qr: ${invoice.payment_method.pix.qr_code})`);

  const payout = await call('/payouts', {
    method: 'POST',
    body: {
      beneficiary: {
        name: 'Maria Santos',
        document: '12345678901',
        phone: '+5511999000111',
        email: 'maria@example.com',
        bank_account: { type: 'pix', key_type: 'cpf', key: '12345678901' },
      },
      amount: 50000,
      currency: 'BRL',
      purpose: 'prize',
      cost_center: 'CC-AWARDS',
      contractor_reference: 'EVENT-Q2-WINNER-12',
    },
  });
  console.log(`Payout sent: ${payout.id} (${payout.status}, our_number: ${payout.our_number})`);

  const statement = await call(
    '/balance-transactions?cost_center=CC-AWARDS&created[gte]=2026-04-01&limit=100',
  );
  console.log(`Statement: ${statement.data.length} entries`);
  ```

  ```python Python theme={null}
  import os
  import uuid
  import requests

  API_URL = 'https://api.play2sell.com/functions/v1/payments'
  API_KEY = os.environ['SALESOS_API_KEY']

  def call(path: str, method: str = 'GET', body: dict | None = None) -> dict:
      headers = {
          'Authorization': f'Bearer {API_KEY}',
          'Content-Type': 'application/json',
      }
      if method == 'POST':
          headers['Idempotency-Key'] = str(uuid.uuid4())

      r = requests.request(method, f'{API_URL}{path}', json=body, headers=headers, timeout=30)
      if not r.ok:
          problem = r.json()
          raise Exception(f"{problem.get('code')}: {problem.get('detail') or problem.get('title')}")
      return r.json()


  invoice = call('/invoices', 'POST', {
      'customer': {'country': 'BR', 'tax_id': '12345678000190', 'name': 'Acme Corp', 'email': 'billing@acme.com'},
      'amount': 12345,
      'currency': 'BRL',
      'description': 'Consulting — April/2026',
      'payment_method': {'type': 'pix', 'expires_in_seconds': 3600},
      'tax_document': {'auto_issue': True, 'type': 'nfse', 'service_code': '01.05'},
      'cost_center': 'CC-OPERATIONS',
      'contractor_reference': 'PO-9981',
  })
  print(f"Invoice issued: {invoice['id']} (our_number: {invoice['our_number']})")
  print(f"PIX QR code: {invoice['payment_method']['pix']['qr_code']}")

  payout = call('/payouts', 'POST', {
      'beneficiary': {
          'name': 'Maria Santos',
          'document': '12345678901',
          'phone': '+5511999000111',
          'email': 'maria@example.com',
          'bank_account': {'type': 'pix', 'key_type': 'cpf', 'key': '12345678901'},
      },
      'amount': 50000,
      'currency': 'BRL',
      'purpose': 'prize',
      'cost_center': 'CC-AWARDS',
      'contractor_reference': 'EVENT-Q2-WINNER-12',
  })
  print(f"Payout sent: {payout['id']} ({payout['status']}, our_number: {payout['our_number']})")

  statement = call('/balance-transactions?cost_center=CC-AWARDS&created[gte]=2026-04-01&limit=100')
  print(f"Statement: {len(statement['data'])} entries")
  ```

  ```php PHP theme={null}
  <?php

  $apiUrl       = 'https://api.play2sell.com/functions/v1/payments';
  $apiKey       = getenv('SALESOS_API_KEY');
  $apiKeySecret = getenv('SALESOS_API_SECRET');

  // Construye el header Authorization P2S-SIGN-V1. Aviso: hash_hmac() de
  // PHP recibe (datos, clave) — al revés de Node/Python.
  function p2sSignedHeader(string $method, string $fullPath, string $body): string {
      global $apiKey, $apiKeySecret;
      $ts          = (string) time();
      $payloadHash = hash('sha256', $body);
      $k1  = hash_hmac('sha256', $apiKey,      $apiKeySecret, true);
      $k2  = hash_hmac('sha256', $ts,          $k1,           true);
      $k3  = hash_hmac('sha256', $method,      $k2,           true);
      $k4  = hash_hmac('sha256', $fullPath,    $k3,           true);
      $sig = hash_hmac('sha256', $payloadHash, $k4);  // hex
      return "P2S-SIGN-V1 {$apiKey}:{$ts}:{$sig}";
  }

  function call(string $path, string $method = 'GET', ?array $body = null): array {
      global $apiUrl;
      $bodyStr  = $body ? json_encode($body) : '';
      $fullPath = parse_url($apiUrl, PHP_URL_PATH) . $path;

      $headers = [
          'Authorization: ' . p2sSignedHeader($method, $fullPath, $bodyStr),
          'Content-Type: application/json',
      ];
      if ($method === 'POST') {
          $headers[] = 'Idempotency-Key: ' . bin2hex(random_bytes(16));
      }

      $ch = curl_init($apiUrl . $path);
      curl_setopt_array($ch, [
          CURLOPT_CUSTOMREQUEST => $method,
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_HTTPHEADER => $headers,
          CURLOPT_POSTFIELDS => $bodyStr ?: null,
          CURLOPT_TIMEOUT => 30,
      ]);

      $res = curl_exec($ch);
      $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);
      $json = json_decode($res, true);

      if ($code >= 400) {
          throw new Exception(($json['code'] ?? 'error') . ': ' . ($json['detail'] ?? $json['title'] ?? 'unknown'));
      }
      return $json;
  }

  $invoice = call('/invoices', 'POST', [
      'customer' => ['country' => 'BR', 'tax_id' => '12345678000190', 'name' => 'Acme Corp', 'email' => 'billing@acme.com'],
      'amount' => 12345,
      'currency' => 'BRL',
      'description' => 'Consulting — April/2026',
      'payment_method' => ['type' => 'pix', 'expires_in_seconds' => 3600],
      'tax_document' => ['auto_issue' => true, 'type' => 'nfse', 'service_code' => '01.05'],
      'cost_center' => 'CC-OPERATIONS',
      'contractor_reference' => 'PO-9981',
  ]);
  echo "Invoice issued: {$invoice['id']} (our_number: {$invoice['our_number']})\n";
  echo "PIX QR: {$invoice['payment_method']['pix']['qr_code']}\n";

  $payout = call('/payouts', 'POST', [
      'beneficiary' => [
          'name' => 'Maria Santos',
          'document' => '12345678901',
          'phone' => '+5511999000111',
          'email' => 'maria@example.com',
          'bank_account' => ['type' => 'pix', 'key_type' => 'cpf', 'key' => '12345678901'],
      ],
      'amount' => 50000,
      'currency' => 'BRL',
      'purpose' => 'prize',
      'cost_center' => 'CC-AWARDS',
      'contractor_reference' => 'EVENT-Q2-WINNER-12',
  ]);
  echo "Payout sent: {$payout['id']} ({$payout['status']}, our_number: {$payout['our_number']})\n";
  ```
</CodeGroup>

***

## Mejores Prácticas

### Higiene de centros de costos

Elige un conjunto pequeño y estable de códigos (≤ 50). Documéntalos en el wiki de finanzas. Rechaza en el servidor llamadas que referencien códigos desconocidos — fallar ruidosamente es mejor que clasificar mal en silencio.

### Usa ambas claves juntas para conciliación

* **`our_number` (generado por el servidor)** es tu **clave bancaria** — impreso en el boleto / enviado al proveedor, presente en tu archivo de extracto bancario.
* **`contractor_reference` (lo proporcionas)** es tu **clave de contrato** — el PO, ID de contrato con vendor o ID del evento referente; presente en tu ERP.

Cruzar ambas en el cierre mensual te da una conciliación de un clic entre el banco, la API Payments de SalesOS y tu ERP.

### Ruteo por región

SalesOS rutea automáticamente por `customer.country` y `currency`. No necesitas escoger backend; confía en el ruteo.

### Confiabilidad de webhooks

Trata los webhooks como fuente de verdad para estados terminales. El polling funciona pero consume rate limit. Verifica siempre `X-Pay-Signature` y deduplica por `X-Pay-Delivery`.

### Manejo de fallos

* **422:** corrige los datos y reenvía con `Idempotency-Key` nueva.
* **429:** espera según `Retry-After`.
* **502 (provider\_error):** reenvía con la **misma** `Idempotency-Key` — la solicitud original no hizo commit, así que reintentar es seguro.
* **5xx:** backoff exponencial (2s, 4s, 8s). Contacta al soporte si persiste.

***

## Límites de Solicitudes

Cada API key tiene un límite configurable (por defecto: 1000 solicitudes por hora). El contador se reinicia cada hora.

| Límite                             | Valor       |
| ---------------------------------- | ----------- |
| Solicitudes por hora (por defecto) | 1000        |
| Tamaño máximo del payload          | 1 MB        |
| Tamaño máximo de página en listas  | 100         |
| Timeout por solicitud              | 60 segundos |

Las respuestas con rate limit incluyen el header `Retry-After` (segundos).

***

## Seguridad

* Las API keys se hashean con bcrypt — nunca se almacenan en texto plano.
* Cada clave está limitada a un único tenant — sin acceso entre tenants.
* Se pueden configurar listas de IPs permitidas por clave.
* Todas las solicitudes se registran con fines de auditoría (inmutable, retención de 7 años).
* Las claves pueden revocarse instantáneamente desde el Dashboard.

<Warning>
  Nunca expongas tu API key en código del lado del cliente (JavaScript en el navegador, apps móviles o repositorios públicos). La API Payments solo debe ser llamada desde tu servidor backend.
</Warning>

***

## Próximos Pasos

<CardGroup cols={2}>
  <Card title="Autenticación" icon="key" href="/es/api/authentication">
    Aprende a crear y administrar API Keys
  </Card>

  <Card title="Integración Activities" icon="plug" href="/es/api/integrations/activities">
    Envía actividades de CRM a SalesOS
  </Card>

  <Card title="Soporte" icon="headset" href="mailto:suporte@play2sell.com">
    ¿Necesitas ayuda? Contacta a nuestro equipo de soporte
  </Card>
</CardGroup>
