Suscripciones
Alta, cambios, pausa, baja y facturas de las cuotas recurrentes de los usuarios de una aplicación externa, en /external/v1/subscriptions.
Actualizado el . 10 min de lectura
Incluido en los planes Profesional y Avanzado. Ver precios
Una suscripción define una cuota que se repite: qué se factura, cada cuánto y cómo se cobra. UTILIA OS genera cada periodo la factura con su numeración y su fiscalidad, la envía por correo si tu aplicación lo pide y, con cobro automático, la cobra con la tarjeta autorizada.
Todos los endpoints están bajo /external/v1/subscriptions, exigen la cabecera X-Api-Key y comparten un límite de 60 peticiones por minuto. Cada aplicación solo ve las suscripciones de sus propios usuarios.
Plan y permisos
| Concepto | Valor |
|---|---|
| Aplicativo del plan | Finanzas |
| Capacidad del plan | Facturación recurrente (finance.recurring) |
Sin el 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.
Además, la facturación de la aplicación tiene que estar activada: si no, todas estas rutas, también las lecturas, responden 403 BILLING_NOT_ENABLED. Se activa en Dinero › Facturación, en la ficha de la aplicación.
Las suscripciones no exigen la pasarela: una suscripción en modo manual genera y envía facturas sin cobrar nada. La pasarela solo hace falta para el cobro automático.
| Endpoint | Permiso de la API (scope) |
|---|---|
Lecturas (GET) | billing:subscriptions:read |
| Alta, cambios, pausa, reanudación, reactivación y generación | billing:subscriptions:write |
POST /subscriptions/:id/generate-now de una suscripción que se cobra sola | además, billing:scheduled-charges:write o billing:charge |
POST /subscriptions/:id/invoices/:invoiceId/retry-charge | billing:scheduled-charges:write |
POST /subscriptions/:id/cancel | billing:subscriptions:cancel |
Reintentar el cobro de una cuota mueve dinero en el momento, así que no
va con el permiso de las suscripciones: exige billing:scheduled-charges:write
(«Programar cobros»), el mismo que adelantar un reintento de un cobro
programado. Es un permiso crítico financiero, que solo concede un Super
Administrador con un motivo. Por lo mismo, emitir ya la cuota de una
suscripción que se cobra sola con la autorización del usuario adelanta
también el cobro y pide uno de esos dos permisos; sin ninguno, la
respuesta es 403 INSUFFICIENT_SCOPE con los dos en requiredScopes.
Dar de baja tiene permiso propio porque corta un ingreso recurrente y no se deshace: para volver a cobrar hay que crear una suscripción nueva. billing:subscriptions:cancel solo lo concede un Super Administrador, con un motivo. Por eso pausar y dar de baja son operaciones distintas: pausar conserva el historial y la autorización de cobro.
Crear una suscripción
/api/external/v1/subscriptionsDa de alta una cuota recurrente para un usuario de la aplicación. La ficha de cliente del CRM se obtiene a partir del userId.
Cuerpo
- userIdstringobligatorio
- Identificador del usuario en tu aplicación (externalId). De él sale el cliente del CRM al que se factura.
- namestringobligatorio
- Nombre visible de la suscripción, entre 1 y 200 caracteres.
- descriptionstring
- Descripción interna.
- frequencyenum
- MONTHLY_FIXED_DAY (día fijo del mes) o INTERVAL_DAYS (cada N días). Por defecto mensual.
- dayOfMonthinteger
- Día del mes, entre 1 y 28, o 99 para el último día. Solo con frecuencia mensual. Si lo omites, se toma el día de startDate, rebajado a 28 cuando es posterior.
- intervalDaysinteger
- Días entre facturas, entre 1 y 365. Con frecuencia por intervalo.
- startDatestringobligatorio
- Fecha de la primera factura, en formato ISO 8601.
- endDatestring
- Fecha de fin. Al alcanzarla la suscripción pasa a COMPLETED.
- automationenum
- AUTO emite la factura de cada periodo; DRAFT la deja en borrador para que tu equipo la revise. Por defecto, AUTO.
- chargeModeenum
- MANUAL (la paga el usuario) o AUTO_STRIPE (se cobra sola con la tarjeta autorizada). Por defecto, MANUAL.
- paymentAuthorizationIdstring
- Autorización de cobro que respalda el cobro automático. En la práctica, obligatoria con AUTO_STRIPE.
- taxSystemenum
- IVA, IGIC, IPSI, EXEMPT o INTERNATIONAL. Sin este campo, el de tu organización.
- consumerResidenceobjeto
- Residencia del consumidor de las facturas generadas, con la misma forma que en Facturas. Solo admite OTHER_EU (con su país) y PENINSULA_BALEARES; evidence (texto libre) no se usa aquí. Con OTHER_EU se guarda el Estado de consumo. Obligatoria con EU_OSS (ventanilla única del IVA, Próximamente), con evidenceItems. La prueba no se devuelve nunca.
- typeenum
- Tipo de las facturas que genera: STANDARD (ordinarias, por defecto) o SIMPLIFIED (simplificadas). Las simplificadas solo si cada cuota no pasa de 400 € impuestos incluidos; si no, 422 SIMPLIFIED_INVOICE_AMOUNT_EXCEEDED.
- seriesCodestring
- Serie en la que se numeran las facturas generadas: el código de una serie de tu aplicación, del mismo tipo y activa (GET /external/v1/invoices/series), o DEFAULT para la de siempre. Si lo omites, la serie por defecto de la aplicación para el tipo o, si no tiene, la de siempre. La serie resuelta queda fijada en la suscripción: si después cambia la serie por defecto de la aplicación, la suscripción no se mueve. Errores: 400 INVOICE_SERIES_NOT_AVAILABLE, 400 INVOICE_SERIES_TYPE_MISMATCH y 409 INVOICE_SERIES_INACTIVE.
- discountPercentnumber
- Descuento global entre 0 y 100.
- daysUntilDueinteger
- Días hasta el vencimiento de cada factura, entre 0 y 365.
- notesstring
- Notas que se imprimen en el PDF de cada factura.
- sendEmailboolean
- Envío por correo de cada factura generada. Sin este campo manda la preferencia de la aplicación.
- linesarrayobligatorio
- Líneas de la cuota. Mínimo una.
- metadataobjeto
- Datos libres. Las claves que empiezan por external están reservadas.
- idempotencyKeystring
- Clave de idempotencia, válida 24 horas.
Cada línea acepta order, name (obligatorio), description, quantity, unitPrice (obligatorio), discount, taxType (obligatorio), igicOperationType, exemptionReason y exemptionNote, y en las líneas EU_VAT de la ventanilla única, vatRateClass y ossSupplyType. Con taxSystem: "EU_OSS" (en «Próximamente»), cada cuota vuelve a comprobar el periodo de alta y resuelve el tipo de IVA del país del consumidor en su fecha.
Los campos fiscales de la línea no son decorativos
Las facturas se emiten ya numeradas y cerradas, mes tras mes, sin que intervenga nadie. Una plantilla fiscalmente incorrecta produce facturas incorrectas sin fin. Si operas en Canarias, revisa igicOperationType; si la operación está exenta, exemptionReason y exemptionNote. Los valores admitidos son los mismos que en Facturas.
Ejemplo
curl -X POST https://os.utilia.ai/api/external/v1/subscriptions \
-H "X-Api-Key: utl_live_..." \
-H "Content-Type: application/json" \
-d '{
"userId": "user_01HXYZ",
"name": "Nubeta Profesional mensual",
"frequency": "MONTHLY_FIXED_DAY",
"dayOfMonth": 1,
"startDate": "2026-10-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"
}
],
"metadata": { "planId": "pro_monthly" },
"idempotencyKey": "sub_user_01HXYZ_pro"
}'Respuesta (201)
{
"id": "cksub1234567890",
"name": "Nubeta Profesional mensual",
"status": "ACTIVE",
"frequency": "MONTHLY_FIXED_DAY",
"automation": "AUTO",
"chargeMode": "AUTO_STRIPE",
"type": "STANDARD",
"seriesCode": "DEFAULT",
"dayOfMonth": 1,
"intervalDays": null,
"startDate": "2026-10-01",
"endDate": null,
"nextInvoiceDate": "2026-11-01",
"periodAmount": 53.45,
"currency": "eur",
"externalUserId": "user_01HXYZ",
"paymentAuthorizationId": "ckauth1234567890",
"createdAt": "2026-10-01T09:12:44.000Z",
"updatedAt": "2026-10-01T09:12:44.000Z",
"taxSystem": "IGIC",
"discountPercent": 0,
"daysUntilDue": 7,
"notes": null,
"sendEmail": true,
"lines": [
{
"id": "ckline1234567890",
"order": 0,
"name": "Plan Profesional",
"description": null,
"quantity": 1,
"unitPrice": 49.95,
"discount": 0,
"taxType": "IGIC_7",
"subtotal": 49.95
}
],
"paymentFailureCount": 0,
"invoiceCount": 0,
"metadata": { "planId": "pro_monthly" }
}El cobro automático se rechaza por dos motivos distintos
Pedir chargeMode: "AUTO_STRIPE" falla, en lugar de pasar en silencio a cobro manual, en dos casos que se arreglan en sitios diferentes:
PAYMENT_AUTHORIZATION_REQUIRED: el usuario no tiene una autorización de cobro vigente. Lo resuelves tú: lleva a la persona a firmarla siguiendo elnextStepque trae el error.AUTO_CHARGE_DISABLED_FOR_ORGANIZATION: tu organización tiene apagado el cobro automático con tarjeta. Este error no traenextStep, porque tu aplicación no puede resolverlo: el interruptor está en Finanzas › Ajustes › Cobro automático con tarjeta. Recoger consentimientos no desbloquea nada mientras siga apagado.
Hasta el 5 de septiembre de 2026, el segundo caso llegaba como el primero. Si tu código comprueba errorCode === 'PAYMENT_AUTHORIZATION_REQUIRED' para pedir la firma al usuario, añade la rama del código nuevo.
Errores
| Situación | HTTP | errorCode |
|---|---|---|
| El usuario indicado no existe en la aplicación | 404 | EXTERNAL_USER_NOT_FOUND |
| Cobro automático sin autorización vigente | 409 | PAYMENT_AUTHORIZATION_REQUIRED, con nextStep |
| La organización tiene apagado el cobro automático | 409 | AUTO_CHARGE_DISABLED_FOR_ORGANIZATION, sin nextStep |
| El importe por periodo supera el tope de la aplicación | 422 | BILLING_AMOUNT_EXCEEDS_LIMIT |
| Suscripción de simplificadas con una cuota de más de 400 € impuestos incluidos | 422 | SIMPLIFIED_INVOICE_AMOUNT_EXCEEDED |
| La serie no existe o no es de tu aplicación | 400 | INVOICE_SERIES_NOT_AVAILABLE |
La serie es de otro tipo que type | 400 | INVOICE_SERIES_TYPE_MISMATCH |
| La serie está desactivada | 409 | INVOICE_SERIES_INACTIVE |
| Dato de la ventanilla única del IVA con la función no disponible para tu organización | 403 | FEATURE_COMING_SOON |
| Reglas de la ventanilla única del IVA (prueba, país, régimen, umbral) | 400 | OSS_* |
Listar suscripciones
/api/external/v1/subscriptionsLista paginada de las suscripciones de la aplicación.
- userIdstring
- Filtra por usuario. Sin él se listan las de todos tus usuarios.
- statusenum
- ACTIVE, PAUSED, CANCELLED o COMPLETED.
- frequencyenum
- MONTHLY_FIXED_DAY o INTERVAL_DAYS.
- automationenum
- AUTO o DRAFT.
- chargeModeenum
- MANUAL o AUTO_STRIPE.
- pageinteger
- Página, por defecto 1.
- limitinteger
- Elementos por página, por defecto 20.
La respuesta tiene la forma { data, total, page, limit, totalPages }, y cada fila es el resumen de la suscripción, sin líneas ni metadatos. El detalle completo se pide con GET /subscriptions/:id.
Detalle, cambios y ciclo de vida
/api/external/v1/subscriptions/:idDetalle completo, con líneas y metadatos. Permiso billing:subscriptions:read. userId en la consulta es opcional: si lo mandas, la suscripción tiene que ser de ese usuario. 404 SUBSCRIPTION_NOT_ACCESSIBLE si no es de tu aplicación.
/api/external/v1/subscriptions/:idCambia las condiciones para las facturas futuras: líneas, frecuencia, fecha del próximo cobro, modo de cobro, tipo, serie, envío por correo. Permiso billing:subscriptions:write. userId en el cuerpo es obligatorio. Responde la suscripción completa.
/api/external/v1/subscriptions/:id/pausePausa temporal: deja de generar facturas y conserva el historial y la autorización de cobro. Permiso billing:subscriptions:write. Emite SUBSCRIPTION_PAUSED.
/api/external/v1/subscriptions/:id/resumeReanuda una suscripción pausada. Permiso billing:subscriptions:write. Emite SUBSCRIPTION_RESUMED.
/api/external/v1/subscriptions/:id/cancelBaja definitiva: deja de facturar y conserva el historial. Permiso propio billing:subscriptions:cancel. Emite SUBSCRIPTION_CANCELLED.
/api/external/v1/subscriptions/:id/reactivateReactiva una suscripción cancelada, cuando su estado lo permite. Permiso billing:subscriptions:write. Emite SUBSCRIPTION_RESUMED.
/api/external/v1/subscriptions/:id/generate-nowGenera ya la factura del periodo de una suscripción activa, sin esperar a su fecha. Permiso billing:subscriptions:write; si la suscripción se cobra sola, además billing:scheduled-charges:write o billing:charge. 409 SUBSCRIPTION_STATE_INVALID si no está activa.
Todas las acciones toman en el cuerpo userId (obligatorio, el externalId del usuario dueño de la suscripción) y reason (opcional, hasta 500 caracteres, para el historial y la auditoría). pause, resume, cancel y reactivate responden la suscripción con la forma del listado. PATCH acepta además los mismos campos del alta que sí se pueden cambiar, también type y seriesCode, que valen para las facturas futuras. Si cambias type sin mandar seriesCode, la suscripción pasa a la serie por defecto de tu aplicación para el nuevo tipo (o a la de siempre).
Los cambios y las acciones del ciclo de vida, salvo generate-now, los
hace UTILIA OS a nombre de la persona responsable de la aplicación y con
sus permisos de Finanzas: si a esa persona le falta el permiso sobre las
suscripciones, la respuesta es 403 APP_RESPONSIBLE_PERMISSION_DENIED.
No existe DELETE: dar de baja es cancel, que conserva el historial de facturas y de intentos de cobro. Un borrado destruiría datos que la contabilidad necesita.
Estados de una suscripción
Activa
ACTIVE
Genera la factura de cada periodo.Se llega desde Activa cuando: pause, o acumula cobros fallidos hasta el umbral de tu organización
Pausada
PAUSED
No genera facturas; conserva el historial y la autorización.Se llega desde Activa cuando: cancel
Cancelada
CANCELLED
Baja definitiva; conserva el historial.
Se llega desde Activa cuando: Llega la fecha de fin (endDate)
Completada
COMPLETED
Estado final
Aviso SUBSCRIPTION_COMPLETED.
Otras transiciones
- Pausada pasa a Activa: resume la reanuda.
- Pausada pasa a Cancelada: cancel también la da de baja.
- Cancelada pasa a Activa: reactivate la recupera.
Errores del ciclo de vida
| Situación | HTTP | errorCode |
|---|---|---|
| La suscripción es de otra aplicación o de otro usuario | 404 | SUBSCRIPTION_NOT_ACCESSIBLE |
| Pausar una pausada, reanudar una cancelada, cambiar una completada | 409 | SUBSCRIPTION_STATE_INVALID |
| La persona responsable no tiene permiso sobre las suscripciones | 403 | APP_RESPONSIBLE_PERMISSION_DENIED |
| La aplicación no tiene una persona responsable activa | 403 | APP_RESPONSIBLE_INACTIVE |
Respuesta de generate-now
{
"generated": true,
"invoiceId": "ckinv1234567890",
"invoiceNumber": "202600042",
"invoiceStatus": "SENT",
"reason": null
}Con generated: false, el campo reason explica por qué no tocaba generar todavía.
Próximo cobro
/api/external/v1/subscriptions/:id/upcoming-chargeQué se va a facturar y cobrar la próxima vez, y si el cobro automático está listo.
{
"subscriptionId": "cksub1234567890",
"nextInvoiceDate": "2026-11-01",
"nextChargeDate": "2026-11-08",
"amount": 53.45,
"currency": "eur",
"autoChargeReady": true,
"autoChargeBlockedReason": null
}Es la consulta que conviene mostrar en el perfil del usuario dentro de tu aplicación: autoChargeReady: false, con su motivo, te avisa antes de que la renovación falle.
Facturas de la suscripción
/api/external/v1/subscriptions/:id/invoicesFacturas ya generadas por esta suscripción, con el periodo que cubre cada una.
{
"data": [
{
"id": "ckinv1234567890",
"invoiceNumber": "202600042",
"status": "PAID",
"totalAmount": 53.45,
"paidAmount": 53.45,
"currency": "eur",
"issueDate": "2026-10-01T00:00:00.000Z",
"dueDate": "2026-10-08T00:00:00.000Z",
"periodStart": "2026-10-01T00:00:00.000Z",
"periodEnd": "2026-10-31T23:59:59.000Z",
"taxSystem": "IGIC",
"consumerCountryCode": null,
"ossScheme": null
}
],
"total": 3,
"page": 1,
"limit": 20,
"totalPages": 1
}Cada fila es reducida: trae el periodo que cubre, pero no pdfUrl, externalUserId ni createdAt. Desde el 15 de octubre de 2026 trae también el sistema impositivo, el Estado de consumo y el régimen de la ventanilla única con que se emitió cada cuota. Para el detalle completo de una factura, usa GET /external/v1/invoices/:id.
Historial de ejecuciones
/api/external/v1/subscriptions/:id/logsQué pasó en cada ciclo: si se generó la factura, si se saltó o si falló.
{
"data": [
{
"id": "cklog1234567890",
"executedAt": "2026-10-01T06:00:00.000Z",
"result": "GENERATED",
"invoiceId": "ckinv1234567890",
"invoiceNumber": "202600042",
"detail": "Factura generada y enviada por correo."
}
],
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
}Reintentar el cobro de una factura de la suscripción
/api/external/v1/subscriptions/:id/invoices/:invoiceId/retry-chargeCancela los reintentos pendientes del cobro de una factura generada por esta suscripción y lanza un intento inmediato con la tarjeta del usuario. Permiso billing:scheduled-charges:write. Exige Pagos con tarjeta (Stripe) y el cobro automático en el plan. Como mucho 3 por minuto y aplicación.
Además del permiso, exige en el plan Pagos con tarjeta (Stripe) y la
capacidad de cobro automático (403 MODULE_NOT_ENABLED o
CAPABILITY_NOT_ENABLED), y una persona responsable activa en la
aplicación (403 APP_RESPONSIBLE_INACTIVE). Una rectificativa o un
abono no se cobran nunca: 409 INVOICE_STATE_INVALID. No hay ensayo en
seco. Admite como mucho 3 llamadas por minuto y aplicación: por encima,
429.
Devuelve un acuse de recibo: el resultado del cobro no llega en la respuesta.
{
"accepted": true,
"scheduledChargeId": "cksch1234567890",
"status": "PENDING",
"scheduledAt": "2026-10-05T10:30:00.000Z"
}El resultado llega por los avisos de cobro (CHARGE_*).
Avisos de suscripciones
Todos dirigidos a la aplicación dueña del usuario.
| Aviso | Cuándo |
|---|---|
SUBSCRIPTION_CREATED | Alta. |
SUBSCRIPTION_UPDATED | Cambio de condiciones, con la lista de campos cambiados. |
SUBSCRIPTION_PAUSED | Pausa temporal. |
SUBSCRIPTION_RESUMED | Reanudación o reactivación. |
SUBSCRIPTION_CANCELLED | Baja definitiva. |
SUBSCRIPTION_COMPLETED | Se alcanzó la fecha de fin. |
SUBSCRIPTION_INVOICE_GENERATED | Factura del periodo, con emailSent. |
SUBSCRIPTION_PAYMENT_AUTHORIZATION_PENDING | Está configurada para cobrar sola pero no tiene autorización vigente. |
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 suscripcion = await sdk.subscriptions.create({
userId: 'user_01HXYZ',
name: 'Nubeta Profesional mensual',
frequency: 'MONTHLY_FIXED_DAY',
dayOfMonth: 1,
startDate: '2026-10-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',
});
const proximo = await sdk.subscriptions.getUpcomingCharge(suscripcion.id, {
userId: 'user_01HXYZ',
});
await sdk.subscriptions.pause(suscripcion.id, {
userId: 'user_01HXYZ',
reason: 'El cliente pidió parar dos meses.',
});Siguiente paso
- Autorizaciones de cobroRecoger el consentimiento que habilita el cobro automático.
- Acciones sobre una facturaEnviar, cobrar, marcar pagada o devolver una factura generada.
- Contenido de los avisosLos avisos de suscripciones y de cobros, campo a campo.
- Guía de facturaciónEl recorrido completo, del alta del usuario al cobro recurrente.