Suscripciones
Monta cuotas que UTILIA OS factura, envía y, si quieres, cobra solas cada periodo; consulta el próximo cobro y pausa, reanuda, da de baja o reactiva.
Actualizado el . 5 min de lectura
Incluido en los planes Profesional y Avanzado. Ver precios
Una suscripción es una cuota que se repite: con su importe, su periodicidad y sus condiciones, UTILIA OS emite cada periodo la factura con su numeración y su fiscalidad, la envía por correo si lo pides y, con cobro automático, la cobra contra la tarjeta que el usuario autorizó. En tu organización aparecen en Finanzas › Cobros recurrentes, necesitas entrar con tu cuenta, junto a las que crea el propio equipo. Funciona con la clave de tu aplicación externa, siempre desde tu servidor.
Antes de empezar
- El plan necesita el aplicativo Finanzas con la capacidad de cobros recurrentes. Si falta, la respuesta es un 403
MODULE_NOT_ENABLEDoCAPABILITY_NOT_ENABLED(errores de plan). - Scopes:
billing:subscriptions:readpara consultar,billing:subscriptions:writepara el alta y los cambios, ybilling:subscriptions:cancelpara dar de baja (se concede aparte porque corta un ingreso). - Para el cobro automático hace falta además una autorización de cobro vigente del usuario, de alcance
RECURRING.
| JavaScript | Python | Para qué |
|---|---|---|
create(datos) | create(datos) | Dar de alta la cuota |
list(filtros) / get(id, { userId }) | list(filtros) / get(id, user_id=...) | Consultar |
update(id, datos) | update(id, datos) | Cambiar condiciones |
pause, resume, cancel, reactivate | pause, resume, cancel, reactivate | Cambiar de estado |
getUpcomingCharge(id, { userId }) | get_upcoming_charge(...) | Ver el próximo cobro |
generateNow(id, { userId }) | generate_now(...) | Emitir ya la factura del periodo |
listInvoices, listLogs | list_invoices, list_logs | Facturas generadas e historial |
retryCharge(id, facturaId, { userId }) | retry_charge(...) | Reintentar el cobro de una factura |
Todos los métodos, salvo list, piden el userId del usuario dueño de la suscripción.
Dar de alta la cuota
Cuatro campos definen cómo se comporta:
| JavaScript | Python | Qué decide |
|---|---|---|
frequency | frequency | MONTHLY_FIXED_DAY, con dayOfMonth de 1 a 28 (o 99 para el último día del mes); o INTERVAL_DAYS, con intervalDays. Sin dayOfMonth, se toma el día de startDate, con 28 como máximo. |
automation | automation | AUTO emite la factura de cada periodo; DRAFT la deja en borrador para que tu equipo la revise. |
chargeMode | charge_mode | MANUAL: la paga el usuario. AUTO_STRIPE: se cobra sola con la tarjeta autorizada (necesita paymentAuthorizationId). |
sendEmail | send_email | Si se envía cada factura por correo. Sin el campo, manda la preferencia de la aplicación. |
Además: name, startDate y lines (obligatorios), endDate, taxSystem, discountPercent, daysUntilDue, notes, metadata e idempotencyKey. Las líneas llevan los mismos campos fiscales que una factura; como la plantilla genera facturas mes tras mes sin que nadie la revise, una línea fiscalmente mal planteada produce facturas mal emitidas indefinidamente. Revisa el igicOperationType si operas en Canarias, y exemptionReason y exemptionNote en operaciones exentas.
const suscripcion = await sdk.subscriptions.create({
userId: 'user_01HXYZ',
name: 'Plan Profesional mensual',
frequency: 'MONTHLY_FIXED_DAY',
dayOfMonth: 1,
startDate: '2026-11-01',
automation: 'AUTO',
chargeMode: 'AUTO_STRIPE',
paymentAuthorizationId: 'ckauth1234567890',
taxSystem: 'IGIC',
daysUntilDue: 7,
sendEmail: true,
lines: [{ name: 'Plan Profesional', quantity: 1, unitPrice: 49.95, taxType: 'IGIC_7' }],
idempotencyKey: 'sub_user_01HXYZ_pro',
});El cobro automático sin autorización falla, no se degrada
Pedir AUTO_STRIPE sin una autorización vigente del usuario responde 409 PAYMENT_AUTHORIZATION_REQUIRED, con un nextStep que dice las dos operaciones que faltan. No se convierte en silencio a cobro manual: te dejaría creyendo que tienes montado un cobro que nunca ocurriría. Un 409 AUTO_CHARGE_DISABLED_FOR_ORGANIZATION es otra cosa: la organización tiene apagado el cobro automático y solo puede encenderlo su equipo, en Finanzas › Ajustes › Cobro automático con tarjeta. Ese error no trae nextStep.
Consultar y cambiar
list filtra por userId (sin él, lista las de todos los usuarios de tu aplicación), status (ACTIVE, PAUSED, CANCELLED o COMPLETED), page y limit, y devuelve un sobre plano con data, total, page, limit y totalPages; las filas son un resumen sin líneas. El detalle completo lo da get.
update cambia solo los campos que mandes. Si mandas lines, es la lista completa que quieres: las líneas que no aparezcan se eliminan.
getUpcomingCharge es la consulta que conviene enseñar en el perfil del usuario: dice cuándo y cuánto se cobrará y, con autoChargeReady en falso y su motivo (NO_AUTHORIZATION, AUTHORIZATION_REVOKED, PAYMENT_METHOD_EXPIRED o AUTOCHARGE_DISABLED), te avisa antes de que la renovación falle.
const pagina = await sdk.subscriptions.list({ userId: 'user_01HXYZ', status: 'ACTIVE' });
const proximo = await sdk.subscriptions.getUpcomingCharge(suscripcion.id, { userId: 'user_01HXYZ' });
if (!proximo.autoChargeReady) avisarAlUsuario(proximo.autoChargeBlockedReason);Pausar, reanudar, dar de baja y reactivar
Estados de una suscripción
Activa
ACTIVE
Genera la factura de cada periodo y, con cobro automático, la cobra.Se llega desde Activa cuando: pause
Pausada
PAUSED
No genera facturas ni cobra; conserva todo.Se llega desde Activa cuando: cancel
Cancelada
CANCELLED
Dada de baja, con su historial y sus facturas.
Se llega desde Activa cuando: No quedan periodos antes de endDate
Completada
COMPLETED
El generador la cierra solo al llegar a su fecha de fin.
Otras transiciones
- Pausada pasa a Activa: resume: vuelve a facturar desde la siguiente fecha prevista, sin recuperar los periodos saltados.
- Pausada pasa a Cancelada: cancel también da de baja una suscripción pausada.
- Cancelada pasa a Activa: reactivate la vuelve a poner en marcha.
No hay borrado: la contabilidad necesita su rastro.
pause y cancel admiten un motivo (reason). Las acciones que no caben en el estado actual (pausar una pausada, reanudar una dada de baja) responden 409 SUBSCRIPTION_STATE_INVALID.
await sdk.subscriptions.pause(suscripcion.id, { userId: 'user_01HXYZ', reason: 'Pausa de dos meses.' });
await sdk.subscriptions.resume(suscripcion.id, { userId: 'user_01HXYZ' });
await sdk.subscriptions.cancel(suscripcion.id, { userId: 'user_01HXYZ', reason: 'Baja solicitada.' });
await sdk.subscriptions.reactivate(suscripcion.id, { userId: 'user_01HXYZ' });Facturas, historial y cobros pendientes
listInvoices devuelve las facturas generadas con el periodo que cubre cada una (sin pdfUrl; para el detalle, invoices.get). listLogs devuelve cada ejecución del generador con su resultado: GENERATED, SKIPPED o FAILED.
generateNow emite ya la factura del periodo, solo si la suscripción está activa. Si no toca, responde con generated en falso y el motivo en reason, sin error. retryCharge reintenta el cobro de una factura concreta de la suscripción; devuelve un acuse, y el resultado llega por los avisos de cobros.
Errores y avisos
errorCode | HTTP | Qué hacer |
|---|---|---|
PAYMENT_AUTHORIZATION_REQUIRED | 409 | Recoge la autorización del usuario y vuelve a intentarlo (trae nextStep). |
AUTO_CHARGE_DISABLED_FOR_ORGANIZATION | 409 | La organización tiene apagado el cobro automático. |
SUBSCRIPTION_NOT_ACCESSIBLE | 404 | Es de otra aplicación o de otro usuario. |
SUBSCRIPTION_STATE_INVALID | 409 | La acción no cabe en el estado actual. |
BILLING_AMOUNT_EXCEEDS_LIMIT | 422 | El importe por periodo supera el tope de tu aplicación. |
EXTERNAL_USER_NOT_FOUND | 404 | Identifica antes al usuario. |
Avisos que recibe tu servidor: SUBSCRIPTION_CREATED, SUBSCRIPTION_UPDATED (con los campos cambiados), SUBSCRIPTION_PAUSED, SUBSCRIPTION_RESUMED, SUBSCRIPTION_CANCELLED, SUBSCRIPTION_COMPLETED (se llegó a la fecha de fin), SUBSCRIPTION_INVOICE_GENERATED (con emailSent) y SUBSCRIPTION_PAYMENT_AUTHORIZATION_PENDING (configurada para cobrar sola pero sin autorización vigente). Verifícalos con webhooks.parse.