Cobros programados
Programar el cobro de una factura para una fecha futura, cancelarlo y adelantar un reintento, desde la API externa /external/v1.
Actualizado el . 5 min de lectura
Incluido en los planes Profesional y Avanzado. Ver precios
Un cobro programado es una orden para cargar una factura concreta en una fecha futura, con la tarjeta que el usuario ya autorizó. Es lo que sirve para «cóbrame el día 8», sin que haya una cuota recurrente detrás.
Los endpoints están bajo /external/v1, exigen la cabecera X-Api-Key y comparten un límite de 60 peticiones por minuto.
Programar solo deja la orden puesta: el cargo lo ejecuta UTILIA OS cuando llega la fecha, y todo lo que pasa después te llega por los avisos de cobro: CHARGE_STARTED, CHARGE_SUCCEEDED, CHARGE_FAILED, CHARGE_REQUIRES_ACTION, CHARGE_RETRY_SCHEDULED y CHARGE_RETRIES_EXHAUSTED.
Plan y permisos
| Concepto | Valor |
|---|---|
| Aplicativos del plan | Finanzas y Pagos con tarjeta (Stripe) |
| Capacidad del plan | Cobro automático (finance.direct_debit) |
Sin un aplicativo, la respuesta es 403 con errorCode: 'MODULE_NOT_ENABLED' y su moduleKey. Sin la capacidad, 403 con errorCode: 'CAPABILITY_NOT_ENABLED' y su capabilityKey.
| Endpoint | Permiso de la API (scope) |
|---|---|
GET /invoices/:id/scheduled-charges | billing:scheduled-charges:read |
POST /invoices/:id/scheduled-charges | billing:scheduled-charges:write |
DELETE /scheduled-charges/:id | billing:scheduled-charges:write |
POST /scheduled-charges/:id/retry-now | billing:scheduled-charges:write |
Programar un cobro
/api/external/v1/invoices/:id/scheduled-chargesPrograma el cargo de una factura emitida para una fecha futura, contra una autorización de cobro vigente del usuario.
Cuerpo
- userIdstringobligatorio
- Identificador en tu aplicación (externalId) del usuario al que pertenece la factura.
- scheduledAtstringobligatorio
- Momento de ejecución, en formato ISO 8601.
- paymentAuthorizationIdstringobligatorio
- Autorización de cobro que respalda el cargo. Tiene que ser de alcance SCHEDULED_INVOICE.
- paymentMethodIdstring
- Tarjeta concreta. Tiene que ser la que respalda la autorización.
- idempotencyKeystring
- Clave de idempotencia, válida 24 horas.
Hace falta una autorización de alcance SCHEDULED_INVOICE
Una autorización RECURRING cubre las cuotas de una suscripción, no un cobro puntual de una factura suelta. Si envías una, la respuesta es 409 PAYMENT_AUTHORIZATION_NOT_ACTIVE con un mensaje que nombra el alcance que hace falta. Una aplicación que cobre cuotas y programe cobros sueltos necesita dos autorizaciones del mismo usuario.
A diferencia del panel, la vía externa no admite firmar la autorización en el mismo cuerpo que programa el cobro: así la prueba legal solo viaja en llamadas de consentimiento y es fácil de auditar. Recógela antes con POST /payment-authorizations.
Respuesta (201)
{
"id": "cksch1234567890",
"invoiceId": "ckinv1234567890",
"invoiceNumber": "202600042",
"externalUserId": "user_01HXYZ",
"status": "PENDING",
"scheduledAt": "2026-10-08T08:00:00.000Z",
"amountCents": 5345,
"currency": "eur",
"paymentAuthorizationId": "ckauth1234567890",
"attemptCount": 0,
"maxAttempts": 3,
"nextRetryAt": null,
"failureCode": null,
"failureMessage": null,
"cancelReason": null,
"cancellableUntil": "2026-10-07T08:00:00.000Z",
"createdAt": "2026-10-01T09:14:00.000Z"
}- statusenumobligatorio
- PENDING, RUNNING, AUTH_REQUIRED, RETRY_SCHEDULED, COMPLETED, FAILED_PERMANENTLY o CANCELLED.
- cancellableUntilstringobligatorio
- Momento hasta el que se puede cancelar. Pasado ese punto, la orden entra en ejecución.
- failureCodestring
- Código del último rechazo. En los cobros programados viaja nulo: el fallo se guarda completo y se resume en texto en failureMessage.
- failureMessagestring
- Explicación legible del último rechazo.
Produce el aviso CHARGE_SCHEDULED.
Errores
| Situación | HTTP | errorCode |
|---|---|---|
| La factura es de otra aplicación o de otro usuario | 404 | SCHEDULED_CHARGE_NOT_ACCESSIBLE |
| La factura no tiene cliente asociado | 409 | SCHEDULED_CHARGE_NOT_ACCESSIBLE |
| La autorización indicada no es de ese usuario | 404 | PAYMENT_AUTHORIZATION_NOT_FOUND |
| La autorización no está vigente, o su alcance no sirve | 409 | PAYMENT_AUTHORIZATION_NOT_ACTIVE |
| La tarjeta indicada no es la que respalda la autorización | 404 | PAYMENT_METHOD_NOT_FOUND |
| El usuario indicado no existe en la aplicación | 404 | EXTERNAL_USER_NOT_FOUND |
Listar los cobros programados de una factura
/api/external/v1/invoices/:id/scheduled-chargesCobros programados de una factura, con sus intentos y el motivo del último rechazo.
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId).
- statusenum
- Filtra por estado.
- pageinteger
- Página, por defecto 1.
- limitinteger
- Elementos por página, por defecto 20.
La respuesta tiene la forma { data, total, page, limit, totalPages }.
Cancelar un cobro programado
/api/external/v1/scheduled-charges/:idCancela una orden que todavía no ha entrado en ejecución.
Los dos parámetros van en la cadena de consulta:
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId).
- cancelReasonstring
- Motivo de la cancelación, para la auditoría.
Devuelve el cobro programado ya cancelado y produce el aviso CHARGE_CANCELLED. Si la orden ya está en ejecución o ha terminado, responde 409 SCHEDULED_CHARGE_NOT_CANCELLABLE.
Adelantar un reintento
/api/external/v1/scheduled-charges/:id/retry-nowAdelanta el siguiente intento de un cobro que quedó a la espera de reintento.
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId).
Devuelve un acuse de recibo, no el resultado del cobro.
{
"accepted": true,
"scheduledChargeId": "cksch1234567890",
"status": "PENDING",
"scheduledAt": "2026-10-05T10:30:00.000Z"
}Produce el aviso CHARGE_RETRY_SCHEDULED. Solo se admiten cinco reintentos manuales por factura cada 24 horas; a partir del sexto, la respuesta es 409 (no 429) con errorCode: 'SCHEDULED_CHARGE_RETRY_TOO_SOON'. Los reintentos que UTILIA OS programa por su cuenta no cuentan para ese límite. Agotar el cupo no cancela el cobro: significa «hoy ya no», y el cobro programado se ejecutará igualmente en el siguiente reintento automático. Reintentar sin parar no lo acelera.
Estados, reintentos y agotamiento
Un cobro programado se ejecuta cuando llega su fecha y, según responda el banco, termina cobrado, queda a la espera de un reintento o se da por perdido. Solo se puede cancelar mientras está activo, reintentando o pendiente de autenticación.
Estados de un cobro programado
Activa
PENDING
Programado, esperando a su fecha.Se llega desde Activa cuando: La cancelas con DELETE
Cancelada
CANCELLED
Estado final
Aviso CHARGE_CANCELLED.
Se llega desde Activa cuando: Llega la fecha y UTILIA OS lanza el cargo
Ejecutando
RUNNING
El cargo está en curso.Se llega desde Ejecutando cuando: El banco pide autenticación reforzada
Requiere 3DS
AUTH_REQUIRED
Aviso CHARGE_REQUIRES_ACTION; el cargo no se ha producido.Se llega desde Ejecutando cuando: El banco rechaza el cargo y quedan intentos
Reintentando
RETRY_SCHEDULED
Aviso CHARGE_RETRY_SCHEDULED con los intentos que quedan.Se llega desde Reintentando cuando: Se agotan los intentos
Agotada
FAILED_PERMANENTLY
Estado final
Aviso CHARGE_RETRIES_EXHAUSTED.
Se llega desde Ejecutando cuando: El banco acepta el cargo
Exitosa
COMPLETED
Estado final
Aviso CHARGE_SUCCEEDED.
Otras transiciones
- Ejecutando pasa a Agotada: El banco rechaza el cargo de forma definitiva: tarjeta perdida, robada o fraude.
- Reintentando pasa a Ejecutando: Llega la fecha del reintento automático.
- Reintentando pasa a Activa: Adelantas el reintento con retry-now.
- Reintentando pasa a Cancelada: La cancelas con DELETE.
- Requiere 3DS pasa a Cancelada: La cancelas con DELETE.
UTILIA OS reintenta los cobros rechazados hasta agotar los intentos configurados. Cada reintento programado produce CHARGE_RETRY_SCHEDULED con cuántos quedan; cuando no queda ninguno, produce CHARGE_RETRIES_EXHAUSTED con el último código de rechazo y si la suscripción asociada ha quedado pausada.
CHARGE_REQUIRES_ACTION significa que el banco pide autenticación reforzada y el cargo no se ha producido. En los cobros programados, la dirección del enlace público no viaja en el aviso: llega publicLinkAvailable: true y tu aplicación pide la dirección con POST /external/v1/invoices/:id/public-link. De un enlace emitido solo se guarda su huella, así que no se puede reconstruir.
Con el SDK
import { UtiliaSDK } from '@utilia-os/sdk-js';
const sdk = new UtiliaSDK({
baseURL: 'https://os.utilia.ai/api',
apiKey: process.env.UTILIA_API_KEY!,
});
const cobro = await sdk.invoices.scheduledCharges.schedule('ckinv1234567890', {
userId: 'user_01HXYZ',
scheduledAt: '2026-10-08T08:00:00.000Z',
paymentAuthorizationId: 'ckauth1234567890',
idempotencyKey: 'cobro_ckinv1234567890_08oct',
});
await sdk.invoices.scheduledCharges.cancel(cobro.id, {
userId: 'user_01HXYZ',
cancelReason: 'El cliente pidió aplazarlo.',
});No hay operación para cambiar la fecha: mover una fecha que el usuario ya conoce es un encargo distinto, así que se cancela la orden y se crea otra.