> ## Documentation Index
> Fetch the complete documentation index at: https://devs.tapipay.la/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscriptions

> Recurring charges that automatically generate a series of debts.

A **subscription** (`Subscription`) is a recurring charge that automatically generates a series of [debts](/en/resources/debts), one per cycle. You define **what** to charge (amount, frequency, debtor, and duration) and TapiPay generates the cycles and resolves all of the internal complexity for you.

**When to use it:** charges that repeat over time (monthly payments, credit installments, periodic plans). For a single charge use a [debt](/en/resources/debts); to charge without a known debtor, use a [payment link](/en/resources/payment-links).

## Upfront generation of cycles

When you create the subscription, TapiPay calculates the dates of all cycles and **generates the debts upfront** (there is no process that creates one cycle per month). The generation of those debts happens **asynchronously**: the subscription is returned in `ACTIVE` status as soon as the calculation and queuing finish successfully.

<Note>
  If the generation of the debts fails, the subscription is automatically reverted so that you can retry with the same `externalSubscriptionId` without duplicating charges.
</Note>

## Create a subscription

```bash theme={null}
curl --request POST \
  --url 'https://tapipay-facade.dev.tapila.cloud/subscriptions' \
  --header 'x-api-key: YOUR_API_KEY' \
  --header 'x-authorization-token: YOUR_TAPI_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "externalSubscriptionId": "SUB-2026-007",
    "externalClientId": "CLI-00042",
    "amount": 999.00,
    "currency": "MXN",
    "intervalUnit": "month",
    "intervalCount": 1,
    "startDate": "2026-06-15",
    "totalCycles": 12
  }'
```

### Main fields

<ResponseField name="externalSubscriptionId" type="string" required>
  Natural key of the subscription. Unique per organization. If you repeat it, the API responds `409 RESOURCE_CONFLICT`.
</ResponseField>

<ResponseField name="externalClientId / contactData" type="string | object" required>
  The debtor. Send one of the two (not both), just like in a [debt](/en/resources/debts#identify-the-debtor). In v1 each subscription has exactly one debtor.
</ResponseField>

<ResponseField name="amount" type="number" required>
  Amount per cycle in **pesos**, with up to 2 decimal places (minimum `0.01`).
</ResponseField>

<ResponseField name="intervalUnit" type="string" required>
  Interval unit: `day`, `week`, `month`, or `year`.
</ResponseField>

<ResponseField name="intervalCount" type="integer" required>
  Number of units per cycle. For example, `intervalUnit: "month"` + `intervalCount: 3` is quarterly.
</ResponseField>

<ResponseField name="startDate" type="string" required>
  Start date in `YYYY-MM-DD` format.
</ResponseField>

<ResponseField name="totalCycles" type="integer">
  Number of cycles to generate (maximum 520). **Mutually exclusive** with `endDate`.
</ResponseField>

<ResponseField name="endDate" type="string">
  End date (`YYYY-MM-DD`), later than `startDate`. **Mutually exclusive** with `totalCycles`.
</ResponseField>

<ResponseField name="billingDay" type="object">
  Override of the billing day: `{ day, month? }`. Only applies to `month` or `year` intervals. See below.
</ResponseField>

<ResponseField name="enforcePaymentOrder" type="boolean" default="false">
  If `true`, the debtor must pay the cycles in chronological order.
</ResponseField>

It also accepts `description`, `productName`, `allowOverduePayment`, `allowPartialPayments`, `autopay`, and `paymentMethods` with the same behavior as a [debt](/en/resources/debts). See [Payment methods](/en/concepts/payment-methods).

### Frequencies with `intervalUnit` + `intervalCount`

| Frequency     | `intervalUnit` | `intervalCount` |
| ------------- | -------------- | --------------- |
| Weekly        | `week`         | `1`             |
| Biweekly      | `week`         | `2`             |
| Monthly       | `month`        | `1`             |
| Quarterly     | `month`        | `3`             |
| Every 45 days | `day`          | `45`            |
| Yearly        | `year`         | `1`             |

### Billing day (`billingDay`)

By default, the billing day is inferred from the `startDate` (*anniversary* model): if the subscription starts on the 15th, all charges fall on the 15th. The `billingDay` override decouples the billing day from the `startDate`.

<Warning>
  `billingDay` is only valid for `month` or `year` intervals. The `month` subfield only applies to `year` intervals.
</Warning>

**End of month:** if the configured day exceeds the last day of the month (for example, `31` in February), the charge is made on the **last day of the month**; the following month returns to the original day.

```text billingDay = { day: 31 }, monthly, startDate 2026-01-31 theme={null}
Cycle 1: 2026-01-31   (January has 31)
Cycle 2: 2026-02-28   (February has 28 in 2026)
Cycle 3: 2026-03-31   (returns to the original day)
Cycle 4: 2026-04-30   (April has 30)
```

### Indefinite duration

If you do not send `totalCycles` or `endDate`, the subscription stays `ACTIVE` and **only the debt for the first cycle** is generated (to avoid generating charges without limit). Define `totalCycles` or `endDate` when you want to generate all cycles upfront.

## The response

The scheduling data travels **nested** in the `scheduling` object.

```json theme={null}
{
  "data": {
    "subscriptionId": "sub_4pQ2nW8s",
    "externalSubscriptionId": "SUB-2026-007",
    "status": "ACTIVE",
    "scheduling": {
      "intervalUnit": "month",
      "intervalCount": 1,
      "startDate": "2026-06-15",
      "totalCycles": 12,
      "endDate": null,
      "billingDay": { "day": 15 }
    },
    "externalClientId": "CLI-00042",
    "amount": 999.00,
    "currency": "MXN",
    "paymentUrl": "https://app.tapipay.la/s/acme-corp/portal/CLI-00042/",
    "enforcePaymentOrder": false,
    "autopay": false,
    "allowOverduePayment": true,
    "createdAt": "2026-06-03T18:15:00Z",
    "updatedAt": "2026-06-03T18:15:00Z"
  },
  "requestId": "req_a1b2c3d4"
}
```

## Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> PROCESSING
    PROCESSING --> ACTIVE: debts generated
    PROCESSING --> CANCELLED
    ACTIVE --> PAUSED: pause
    PAUSED --> ACTIVE: resume
    ACTIVE --> CANCELLED: cancel
    PAUSED --> CANCELLED: cancel
    CANCELLED --> [*]
```

| Status       | Meaning                                                                        |
| ------------ | ------------------------------------------------------------------------------ |
| `PROCESSING` | Transitory state while the debts are generated when creating the subscription. |
| `ACTIVE`     | Active. The debts for the cycles have already been generated.                  |
| `PAUSED`     | Manually paused. Can be resumed.                                               |
| `CANCELLED`  | Manually cancelled. Final state.                                               |

<Note>
  Each debt generated by the subscription has its **own** lifecycle (`PENDING`, `PAID`, etc.). Cancelling the subscription does not automatically cancel the debts already generated. See [Debts](/en/resources/debts#lifecycle).
</Note>

### Payment order (`enforcePaymentOrder`)

With `enforcePaymentOrder: true`, the debtor cannot pay a cycle if there are earlier unpaid cycles. Attempting it returns `422 BUSINESS_RULE_VIOLATION`. They must settle them in chronological order.

## Endpoints

| Method  | Path                         | Description                                                                                                                                    |
| ------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST`  | `/subscriptions`             | Create a subscription.                                                                                                                         |
| `GET`   | `/subscriptions/{id}`        | Retrieve a subscription.                                                                                                                       |
| `GET`   | `/subscriptions`             | List subscriptions.                                                                                                                            |
| `PATCH` | `/subscriptions/{id}`        | Update editable fields (`amount`, `description`, `enforcePaymentOrder`, `allowOverduePayment`, `autopay`, `paymentMethods`, `additionalData`). |
| `POST`  | `/subscriptions/{id}/pause`  | Pause.                                                                                                                                         |
| `POST`  | `/subscriptions/{id}/resume` | Resume.                                                                                                                                        |
| `POST`  | `/subscriptions/{id}/cancel` | Cancel.                                                                                                                                        |
| `GET`   | `/subscriptions/{id}/debts`  | List the generated debts.                                                                                                                      |

<Card title="Try it in the API Reference" icon="play" href="/api-reference">
  Explore each endpoint with its interactive playground.
</Card>
