# Suscripciones

Alta, cambios, pausa, baja y facturas de las cuotas recurrentes de los usuarios de una aplicación externa, en /external/v1/subscriptions.

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

**`POST /api/external/v1/subscriptions`**

Da 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

| Nombre | Tipo | Obligatorio | Descripción |
| - | - | - | - |
| `userId` | `string` | Sí | Identificador del usuario en tu aplicación (externalId). De él sale el cliente del CRM al que se factura. |
| `name` | `string` | Sí | Nombre visible de la suscripción, entre 1 y 200 caracteres. |
| `description` | `string` | No | Descripción interna. |
| `frequency` | `enum` | No | MONTHLY_FIXED_DAY (día fijo del mes) o INTERVAL_DAYS (cada N días). Por defecto mensual. |
| `dayOfMonth` | `integer` | No | 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. |
| `intervalDays` | `integer` | No | Días entre facturas, entre 1 y 365. Con frecuencia por intervalo. |
| `startDate` | `string` | Sí | Fecha de la primera factura, en formato ISO 8601. |
| `endDate` | `string` | No | Fecha de fin. Al alcanzarla la suscripción pasa a COMPLETED. |
| `automation` | `enum` | No | AUTO emite la factura de cada periodo; DRAFT la deja en borrador para que tu equipo la revise. Por defecto, AUTO. |
| `chargeMode` | `enum` | No | MANUAL (la paga el usuario) o AUTO_STRIPE (se cobra sola con la tarjeta autorizada). Por defecto, MANUAL. |
| `paymentAuthorizationId` | `string` | No | Autorización de cobro que respalda el cobro automático. En la práctica, obligatoria con AUTO_STRIPE. |
| `taxSystem` | `enum` | No | IVA, IGIC, IPSI, EXEMPT o INTERNATIONAL. Sin este campo, el de tu organización. |
| `consumerResidence` | `objeto` | No | 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. |
| `type` | `enum` | No | 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. |
| `seriesCode` | `string` | No | 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. |
| `discountPercent` | `number` | No | Descuento global entre 0 y 100. |
| `daysUntilDue` | `integer` | No | Días hasta el vencimiento de cada factura, entre 0 y 365. |
| `notes` | `string` | No | Notas que se imprimen en el PDF de cada factura. |
| `sendEmail` | `boolean` | No | Envío por correo de cada factura generada. Sin este campo manda la preferencia de la aplicación. |
| `lines` | `array` | Sí | Líneas de la cuota. Mínimo una. |
| `metadata` | `objeto` | No | Datos libres. Las claves que empiezan por external están reservadas. |
| `idempotencyKey` | `string` | No | 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.

> **Atención: 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](https://os.utilia.ai/docs/developers/api/invoices).

### Ejemplo

```bash
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)

```json
{
  "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" }
}
```

> **Atención: 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 el `nextStep` que trae el error.
> - **`AUTO_CHARGE_DISABLED_FOR_ORGANIZATION`**: tu organización tiene apagado el cobro automático con tarjeta. Este error **no trae `nextStep`**, 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

**`GET /api/external/v1/subscriptions`**

Lista paginada de las suscripciones de la aplicación.

| Nombre | Tipo | Obligatorio | Descripción |
| - | - | - | - |
| `userId` | `string` | No | Filtra por usuario. Sin él se listan las de todos tus usuarios. |
| `status` | `enum` | No | ACTIVE, PAUSED, CANCELLED o COMPLETED. |
| `frequency` | `enum` | No | MONTHLY_FIXED_DAY o INTERVAL_DAYS. |
| `automation` | `enum` | No | AUTO o DRAFT. |
| `chargeMode` | `enum` | No | MANUAL o AUTO_STRIPE. |
| `page` | `integer` | No | Página, por defecto 1. |
| `limit` | `integer` | No | 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

**`GET /api/external/v1/subscriptions/:id`**

Detalle 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.

Parámetro de ruta: `id`.

**`PATCH /api/external/v1/subscriptions/:id`**

Cambia 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.

Parámetro de ruta: `id`.

**`POST /api/external/v1/subscriptions/:id/pause`**

Pausa temporal: deja de generar facturas y conserva el historial y la autorización de cobro. Permiso billing:subscriptions:write. Emite SUBSCRIPTION_PAUSED.

Parámetro de ruta: `id`.

**`POST /api/external/v1/subscriptions/:id/resume`**

Reanuda una suscripción pausada. Permiso billing:subscriptions:write. Emite SUBSCRIPTION_RESUMED.

Parámetro de ruta: `id`.

**`POST /api/external/v1/subscriptions/:id/cancel`**

Baja definitiva: deja de facturar y conserva el historial. Permiso propio billing:subscriptions:cancel. Emite SUBSCRIPTION_CANCELLED.

Parámetro de ruta: `id`.

**`POST /api/external/v1/subscriptions/:id/reactivate`**

Reactiva una suscripción cancelada, cuando su estado lo permite. Permiso billing:subscriptions:write. Emite SUBSCRIPTION_RESUMED.

Parámetro de ruta: `id`.

**`POST /api/external/v1/subscriptions/:id/generate-now`**

Genera 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.

Parámetro de ruta: `id`.

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**

1. **Activa** (`ACTIVE`). Genera la factura de cada periodo.
   - **Pausada** (`PAUSED`). Se llega desde Activa cuando: pause, o acumula cobros fallidos hasta el umbral de tu organización. No genera facturas; conserva el historial y la autorización.
   - **Cancelada** (`CANCELLED`). Se llega desde Activa cuando: cancel. Baja definitiva; conserva el historial.
2. **Completada** (`COMPLETED`, estado final). Se llega desde Activa cuando: Llega la fecha de fin (endDate). Aviso SUBSCRIPTION_COMPLETED.

Otras transiciones:

- Pausada → Activa: resume la reanuda.
- Pausada → Cancelada: cancel también la da de baja.
- Cancelada → Activa: reactivate la recupera.

_Los rótulos son los del panel de Finanzas; el valor de status es el que devuelve la API._

### 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`

```json
{
  "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

**`GET /api/external/v1/subscriptions/:id/upcoming-charge`**

Qué se va a facturar y cobrar la próxima vez, y si el cobro automático está listo.

Parámetro de ruta: `id`.

```json
{
  "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

**`GET /api/external/v1/subscriptions/:id/invoices`**

Facturas ya generadas por esta suscripción, con el periodo que cubre cada una.

Parámetro de ruta: `id`.

```json
{
  "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

**`GET /api/external/v1/subscriptions/:id/logs`**

Qué pasó en cada ciclo: si se generó la factura, si se saltó o si falló.

Parámetro de ruta: `id`.

```json
{
  "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

**`POST /api/external/v1/subscriptions/:id/invoices/:invoiceId/retry-charge`**

Cancela 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.

Parámetros de ruta: `id`, `invoiceId`.

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.

```json
{
  "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

### JavaScript / TypeScript

```typescript
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.',
});
```

### Python

```python
import os
from utilia_sdk import UtiliaSDK
from utilia_sdk.models import (
    CreateExternalSubscriptionInput,
    CreateExternalSubscriptionLineInput,
    ExternalSubscriptionActionInput,
)

sdk = UtiliaSDK(
    base_url="https://os.utilia.ai/api",
    api_key=os.environ["UTILIA_API_KEY"],
)

suscripcion = await sdk.subscriptions.create(
    CreateExternalSubscriptionInput(
        user_id="user_01HXYZ",
        name="Nubeta Profesional mensual",
        frequency="MONTHLY_FIXED_DAY",
        day_of_month=1,
        start_date="2026-10-01",
        automation="AUTO",
        charge_mode="AUTO_STRIPE",
        payment_authorization_id="ckauth1234567890",
        tax_system="IGIC",
        days_until_due=7,
        send_email=True,
        lines=[
            CreateExternalSubscriptionLineInput(
                name="Plan Profesional",
                quantity=1,
                unit_price=49.95,
                tax_type="IGIC_7",
            )
        ],
        idempotency_key="sub_user_01HXYZ_pro",
    )
)

proximo = await sdk.subscriptions.get_upcoming_charge(
    suscripcion.id, user_id="user_01HXYZ"
)

await sdk.subscriptions.pause(
    suscripcion.id,
    ExternalSubscriptionActionInput(
        user_id="user_01HXYZ",
        reason="El cliente pidió parar dos meses.",
    ),
)
```

## Siguiente paso

- [Autorizaciones de cobro](https://os.utilia.ai/docs/developers/api/payment-authorizations): Recoger el consentimiento que habilita el cobro automático.
- [Acciones sobre una factura](https://os.utilia.ai/docs/developers/api/invoice-actions): Enviar, cobrar, marcar pagada o devolver una factura generada.
- [Contenido de los avisos](https://os.utilia.ai/docs/developers/webhooks/events): Los avisos de suscripciones y de cobros, campo a campo.
- [Guía de facturación](https://os.utilia.ai/docs/developers/api/billing-guide): El recorrido completo, del alta del usuario al cobro recurrente.
