Acepta Cleo en tu checkout

Deja que tus clientes compren ahora y paguen después. Cleo se encarga de la identidad, la evaluación de crédito, la conexión bancaria y el plan de cuotas — tú solo haces dos llamadas a la API.

Compra ahora, paga después Chile · CLP Checkout alojado — sin alcance PCI

Inicio rápido

Toda la integración es un redirect y una confirmación. Cleo aloja el checkout, así que la identidad del comprador, sus credenciales bancarias y la lógica de crédito nunca tocan tus servidores.

1

Crea una sesión

Servidor a servidor con tu clave secreta: envías el amount del pedido y tus URLs de retorno. Cleo devuelve un session_id y un checkout_url.

2

Redirige al comprador

Envía el navegador al checkout_url que te devolvimos. No lo construyas a mano.

3

El comprador completa Cleo

RUT → OTP por WhatsApp → conexión bancaria → decisión de crédito → plan de cuotas → mandato de descuento automático. Todo dentro de Cleo.

4

Confirma el resultado

Recibes un callback servidor a servidor, y siempre puedes consultar el estado de la sesión. Confirma antes de despachar.

Los montos son enteros en CLP — sin decimales. 149990 significa $149.990 CLP.

Autenticación y entornos

Cada llamada lleva tu clave secreta como bearer token. Tu merchant_id se deriva de la clave — no lo envías en el body y no puedes actuar en nombre de otro comercio.

Authorization: Bearer sk_live_<tu-clave>
Content-Type: application/json

Entornos

EntornoURL baseClave
Producciónhttps://api-bnpl.cleo.clsk_live_…
Sandbox — integra aquí primerohttps://sandbox-api-bnpl.cleo.clsk_test_…
Staging / Dev (uso interno de Cleo)staging-api-bnpl.cleo.cl · dev-api-bnpl.cleo.clsk_test_…

Cada entorno tiene su propia base de datos: nada de lo que hagas en sandbox toca producción. La API rechaza una clave del entorno equivocado en vez de hacer algo inesperado con ella:

{"error":"wrong_environment","detail":"This host serves test keys; that key is live. Use the production base URL."}

Lo único que cambia entre sandbox y producción es la URL base y la clave.

Un solo host, dos roles. El mismo host sirve la API (tu servidor, con la clave secreta) y el checkout alojado (el navegador del comprador, en el checkout_url). Tu clave secreta nunca debe llegar al navegador.

La clave se emite cuando Cleo aprueba tu solicitud de comercio y se muestra una sola vez. La guardamos con hash: no podemos recuperarla, solo reemplazarla. Se verifica en cada llamada, así que una clave revocada deja de funcionar en la petición siguiente.

RespuestaSignificado
401 merchant_key_requiredFalta el header Authorization.
401 invalid_or_revoked_keyClave incorrecta o revocada.
401 wrong_environmentClave de test contra producción, o al revés.
409 merchant_not_linkedLa clave es válida pero la cuenta no está terminada — contacta a Cleo.
429 rate_limitedMás de 120 peticiones por minuto.

Crear una sesión

POST/v1/sessions

Body

CampoRequeridoNotas
amountEntero en CLP, entre 1.000 y 10.000.000. Es lo que se le cobra al comprador; él no puede modificarlo.
currencySolo CLP (valor por defecto).
merchant_order_idrecomendadoTu referencia de pedido. Activa las protecciones de idempotencia y doble pago (más abajo) y viaja de vuelta en el callback.
success_url / failure_urlrecomendadoA dónde vuelve el navegador del comprador al terminar.
callback_url / callback_failure_urlA dónde publicamos el resultado servidor a servidor. Si no las envías, usamos las configuradas por defecto en tu cuenta.
callback_method / callback_failure_methodPOST (por defecto) o GET.
merchant_session_id · merchant_customer_idTus propias referencias; se devuelven tal cual en el callback.
expires_in_minutesEntre 5 y 1440. Por defecto 60.
sign_callbackSi es true, los callbacks salientes de esta sesión llevan un header X-Cleo-Signature: sha256=… — ver Verificando la firma del callback.
payment_methodFija el plan de cuotas y salta la selección. Uno de PAY_IN_14_DAYS, PAY_IN_30_DAYS, PAY_IN_3_PARTS, PAY_IN_6_PARTS, PAY_IN_9_PARTS, PAY_IN_12_PARTS, ETPAY.

Todas las URLs deben ser https públicas. Rechazamos localhost, rangos privados, direcciones link-local y URLs con credenciales.

curl -X POST https://api-bnpl.cleo.cl/v1/sessions \
  -H "Authorization: Bearer $CLEO_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "amount": 149990,
        "currency": "CLP",
        "merchant_order_id": "ORD-10432",
        "success_url": "https://tienda.example.com/checkout/gracias?order=10432",
        "failure_url": "https://tienda.example.com/checkout/error?order=10432",
        "callback_url": "https://tienda.example.com/api/cleo/webhook"
      }'

Respuesta · 201

{
  "session_id":   "b26d5129-146e-459b-9411-3a3800874305",
  "checkout_url": "https://api-bnpl.cleo.cl/checkout/start/b26d5129-146e-459b-9411-3a3800874305",
  "expires_at":   "2026-07-23T15:26:06.900Z",
  "amount":       149990,
  "currency":     "CLP",
  "status":       "RUT",
  "reused":       false
}
Usa siempre el checkout_url tal como viene en la respuesta. En sandbox apunta al host de sandbox; construirlo a mano es la forma más rápida de mandar al comprador al entorno equivocado.

Reintentos y doble pago

Si vuelves a crear una sesión con el mismo merchant_order_id:

  • mientras la sesión siga abierta, devolvemos la existente (200, "reused": true) — un reintento nunca crea un segundo checkout;
  • una vez que ese pedido está pagado, devolvemos 409 order_already_paid, así una pestaña vieja o una petición duplicada no puede cobrarle dos veces al comprador.

Los comercios que legítimamente reutilizan referencias pueden marcarse para permitirlo.

Redirigir al comprador

Envía el navegador al checkout_url que recibiste:

GEThttps://api-bnpl.cleo.cl/checkout/start/{session_id}

Esa página muestra el nombre de tu tienda, la referencia del pedido y el monto, y luego ejecuta el flujo de Cleo: verificación del RUT chileno, OTP por WhatsApp, conexión bancaria de solo lectura, decisión de crédito, elección del plan de cuotas y firma del mandato de descuento automático. El monto lo fija el comercio y el comprador no puede modificarlo.

Al terminar, el comprador ve un botón «Volver a {tu tienda}» que apunta a tu success_url.

El redirect no es una confirmación de pago. Es navegación, y el comprador puede cerrar la pestaña antes. Despacha con el callback o el estado de la sesión, nunca con el redirect por sí solo.

Callback (resultado)

Cuando el checkout termina, publicamos el resultado en tu callback_url:

{
  "createdAt": "2026-07-23T15:40:11.000Z",
  "callbackId": 8123,
  "version": "v1",
  "event": "CHECKOUT_SUCCEEDED",
  "payload": {
    "sessionId":          "b26d5129-…",
    "merchantId":         "tu-login",
    "merchantSessionId":  null,
    "merchantCustomerId": null,
    "merchantOrderId":    "ORD-10432",
    "status":             "OK",
    "currency":           "CLP",
    "amount":             149990,
    "invoiceNumber":      "CLO-4821-1",
    "customer": {
      "ssn":   "12345678-9",
      "email": "…",
      "phone": "…",
      "name":    { "first": "…", "last": "…", "org": null },
      "address": { "street": "…", "region": "…", "country": "CL" }
    }
  }
}

event es CHECKOUT_SUCCEEDED (status: "OK") o CHECKOUT_FAILED (status: "ERROR", por ejemplo crédito rechazado). Con callback_method: "GET" el mismo JSON llega en base64 en el parámetro de query SWEETPAY_DATA.

Entrega at-least-once

Intentamos dos veces de inmediato y después reintentamos con backoff — 1 min, 5 min, 30 min, 2 h, 6 h, es decir 6 intentos en unas 8,5 horas — hasta que tu endpoint responda 2xx. Cualquier 2xx cuenta como entregado; un timeout de 45 s cuenta como fallo.

Como el reintento reenvía el mismo payload byte a byte, el callbackId es estable entre intentos:

  • Haz tu handler idempotente — deduplica por callbackId. Si procesaste la petición pero el 2xx no nos llegó, te vamos a llamar otra vez.
  • Responde rápido (acusa recibo y luego procesa). Un endpoint lento lo tratamos como fallo.

Si todos los intentos fallan dejamos de insistir y lo registramos; el pedido sigue pagado y el endpoint de estado sigue siendo la fuente de verdad.

El firmado es parcial hoy. Activa sign_callback: true al crear la sesión y los reintentos y el callback de expiración llevan el header X-Cleo-Signature: sha256=… (ver abajo). El primer intento — el que se envía apenas termina el checkout — todavía no firma, porque sale de un servicio distinto. Hasta que eso se cierre, confirma cada callback contra el endpoint de estado con tu clave secreta antes de despachar, esté firmado o no — esa llamada sí está autenticada y es autoritativa, y además cubre el caso de un callback que nunca llega.

Verificando la firma del callback

Cuando X-Cleo-Signature está presente, es sha256=<hex> donde <hex> es HMAC-SHA256(cuerpo crudo, tu webhook_signing_secret) — el mismo whsec_… que te mostramos cuando se emitió tu clave (pídelo de nuevo a Cleo si lo perdiste). Recalcúlalo sobre los bytes crudos exactos que recibiste (antes de cualquier re-serialización JSON, porque el orden de las llaves o los espacios cambian el hash) y compáralo con una comparación de tiempo constante:

const crypto = require("node:crypto");
function verify(rawBody, header, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return header?.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

Consultar el estado

GET/v1/sessions/{session_id}
curl https://api-bnpl.cleo.cl/v1/sessions/b26d5129-146e-459b-9411-3a3800874305 \
  -H "Authorization: Bearer $CLEO_SECRET_KEY"

Respuesta · 200

{
  "session_id":        "b26d5129-…",
  "status":            "CONFIRMED",
  "paid":              true,
  "expired":           false,
  "expires_at":        "2026-07-23T15:26:06.900Z",
  "amount":            149990,
  "currency":          "CLP",
  "merchant_order_id": "ORD-10432",
  "cancelled":         false,
  "callback": { "sent_at": "2026-07-23T15:40:11.000Z", "delivered": true },
  "installments": [
    { "invoice_number": "CLO-4821-1", "amount": 52497, "due_at": "…", "number": 1, "payment_status": "PENDING" }
  ]
}

paid es el campo del que te tienes que fiar. Una sesión que no creaste tú devuelve 404 — solo puedes leer las tuyas.

Cancelar una orden

Próximamente — disponible a partir del 1 de septiembre de 2026. Esta sección describe el contrato para que puedas preparar tu integración con anticipación; los endpoints todavía no responden en producción ni en sandbox.
POST/v1/sessions/{session_id}/cancel
POST/v1/sessions/{session_id}/cancel-partial
// cancel-partial
{ "amount": 15000 }

Cancela (total o por un amount dado) la orden detrás de esa sesión y crea un reembolso por lo ya pagado sobre el nuevo total — mismo motor y reglas que cuando Cleo la cancela por ti: una orden ya cancelada devuelve 409 already_cancelled, una con una cuota en proceso de pago devuelve 409 invoices_in_payment_process, y un monto de cancel-partial que no sea estrictamente menor al remanente cancelable de la orden devuelve 400 amount_too_high (o 400 bad_amount si no es un número positivo). Una sesión que no creaste tú devuelve 404 session_not_found — solo puedes cancelar las tuyas, igual que al leer el estado.

Respuesta · 200

{ "ok": true, "cancelledInvoiceNumbers": ["CLO-4821-1"], "refundCreated": true, "amountRefunded": 31497 }

También vas a recibir un callback ORDER_CANCELLED (ver Callback) en tu callback_url — discrimínalo por event, ya que es la misma URL que recibe CHECKOUT_SUCCEEDED/CHECKOUT_FAILED.

Estados de la sesión

El campo status recorre el viaje del comprador. Para decidir qué hacer con el pedido te basta paid; los estados intermedios sirven para diagnosticar.

EstadoSignificado¿Terminal?
RUT · PHONE · PIN · CUSTOMER_DETAILS · EMAIL_PINIdentidad y contacto — el comprador va avanzando.no
BANK_ONBOARDING · BANK_ONBOARDING_PENDING · CHECKING_CUSTOMER_DATAConexión bancaria y evaluación de crédito en curso.no
PAYMENT_METHOD · SUBSCRIPTION_INTENT_INITEligiendo el plan de cuotas y firmando el mandato.no
CONFIRMEDAprobado y mandato firmado. paid: truedespacha el pedido.
DENIEDLa evaluación de crédito no aprobó.
ERROR · FAILEDEl flujo no pudo completarse.

Una sesión que pasa su expires_at sin confirmarse se devuelve con "expired": true. El comprador puede volver a empezar creando una sesión nueva.

Errores

CódigoErrorQué hacer
400invalid_amountEntero en CLP dentro del rango permitido.
400unsupported_currencySolo CLP.
400*_must_be_https · *_must_be_public · *_invalidUsa una URL https pública.
401merchant_key_required · invalid_or_revoked_key · wrong_environmentRevisa la clave y la URL base del entorno.
403merchant_inactiveCuenta deshabilitada — contacta a Cleo.
404session_not_foundEse session_id no existe o no es tuyo.
409order_already_paidEse merchant_order_id ya está pagado.
409merchant_not_linked · merchant_not_foundConfiguración de la cuenta incompleta — contacta a Cleo.
400invalid_payment_methodUno de los valores listados en Crear una sesión.
400bad_amountamount de cancel-partial debe ser un número positivo.
400amount_too_highamount de cancel-partial debe ser estrictamente menor al remanente cancelable de la orden.
409already_cancelledEsa orden ya fue cancelada.
409invoices_in_payment_processUna cuota está en proceso de pago; reintenta cuando se resuelva.
503feature_disabledLa cancelación todavía no está disponible — ver Cancelar una orden.
429rate_limitedBaja el ritmo; el límite es 120 req/min.
503temporarily_unavailableReintenta con backoff.

Probar en sandbox

Integra primero contra https://sandbox-api-bnpl.cleo.cl con una clave sk_test_…. Estos valores completan una compra de punta a punta sin un WhatsApp, un banco ni una consulta de crédito reales:

CampoValor de prueba
RUT11.111.111-1 — RUT de simulación; devuelve una persona y un perfil bancario ficticios
Teléfono+56911111111
PIN de WhatsApp / Email1111
El perfil simulado declara una renta de $500.000 CLP mensuales. Mantén el monto de tus pedidos de prueba bien por debajo del límite aprobado si quieres ver el camino CONFIRMED, y súbelo para ver DENIED.
La evaluación rechaza con EVALUATED_AMOUNT_TOO_HIGH cuando el monto del pedido más la deuda pendiente del comprador supera $1.000.000 CLP. Ojo: la API acepta al crear la sesión montos de hasta $10.000.000, pero el checkout aplica ese tope al evaluar — un pedido por encima del límite se crea bien y termina en DENIED.

Salir a producción

Cuando tu integración en sandbox esté lista:

1

Pide tus credenciales productivas

Cleo aprueba tu comercio y emite una clave sk_live_…. Se muestra una sola vez — guárdala en tu gestor de secretos, nunca en el repositorio.

2

Cambia la URL base y la clave

sandbox-api-bnpl.cleo.clapi-bnpl.cleo.cl. No hay ningún otro cambio de código.

3

Registra tus URLs productivas

Tu callback_url, success_url y failure_url de producción deben ser https públicas y estar accesibles desde internet.

4

Verifica antes de despachar

Confirma cada callback contra GET /v1/sessions/{id} con tu clave secreta.