Venflow API

Cobros por API

Cobros por API

En una suscripción normal, Venflow cobra sola: el plan define cada cuánto se cobra y el motor ejecuta el débito cuando toca. En el modo Cobro por API eso no ocurre. Venflow guarda la domiciliación que el cliente autorizó y no cobra nada hasta que tú lo pidas, con un monto y una fecha que tú decides.

Sirve para todo lo que no cabe en un calendario fijo: consumo variable, honorarios que se facturan cuando se aprueban, cuotas irregulares, o cualquier caso donde tu sistema ya sabe cuándo y cuánto hay que cobrar.

Suscripción normalCobro por API
Quién decide cuándo se cobraVenflow, según el planTu sistema, con una llamada
Quién decide cuántoEl precio del planTú, en cada cobro (o el precio del plan si lo omites)
Cobro al terminar el checkoutSíNo — la suscripción queda activa sin cobrar
Prueba gratis, cuotas, pago diferidoSíNo aplican
Reintentos automáticos si el banco rechazaSí (dunning)Configurable por cobro (retry_policy)

El flujo completo

1. Creas un link de checkout en modo "Cobro por API"   (dashboard o API)
2. El cliente lo abre y afilia su cuenta bancaria      (checkout)
   → la suscripción queda activa, sin cobro inicial
3. Tu sistema pide cada cobro                          (POST .../charges)
4. Venflow ejecuta el débito y te avisa por webhook

El modo se define en el link, no en el plan. Puedes crear el link de dos formas:

  • En el dashboard: al generar un link, único o universal, activa la opción "Cobro por API".
  • Por API: envía collection_mode: "external" al crear una sesión de checkout (POST /v1/checkout/sessions) o un payment link (POST /v1/payment-links). Si omites el campo, el link se crea en modo normal (engine).
curl -X POST "https://api.venflow.app/v1/checkout/sessions" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Content-Type: application/json" \
  -d '{ "plan": "<plan_id>", "collection_mode": "external" }'

La respuesta trae collection_mode y external_initial_charge, igual que al consultar o listar esos links.

Cobro por API tiene que estar habilitado para tu organización. Si no lo está, la opción no aparece en el dashboard y crear el link por API responde 400.

POST /v1/payment-links además forma parte del recurso de payment links, que se habilita por separado: si tu organización no lo tiene, ese endpoint responde 404 sea cual sea el collection_mode. POST /v1/checkout/sessions no tiene ese requisito.

Todas las suscripciones que nazcan de ese link quedan en modo externo. Un plan puede usarse en links normales y en links de Cobro por API al mismo tiempo: lo que manda es el link por el que entró el cliente.

2. El checkout afilia, pero no cobra

El cliente completa el checkout igual que siempre y autoriza la domiciliación de su cuenta. Al terminar:

  • La suscripción queda activa.
  • No se genera factura ni transacción inicial. No hay primer cobro.
  • La prueba gratis (trial), las cuotas y el pago diferido del plan se ignoran. Aunque el plan los tenga configurados, en este modo no aplican: son formas de decidir cuándo cobra Venflow, y aquí quien decide eres tú. Por API, enviar trial_days mayor que 0 o scheduled_start_date junto con collection_mode: "external" responde 400.
  • El método "Otros Bancos" (pago manual) queda deshabilitado en estos links. Sin una domiciliación guardada no hay nada que debitar después. Por API, enviar allow_manual_payments: true junto con collection_mode: "external" responde 400.

Los planes por consumo (metered), como plan principal o como item adicional, y los de pago único no son compatibles con este modo: los primeros no tienen un precio fijo del cual partir, y los segundos no crean una suscripción que cobrar después. Por API, cualquiera de los dos responde 400.

Variante: cobrar el primer pago en el checkout

Al activar "Cobro por API" en el link, puedes marcar también "Cobrar el primer pago en el checkout" (por API, external_initial_charge: true; solo vale junto con collection_mode: "external", o la creación responde 400). Con esta opción:

  • El cliente sí paga el primer ciclo al domiciliar su cuenta, con factura y transacción como en un checkout normal (IVA y tasa BCV del día incluidos).
  • A partir de ahí la suscripción se comporta igual que en el modo externo puro: todos los cobros siguientes los solicitas tú por el API. Venflow no programa ningún cobro posterior.
  • Si el primer cobro falla, la suscripción no se activa — el mismo comportamiento de un checkout normal con pago fallido.

Úsala cuando tu modelo cobra al momento de la contratación y tu sistema de facturación toma el control desde el segundo ciclo. Si prefieres que el checkout solo registre la domiciliación, deja la opción desactivada.

3. Tu sistema pide el cobro

curl -X POST "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Idempotency-Key: 3f2a9c10-8b44-4e2f-9c77-0f1d2e3a4b5c" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 47.50,
    "metadata": { "invoice_number": "F-2026-0154" }
  }'

Respuesta:

{
  "id": "8f14e45f-ceea-467a-9c2b-3a1b2c3d4e5f",
  "object": "charge",
  "created": 1757030400,
  "livemode": true,
  "subscription": "550e8400-e29b-41d4-a716-446655440001",
  "amount": 47.50,
  "currency": "USD",
  "charge_date": "2026-09-05",
  "status": "scheduled",
  "invoice": null,
  "payment": null,
  "failure_reason": null,
  "metadata": { "invoice_number": "F-2026-0154" }
}

La respuesta no dice que el cliente pagó. Dice que la solicitud quedó creada. Los bancos responden de forma asíncrona — a veces días después — así que el resultado real llega por webhook, y status lo refleja cuando consultas la solicitud.

Referencia campo por campo: POST /v1/subscriptions/{id}/charges.

Idempotency-Key: el seguro contra el doble cobro

El header Idempotency-Key es obligatorio. Sin él la llamada se rechaza con 400.

Existe por un motivo muy concreto: si tu petición sale, llega, crea el cobro, y la respuesta se pierde por un timeout de red, tu sistema no tiene forma de saber si el cobro se creó o no. Reintentar a ciegas sería debitar dos veces al cliente.

Con la clave, reintentar es seguro:

  • La primera llamada con una clave crea la solicitud.
  • Cualquier llamada posterior con la misma clave y los mismos parámetros devuelve esa misma solicitud, con el mismo id, y no crea un segundo cobro.
  • Una clave queda amarrada a la solicitud que creó. Repetirla pidiendo algo distinto se rechaza con 400: ver Misma clave, cobro distinto.
  • La clave es única por organización: si otra organización usa el mismo valor, no interfiere con la tuya.
  • Usa una clave distinta por cada cobro que quieras generar de verdad. Un UUID por cada factura de tu sistema funciona bien, y además te deja repetir la llamada sin miedo si tu proceso se cae a la mitad.

Reintento seguro

Estas dos llamadas son la misma llamada. La segunda no cobra nada nuevo: devuelve la solicitud que creó la primera.

KEY="3f2a9c10-8b44-4e2f-9c77-0f1d2e3a4b5c"

# Primer intento — se pierde la respuesta por timeout
curl -X POST "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Idempotency-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 47.50 }'

# Reintento — misma clave, mismo resultado, un solo débito
curl -X POST "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Idempotency-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 47.50 }'

Genera la clave antes del primer intento y guárdala junto a la operación de tu sistema que origina el cobro. Si la generas dentro del bloque que reintenta, cada intento traerá una clave nueva y el seguro no sirve de nada.

Dos detalles del comportamiento:

  • La repetición devuelve la solicitud original tal cual, aunque la suscripción se haya cancelado entre medio. Un timeout no debe convertir un cobro exitoso en un error que no puedes conciliar.
  • Reusar una clave contra otra suscripción devuelve 400. Eso es un error en cómo generas las claves, y responderte con el cobro de otra suscripción lo escondería.

Misma clave, cobro distinto

Si repites una clave cambiando algún parámetro, la llamada se rechaza con 400 y el mensaje dice cuál campo no coincide. No te devolvemos el cobro original: pediste $20 y responderte con un cobro de $10 y un 200 te dejaría creyendo que quedó registrado lo que pediste, y solo lo descubrirías al conciliar.

Se comparan cinco campos: amount, currency, retry_policy, charge_date y metadata.

KEY="3f2a9c10-8b44-4e2f-9c77-0f1d2e3a4b5c"

# Ya existe un cobro con esta clave por 47.50
curl -X POST "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Idempotency-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 20.00 }'

# → 400: la clave ya se usó para un cobro con un 'amount' distinto

La regla es la misma para los cinco campos:

  • Campo que omites: coincide con lo que se guardó, sea lo que sea. Si no mandas amount, no estás pidiendo un monto distinto: estás repitiendo la llamada. Por eso una llamada que solo trae la clave siempre devuelve el cobro original, aunque este se haya creado ayer y su charge_date ya no sea hoy.
  • Campo que envías: tiene que ser igual al que se guardó. Da lo mismo cómo lo escribas (47.5 y 47.50 son el mismo monto), y también da lo mismo que el valor haya sido el predeterminado en la primera llamada: si mandas currency: "USD" sobre un cobro que quedó en USD, coincide.

El mensaje del error nombra el campo, pero nunca dice cuánto era el monto ni qué fecha tenía el cobro que ya existía. Si necesitas verlo, consúltalo con GET /v1/charges/{id} usando el id que te devolvió la primera llamada, o lístalo con los cobros de la suscripción.

Montos: en dólares o en bolívares

amount va en unidades enteras de la moneda, no en centavos: 47.50 son cuarenta y siete con cincuenta. Qué moneda es, lo decide currency.

currencyQué significa amountQué se debita
USD (predeterminado)Un monto neto en dólares, con la misma semántica que el price de un planEl IVA se calcula encima y el total se convierte a bolívares a la tasa BCV del día del cobro
VESLos bolívares exactos que quieres debitar, impuestos incluidosExactamente ese número, al céntimo

Si no envías currency, se asume USD: una integración escrita antes de que existiera este campo se comporta igual que siempre.

currency: "USD" — neto, antes de IVA

Es un monto neto. Significa exactamente lo mismo que el precio del plan y ocupa su lugar en ese cobro: el IVA y las banderas de impuesto del plan se calculan encima, igual que en cualquier ciclo normal. El total de la factura puede ser mayor que el número que enviaste, y los bolívares que se debitan solo se conocen el día del cobro, cuando se aplica la tasa BCV de esa fecha.

  • Si omites amount, se cobra el precio actual del plan principal de la suscripción.

currency: "VES" — el débito exacto en bolívares

Úsalo cuando tu sistema ya calculó lo que hay que cobrar en bolívares y ese número no puede moverse: el cliente ve 3.375,00 Bs y eso es lo que tiene que salir de su cuenta.

  • amount es el total que se debita, con IVA incluido. No se le suma nada encima.
  • amount es obligatorio: el plan tiene precio en dólares, no en bolívares, así que no hay un valor por defecto que tomar. Sin él la llamada se rechaza con 400.
  • El equivalente en dólares que queda registrado en la factura se deriva dividiendo entre la tasa BCV del día del cobro.

La tasa que se aplica es la del día en que se ejecuta el cobro, no la del día en que creaste la solicitud. Para una solicitud programada a futuro en USD, los bolívares se calculan ese día. Si necesitas que el número en bolívares sea exactamente el que ya calculaste, usa currency: "VES".

Reglas comunes a las dos monedas

  • Debe ser mayor que 0 y tener como máximo 2 decimales. Un 47.999 se rechaza con 400 en vez de redondearse: cobrar 48,00 cuando pediste 47,999 sería dinero que nadie autorizó.
  • El máximo es 99999999.99, que es lo que admite la columna. No hay tope de negocio relativo al precio del plan: en este modo tú eres dueño de tu calendario y de tus montos.
  • El descuento de la suscripción y los items adicionales no se aplican a un cobro por API: tú nombraste una cifra, y esa es la que se cobra.

Ojo con las unidades entre endpoints. Los cobros usan unidades enteras (dólares o bolívares, según currency). En cambio los endpoints de facturas y pagos reportan sus montos en centavos: esos mismos 47,50 dólares aparecen como 4750 en amount_due, amount_total o amount. Si comparas el monto que pediste con el de la factura que generó, convierte antes — y ten en cuenta que la factura está expresada en bolívares.

Ejemplo de un cobro por un monto exacto en bolívares:

curl -X POST "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Idempotency-Key: 7c1e0a55-2b1e-4a77-9c0f-1d2e3a4b5c6d" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 3375.00,
    "currency": "VES",
    "metadata": { "invoice_number": "F-2026-0155" }
  }'

Cuándo se ejecuta el cobro

El campo charge_date es un día de calendario en formato YYYY-MM-DD, en hora de Venezuela (America/Caracas).

Qué envíasCuándo se ejecuta
Nada (omites charge_date)En la próxima corrida del motor, hoy mismo
Una fecha futura ("2026-10-01")Ese día, no antes. Mientras tanto queda scheduled
Una fecha pasada400. Para cobrar de una vez, omite el campo

El motor revisa las solicitudes vencidas cuatro veces al día (10:00, 14:00, 18:00 y 22:00, hora de Venezuela). Un cobro sin fecha sale en la corrida siguiente, no en el mismo segundo de la llamada.

Si la suscripción ya tiene un cobro en camino al banco, el nuevo se pospone a la corrida siguiente en vez de fallar. Es la misma protección anti-doble-cobro que aplica a las suscripciones normales: mientras un débito puede todavía liquidarse, un segundo débito sería un duplicado. La solicitud sigue scheduled y se ejecuta en cuanto se despeje el camino.

Cobros en revisión con el banco

A veces el banco responde a un débito sin confirmar si el dinero se movió o no (por ejemplo, un tiempo de espera agotado). En ese caso Venflow no decide por su cuenta: el cobro queda en revisión hasta que alguien confirma con el banco qué pasó, ya sea el equipo de Venflow o tu equipo con las acciones de factura. La factura del cobro (el campo invoice de la solicitud) se ve con status: "open" y under_review: true.

Mientras dura la revisión:

  • la solicitud de ese cobro sigue en processing y no recibes ningún webhook todavía;
  • todos los cobros nuevos de esa suscripción esperan, aunque ya les haya llegado su charge_date. Siguen scheduled y no hay un límite de tiempo: esperan lo que tarde la revisión, porque cobrar antes de saberlo podría cobrarle dos veces al cliente.

Cómo termina depende de cómo se resuelva la factura:

ResoluciónLa solicitud en revisiónLos cobros que esperaban
mark_paidcompleted, con su webhookSalen
mark_uncollectiblefailed, con su webhookSalen*
mark_openSigue el ciclo de reintentos de su retry_policy. Con none, queda processing y su seguimiento pasa a tu equipoSalen*. Si el cobro reabierto vuelve a quedar en revisión, vuelven a esperar
mark_void (con confirm_under_review: true)Sigue processing, sin webhookSalen*

* Si la revisión se resuelve durante los dos primeros días del cobro, los que esperaban salen cuando se cumplen esos dos días.

Cualquiera de estas resoluciones libera los cobros que esperaban. mark_void exige confirm_under_review: true; mark_open y mark_uncollectible no lo exigen, así que verifica con el banco el estado del cobro antes de usarlos. Si ese cobro sí se procesó, el siguiente sería un segundo débito.

Los cobros que esperaban salen de uno en uno, el más antiguo primero: cada uno sale en una corrida y el siguiente espera a que el banco responda al anterior.

No crees otro cobro para "reintentar" uno que está en processing. No acelera nada: el nuevo espera detrás del primero. Y si el primero termina cobrándose, el nuevo saldría después como un segundo cobro real. Espera el webhook del resultado y, si fue un fallo, crea entonces la solicitud nueva.

Reintentos si el banco rechaza

Por defecto, un cobro por API es un solo intento: si el banco lo rechaza, la solicitud queda failed y reintentar es tu decisión. Pero puedes pedir, por cobro, que Venflow reintente por ti usando la misma cola de reintentos (dunning) de las suscripciones normales. Lo controla el campo retry_policy.

retry_policySi el banco rechaza
none (predeterminado)Un intento. La solicitud queda failed, recibes el webhook de fallo, y reintentar es tu decisión: creas una solicitud nueva cuando quieras. Venflow no reintenta por su cuenta
automaticLa factura del cobro entra a la cola de reintentos de Venflow, con la misma cantidad de reintentos y los mismos días de separación que tu organización ya tiene configurados. La solicitud permanece processing mientras quedan reintentos

Con automatic, la solicitud se resuelve una sola vez, al final:

  • si algún reintento se cobra, pasa a completed y recibes el webhook de éxito;
  • si se agotan los reintentos configurados, pasa a failed y recibes el webhook de fallo.

Nunca recibes un webhook por cada reintento intermedio: solo el resultado final. Mientras tanto, status es processing cuando consultas la solicitud.

automatic reintenta con los mismos intentos y días de tu organización, pero nunca cancela ni cambia el estado de la suscripción. Cuando se agotan los reintentos, lo único que ocurre es que el cobro queda failed y recibes el webhook de fallo; la suscripción sigue activa. Cancelar o cambiar el estado de una suscripción es una acción aparte que decides tú, nunca un efecto de un cobro que falló.

curl -X POST "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..." \
  -H "Idempotency-Key: 9d3b1f22-7a4e-4c81-b0d2-5e6f7a8b9c0d" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 47.50,
    "retry_policy": "automatic"
  }'

Si no envías retry_policy, se asume none: una integración escrita antes de que existiera este campo se comporta igual que siempre, un solo intento.

Los reintentos de automatic usan la configuración de reintentos de tu organización (cantidad y días), la misma que aplica a tus suscripciones normales. No es un valor que se defina en la llamada. Si necesitas otra cadencia, usa none y controla los reintentos tú, con solicitudes nuevas.

Ciclo de estados

                    ┌───────────► canceled   (la cancelaste tú, o la
                    │                         suscripción dejó de estar activa)
  scheduled ─────► processing ─────► completed  (el banco confirmó el débito)
                          │
                          └────────► failed     (el banco rechazó)
EstadoQué significa¿Se puede cancelar?
scheduledCreada, esperando su charge_dateSí
processingEl motor la tomó y el débito va camino al bancoNo
completedEl banco confirmó el débito. Puede llegar días después, por conciliaciónNo, es final
failedEl banco rechazó el débito (o, con retry_policy: automatic, se agotaron los reintentos). failure_reason dice por quéNo, es final
canceledCancelada antes de ejecutarse. failure_reason dice por quéNo, es final

Qué pasa cuando el banco rechaza

Lo decides tú, por cobro, con retry_policy: con none (predeterminado) la solicitud queda failed de una vez y reintentar es tu decisión; con automatic la factura del cobro entra a la cola de reintentos de Venflow y la solicitud sigue processing hasta resolverse.

Consultar y cancelar

# Los cobros de una suscripción — más recientes primero, incluye fallidos y cancelados
curl "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges?limit=20" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..."

# Página siguiente: starting_after = id del último elemento que recibiste
curl "https://api.venflow.app/v1/subscriptions/<subscription_id>/charges?limit=20&starting_after=<ultimo_id>" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..."

# Una solicitud puntual, con su estado actual
curl "https://api.venflow.app/v1/charges/<charge_id>" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..."

# Cancelar una solicitud que todavía no se ejecutó
curl -X DELETE "https://api.venflow.app/v1/charges/<charge_id>" \
  -H "Api-Key: pvk_live_abc123..." \
  -H "Organization: org_xyz789..."

La lista sigue la convención del resto de la API: object: "list", data y has_more.

Cuando el cobro ya se ejecutó, invoice y payment traen los IDs de la factura y del pago que generó — los mismos que ves en /v1/invoices y /v1/payments, y en el dashboard. Mientras la solicitud no se haya ejecutado, los dos vienen en null.

El DELETE solo funciona mientras la solicitud está scheduled. Sobre una en processing, completed, failed o canceled responde 400 diciendo en qué estado está: una vez que el débito salió, ya no hay nada que detener.

Cancelar por tu cuenta con DELETE no emite webhook — ya sabes el resultado, es una respuesta síncrona. Los webhooks son para lo que tú no puedes ver venir.

El resultado llega por webhook

Los tres eventos viajan por el mismo sistema de webhooks que el resto: hasta 3 intentos, separados 5 minutos, firmados con el header X-Webhook-Secret. Si tu endpoint no filtra eventos, los recibes sin configurar nada.

EventoCuándo
EXTERNAL_CHARGE_SUCCESS_EVENTEl banco confirmó el débito
EXTERNAL_CHARGE_FAILED_EVENTEl banco lo rechazó, o el cobro no pudo ejecutarse
EXTERNAL_CHARGE_CANCELLED_EVENTEl motor descartó la solicitud (la suscripción dejó de estar activa)
EXTERNAL_CHARGE_SUCCESS_EVENT
{
  "eventType": "EXTERNAL_CHARGE_SUCCESS_EVENT",
  "charge": {
    "id": "8f14e45f-ceea-467a-9c2b-3a1b2c3d4e5f",
    "status": "completed",
    "amount": "47.50",
    "chargeDate": "2026-09-05",
    "subscriptionId": "550e8400-e29b-41d4-a716-446655440001",
    "invoiceId": "660e8400-e29b-41d4-a716-446655440002",
    "transactionId": "770e8400-e29b-41d4-a716-446655440003",
    "failureReason": null,
    "metadata": { "invoice_number": "F-2026-0154" }
  }
}

Los campos están documentados uno por uno en la referencia de eventos, junto con los payloads de fallo y de cancelación.

Dos cosas que conviene tener presentes al integrar:

  • El id del evento es el mismo id que te devolvió la creación del cobro. Es la llave para conciliar contra tu sistema, junto con la metadata que tú enviaste y que vuelve tal cual.
  • El amount del webhook llega como texto ("47.50") y en dólares — el monto que pediste. Es distinto de los eventos PAYMENT_*, cuyo amount va en bolívares.

Errores al crear un cobro

SituaciónCódigoQué dice
Falta el header Idempotency-Key, o llega vacío400El header es obligatorio y por qué existe
La misma clave usada para otra suscripción400Usa una clave nueva por cada cobro
La misma clave con un parámetro distinto (amount, currency, retry_policy, charge_date o metadata)400Cuál de los cinco campos no coincide — sin revelar el valor guardado
amount menor o igual a 0, con más de 2 decimales, o mayor que 99999999.99400Cuál es el límite roto
charge_date mal formado, inexistente o anterior a hoy400El formato correcto y cuál es "hoy" en Caracas
El plan es por consumo y no enviaste amount400No hay monto por defecto: envía amount
currency: "VES" sin amount400El plan tiene precio en dólares: en bolívares hay que decir el monto
currency distinto de USD o VES400Cuáles son los valores aceptados
retry_policy distinto de none o automatic400Cuáles son los valores aceptados
La suscripción no está en modo Cobro por API400El motor es dueño de sus cobros
La suscripción no está active400En qué estado está
La suscripción no existe, o es de otra organización404Subscription not found — la API nunca confirma que un ID exista en otra organización

Permisos de la clave API

Los cuatro endpoints requieren que tu clave tenga los permisos del recurso de cobros externos: crear para el POST, leer para los dos GET, y eliminar para el DELETE. Una clave sin ellos recibe el error de permisos habitual, igual que en el resto de la API.