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

# Crear cargo

> Endpoint para crear un cargo en el sistema de OnePay.

### Headers

<ParamField header="x-idempotency" type="string" required placeholder="Token único para garantizar la idempotencia de la petición">
  Token único para garantizar la idempotencia de la petición
</ParamField>

### Body

<ParamField body="title" type="string" required>
  Descripción del cargo en el estado de cuenta del banco
</ParamField>

<ParamField body="customer_id" type="string" required placeholder="9dd4158b-0e45-42bc-b56f-a4c1f856814d">
  ID del cliente que asumirá el cargo. [Crear cliente](/client/customers/create).
</ParamField>

<ParamField body="amount" type="number" required placeholder="100000">
  Monto del cargo en **unidades mayores de la moneda**, no en centavos. Enviar `100000` con `currency: "COP"` cobra \$100.000 COP.

  <Warning>
    Versiones anteriores de esta página decían "centavos". Era un error de la documentación: el
    comportamiento del endpoint no ha cambiado. Si tu integración multiplicaba por 100 para
    compensar, estabas cobrando 100 veces el monto previsto.
  </Warning>

  La unidad es la misma para toda moneda y país. No la confundas con
  [`POST /customer-session/charges`](/client/customer-session/create-charge), que sí recibe centavos.
</ParamField>

<ParamField body="account_id" type="string" placeholder="9d0ac58c-2226-4f1c-b305-15e6dc3b47f6">
  ID de la cuenta bancaria registrada. [Ver creación de cuentas](/client/accounts/create). Requerido si `card_id` está vacío.
</ParamField>

<ParamField body="card_id" type="string" placeholder="bb02b2fd-154c-4c90-9fdf-cec3a0b25cf5">
  ID de la tarjeta tokenizada. [Conoce cómo registrar tarjetas](/client/cards/create). Requerido si `account_id` está vacío.
</ParamField>

<ParamField body="installments" type="integer" placeholder="1">
  Número de cuotas para el cargo. Opcional (útil para flujos VIP).
  Valores permitidos: `1` a `36`.
  Valor por defecto: `1`.
</ParamField>

<ParamField body="currency" type="string" placeholder="COP">
  Moneda del cargo
</ParamField>

<ParamField body="redirect_url" type="string" placeholder="https://onepay.la">
  URL de redirección
</ParamField>

<ParamField body="external_id" type="string" placeholder="invoice-123">
  ID único de tu sistema para correlacionar el cargo con tus registros internos. Este campo te permite rastrear el cargo cuando lleguen los eventos de webhook.

  * **Longitud:** 1 a 100 caracteres
  * **Opcional:** Si no se proporciona, el campo será `null`
  * **Aparece en:** el response de creación, los webhooks `charge.*` bajo `charge.external_id`, y
    los webhooks `wallet.transaction.created` de los movimientos que genere el cobro

  <Note>
    Este campo es útil para asociar el cargo con tu ID de factura, orden, o cualquier otro identificador de tu sistema.
  </Note>
</ParamField>

<ParamField body="splits" type="array">
  Los pagos divididos permiten distribuir automáticamente un único pago entre múltiples destinatarios según reglas preestablecidas, optimizando la gestión de fondos sin necesidad de cálculos manuales o costos adicionales.

  <Expandable title="Campos de splits[]">
    <ParamField body="splits[].customer_id" type="string" required>
      ID del cliente beneficiario del split. [Ver clientes](/client/customers/create).
    </ParamField>

    <ParamField body="splits[].split_type" type="string" required>
      Tipo de distribución:

      * `percentage` — Porcentaje entero del monto total (`10` = 10%)
      * `fixed` — Monto fijo en unidades mayores de la moneda (no en centavos)

      <Warning>
        Si el total de splits supera el 100% del monto o excede el monto total, la operación será rechazada.
      </Warning>
    </ParamField>

    <ParamField body="splits[].split_value" type="integer" required>
      Valor del split según el tipo:

      * Si `split_type` es `percentage`: porcentaje entero (`5` = 5%, `100` = 100%)
      * Si `split_type` es `fixed`: monto en la misma unidad que `amount` (unidades mayores, no centavos)

      <Warning>
        Versiones anteriores de esta página describían el porcentaje "en centésimas" (`10000` = 100%).
        Era un error: la suma de los splits de tipo `percentage` no puede pasar de `100`, así que
        esos valores se rechazan con un 422.
      </Warning>
    </ParamField>

    <ParamField body="splits[].description" type="string">
      Descripción interna del split.
    </ParamField>

    <ParamField body="splits[].account_id" type="string">
      ID de la cuenta donde se recibirá el split. Si no se especifica, se usa la cuenta principal del cliente.

      Para dividir hacia otra empresa que usa OnePay, registra su saldo como una cuenta y usa ese
      `account_id`. [Ver la guía](/client/accounts/onepay-balance).
    </ParamField>

    <ParamField body="splits[].to_company_wallet" type="boolean" deprecated>
      Forma anterior de dividir hacia el saldo de otra empresa: `true` y el identificador del saldo
      ajeno en `account_id`, en cada cobro.

      <Warning>
        Obsoleto. Registra ese saldo como una cuenta del cliente y usa `customer_id` + `account_id`
        como en cualquier otro split. [Cómo migrar](/client/accounts/onepay-balance#migrar-desde-to-company-wallet).
        Ambas formas funcionan durante la transición.
      </Warning>
    </ParamField>

    <ParamField body="splits[].scheduled_at" type="string">
      Fecha programada para realizar el split (`YYYY-MM-DD HH:mm:ss`). Si es `null`, se ejecuta de inmediato.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="test_scenario" type="string">
  Escenario de prueba para el cargo \[Opcional].
  [Ver escenarios de prueba](/client/charges/scenarios)
</ParamField>

<Note>
  **Métodos de pago permitidos**

  El método de pago disponible depende de la configuración de tu cuenta. Comunícate con el equipo de soporte para habilitar métodos adicionales.

  | Método                    | Campo requerido | Descripción                                     |
  | ------------------------- | --------------- | ----------------------------------------------- |
  | Cuenta bancaria (ACH)     | `account_id`    | Débito directo desde cuenta bancaria registrada |
  | Tarjeta de crédito/débito | `card_id`       | Cargo a tarjeta tokenizada                      |

  Solo uno de los dos (`account_id` o `card_id`) debe estar presente en la petición.
</Note>

<RequestExample>
  ```bash cURL - Cargo con tarjeta theme={null}
  curl https://api.onepay.la/v1/charges \
    -X POST \
    -H "Authorization: Bearer sk_test_xxx" \
    -H "Content-Type: application/json" \
    -H "x-idempotency: charge-card-001" \
    -d '{
      "title": "Mensualidad Enero 2025",
      "customer_id": "9dd4158b-0e45-42bc-b56f-a4c1f856814d",
      "amount": 85000,
      "card_id": "9e01eeae-2868-4564-9d04-84d1d1d027d2",
      "installments": 3,
      "currency": "COP",
      "external_id": "invoice-2025-001"
    }'
  ```

  ```bash cURL - Cargo con cuenta bancaria theme={null}
  curl https://api.onepay.la/v1/charges \
    -X POST \
    -H "Authorization: Bearer sk_test_xxx" \
    -H "Content-Type: application/json" \
    -H "x-idempotency: charge-account-001" \
    -d '{
      "title": "Cuota mensual",
      "customer_id": "9dd4158b-0e45-42bc-b56f-a4c1f856814d",
      "amount": 200000,
      "account_id": "84cc072e-90e8-33cf-9305-098095fed32f",
      "currency": "COP",
      "external_id": "subscription-456"
    }'
  ```

  ```javascript JavaScript - Cargo con tarjeta theme={null}
  const response = await fetch('https://api.onepay.la/v1/charges', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_test_xxx',
      'Content-Type': 'application/json',
      'x-idempotency': 'charge-card-001'
    },
    body: JSON.stringify({
      title: 'Mensualidad Enero 2025',
      customer_id: '9dd4158b-0e45-42bc-b56f-a4c1f856814d',
      amount: 85000,
      card_id: '9e01eeae-2868-4564-9d04-84d1d1d027d2',
      installments: 3,
      currency: 'COP',
      external_id: 'invoice-2025-001'
    })
  });

  const charge = await response.json();
  ```

  ```javascript JavaScript - Cargo con cuenta bancaria theme={null}
  const response = await fetch('https://api.onepay.la/v1/charges', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_test_xxx',
      'Content-Type': 'application/json',
      'x-idempotency': 'charge-account-001'
    },
    body: JSON.stringify({
      title: 'Cuota mensual',
      customer_id: '9dd4158b-0e45-42bc-b56f-a4c1f856814d',
      amount: 200000,
      account_id: '84cc072e-90e8-33cf-9305-098095fed32f',
      currency: 'COP',
      external_id: 'subscription-456'
    })
  });

  const charge = await response.json();
  ```

  ```python Python - Cargo con tarjeta theme={null}
  import requests

  response = requests.post(
      'https://api.onepay.la/v1/charges',
      headers={
          'Authorization': 'Bearer sk_test_xxx',
          'Content-Type': 'application/json',
          'x-idempotency': 'charge-card-001'
      },
      json={
          'title': 'Mensualidad Enero 2025',
          'customer_id': '9dd4158b-0e45-42bc-b56f-a4c1f856814d',
          'amount': 85000,
          'card_id': '9e01eeae-2868-4564-9d04-84d1d1d027d2',
          'installments': 3,
          'currency': 'COP',
          'external_id': 'invoice-2025-001'
      }
  )

  charge = response.json()
  ```

  ```python Python - Cargo con cuenta bancaria theme={null}
  import requests

  response = requests.post(
      'https://api.onepay.la/v1/charges',
      headers={
          'Authorization': 'Bearer sk_test_xxx',
          'Content-Type': 'application/json',
          'x-idempotency': 'charge-account-001'
      },
      json={
          'title': 'Cuota mensual',
          'customer_id': '9dd4158b-0e45-42bc-b56f-a4c1f856814d',
          'amount': 200000,
          'account_id': '84cc072e-90e8-33cf-9305-098095fed32f',
          'currency': 'COP',
          'external_id': 'subscription-456'
      }
  )

  charge = response.json()
  ```

  ```php PHP - Cargo con tarjeta theme={null}
  <?php
  $curl = curl_init();

  curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.onepay.la/v1/charges",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
      "Authorization: Bearer sk_test_xxx",
      "Content-Type: application/json",
      "x-idempotency: charge-card-001"
    ],
    CURLOPT_POSTFIELDS => json_encode([
      "title" => "Mensualidad Enero 2025",
      "customer_id" => "9dd4158b-0e45-42bc-b56f-a4c1f856814d",
      "amount" => 85000,
      "card_id" => "9e01eeae-2868-4564-9d04-84d1d1d027d2",
      "installments" => 3,
      "currency" => "COP",
      "external_id" => "invoice-2025-001"
    ])
  ]);

  $response = curl_exec($curl);
  $charge = json_decode($response, true);
  ?>
  ```

  ```php PHP - Cargo con cuenta bancaria theme={null}
  <?php
  $curl = curl_init();

  curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.onepay.la/v1/charges",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
      "Authorization: Bearer sk_test_xxx",
      "Content-Type: application/json",
      "x-idempotency: charge-account-001"
    ],
    CURLOPT_POSTFIELDS => json_encode([
      "title" => "Cuota mensual",
      "customer_id" => "9dd4158b-0e45-42bc-b56f-a4c1f856814d",
      "amount" => 200000,
      "account_id" => "84cc072e-90e8-33cf-9305-098095fed32f",
      "currency" => "COP",
      "external_id" => "subscription-456"
    ])
  ]);

  $response = curl_exec($curl);
  $charge = json_decode($response, true);
  ?>
  ```
</RequestExample>

### Response

La respuesta describe el **cobro** (`payment`), que es el recurso que agrupa los intentos de pago.
Por eso `status` usa los estados de cobro (`approved`, `pending`, `declined`) y no los del intento
(`paid`, `failed`).

<ResponseField name="id" type="string">
  ID del cobro. Es el que se usa en [`GET /charges/{id}`](/client/charges/detail) y en
  [`DELETE /charges/{id}`](/client/charges/cancel).

  <Warning>
    Aunque el endpoint se llame `/charges`, **este `id` no es el que llega en los webhooks
    `charge.*`**. Para casar la respuesta con el webhook usa `charge_id`.
  </Warning>
</ResponseField>

<ResponseField name="charge_id" type="string | null">
  ID del intento de pago. Es el mismo `charge.id` que viaja en los webhooks `charge.*`, así que
  te permite correlacionar esta respuesta con el webhook posterior sin depender del `external_id`.

  Es `null` mientras no exista un intento asociado.
</ResponseField>

<ResponseField name="customer_id" type="string">
  ID del cliente al que se atribuye el cobro.

  <Note>
    En modo prueba este campo devuelve el cliente de pruebas de OnePay
    (`9940779b-533c-4406-b1f6-e0c92e8ab087`, "Joe Cruz Mesa"), no el `customer_id` que enviaste, y
    el objeto `customer` de esta misma respuesta también es el de pruebas. Es intencional y solo
    ocurre en sandbox: en producción se conserva el cliente real.

    Para saber a qué cliente se atribuye el cobro en pruebas, usa el objeto `customer` del webhook
    `charge.paid`, que sí trae siempre el que enviaste.
  </Note>
</ResponseField>

<ResponseField name="external_id" type="string">
  El identificador de tu sistema, tal como lo enviaste.
</ResponseField>

<Warning>
  **El webhook puede llegar antes que esta respuesta.** El cobro se procesa de forma síncrona, así
  que el webhook `charge.paid` suele emitirse unos segundos antes de que se cierre la petición HTTP.
  No construyas tu flujo asumiendo que la respuesta llega primero: trata el webhook como la fuente
  de verdad y usa `external_id` o `charge_id` para casarlos.
</Warning>

<ResponseExample>
  ```json 200 theme={null}
  {
    "id": "9e02966f-2ddf-4ee7-a391-5b5b7653e232",
    "title": "Test Charge",
    "amount": 1000,
    "amount_label": "$1.000",
    "source": "charge",
    "status": "declined",
    "currency": "COP",
    "payment_method_type": "Account",
    "payment_method_id": "4dc959d0-d96a-377a-823e-5e05cfde896f",
    "created_at": "2025-01-20T02:18:29.000000Z",
    "paid_at": null,
    "remarks": {
      "code": "INTERNAL_ERROR",
      "description": "Error interno. Comunícate con soporte para resolver el problema."
    },
    "is_test": true,
    "network_id": null,
    "external_id": "invoice-2025-001",
    "customer_id": "9940779b-533c-4406-b1f6-e0c92e8ab087",
    "charge_id": "9e02966f-60e2-71e4-bac1-b402b281e27e",
    "method": {
      "id": "4dc959d0-d96a-377a-823e-5e05cfde896f",
      "title": "Dr.",
      "subtype": "SAVINGS",
      "status": "PENDING",
      "authorization": true,
      "customer_id": "9940779b-533c-4406-b1f6-e0c92e8ab087",
      "re_enroll": false,
      "external_id": null,
      "bank_id": "9e02966e-8d60-49b0-ad66-372bf73c4a54",
      "bank": {
        "id": "9e02966e-8d60-49b0-ad66-372bf73c4a54",
        "name": "Hoeger PLC",
        "logo": null
      }
    },
    "customer": {
      "id": "9940779b-533c-4406-b1f6-e0c92e8ab087",
      "first_name": "Joe",
      "last_name": "Cruz Mesa",
      "email": "hola@onepay.ai",
      "phone": "+12518057105",
      "document_type": "CC",
      "document_number": "930232",
      "created_at": "2025-01-20T02:18:29.000000Z",
      "is_test": true
    },
    "splits": [
      {
        "id": "9e02966f-3204-4e50-9fb7-b8bc0214a93b",
        "customer_id": "9940779b-533c-4406-b1f6-e0c92e8ab087",
        "split_type": "percentage",
        "split_value": 300,
        "description": "Test Split",
        "created_at": "2025-01-20T02:18:29.000000Z",
        "scheduled_at": null,
        "account_id": "4dc959d0-d96a-377a-823e-5e05cfde896f"
      }
    ]
  }
  ```

  ```json 422 theme={null}
  {
     "message":"El campo cantidad es obligatorio. (y 2 errores más)",
     "code":10001,
     "code_name":"validation_error",
     "errors":{
        "amount":[
           "El campo cantidad es obligatorio."
        ],
        "title":[
           "El campo título es obligatorio."
        ],
        "customer_id":[
           "El campo customer id es obligatorio."
        ]
     }
  }
  ```

  ```json 409 theme={null}
  {
    "message": "No se puede generar la operación, genera un token de idempotencia y envíelo en los headers como x-idempotency",
    "code": 10003,
    "code_name": "idempotency_error"
  }
  ```
</ResponseExample>
