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

# Build Recurring Billing with Therius Subscriptions

> Create plans, enroll customers in subscriptions, handle dunning, and manage plan changes, pauses, and cancellations with the Therius subscription API.

Therius subscriptions give you a complete recurring billing engine out of the box. Plans define the billing amount, currency, and interval. Subscriptions enroll individual customers into a plan and store their payment method securely. From there, Therius handles automatic renewals on schedule, trial period logic, dunning retries when a payment fails, and lifecycle events you can listen to via webhooks — so you can focus on your product instead of billing edge cases.

## Subscription Statuses

Every subscription moves through a defined set of statuses over its lifetime:

| Status      | Meaning                                                                                 |
| ----------- | --------------------------------------------------------------------------------------- |
| `pending`   | Created but the first payment has not yet been collected                                |
| `trialing`  | Within the free trial period; no charge has been made                                   |
| `active`    | Billing normally; last payment succeeded                                                |
| `past_due`  | Last renewal failed; Therius is retrying according to the dunning schedule              |
| `suspended` | All dunning retries exhausted; subscription is inactive until payment method is updated |
| `paused`    | Manually paused; no renewals attempted                                                  |
| `cancelled` | Permanently cancelled; cannot be reactivated                                            |
| `completed` | Reached a fixed number of billing cycles and finished naturally                         |

***

## Plans

A plan is the template that defines what you charge and how often. Create a plan once and attach as many subscriptions to it as you need.

```json theme={"dark"}
POST /subscription/plan

{
  "merchantCode": "MERCHANT_001",
  "name": "Pro Monthly",
  "amount": 2900,
  "currency": "USD",
  "exponent": 2,
  "interval": "month",
  "intervalCount": 1,
  "trialPeriodDays": 14
}
```

The response includes a plan `id` that you pass when enrolling a customer.

<Warning>
  The `amount` and `interval` fields are **immutable** after a plan is created. If you need to change pricing or billing frequency, create a new plan and migrate existing subscribers using the change-plan endpoint described below.
</Warning>

***

## Creating a Subscription

Enrolling a customer is a Cardholder Initiated Transaction (CIT), and a CIT may require 3D Secure. Pass the card one of two ways:

* **`card.nonceData` / `card.cardData`** — Therius runs the CIT + first charge here. Simple, but this CIT **cannot do a 3D Secure challenge**, so a 3DS-required card fails.
* **`card.tokenData`** — a card that already completed its CIT (with 3DS) via `/payment/authorization` or `/payment/purchase`. Therius records the mandate and, for cycle 1, either charges an MIT or — if you pass `firstPaymentId` (the payment id from that CIT) — links the payment you already made. This is the reliable path for 3DS cards. See the [endpoint reference](/api-reference/subscriptions/create).

Whichever you use, the customer must have been present and consenting to recurring billing when the card was collected.

```json theme={"dark"}
POST /subscription

{
  "merchantCode": "MERCHANT_001",
  "planId": 42,
  "customerEmail": "ada@example.com",
  "customerName": "Ada Lovelace",
  "card": {
    "nonceData": { "nonce": "<fresh nonce>" }
  }
}
```

The response includes the subscription `id`, its initial `status` (`trialing` if the plan has a trial, otherwise `pending` until the first payment clears), the current period start and end dates, and the first invoice object.

<Info>
  However you supply the card — SDK nonce, raw `cardData`, or a `vt_...` token — card scheme rules require the customer to be actively present and consenting when their card is first saved for recurring charges. If you use an SDK nonce it must be fresh; you cannot reuse a nonce from a previous session.
</Info>

***

## Lifecycle Management

After a subscription is active, use the following endpoints to manage it. All endpoints accept `POST` and require your API key.

<CardGroup cols={2}>
  <Card icon="pause" title="Pause">
    `POST /subscription/{id}/pause`

    Suspends renewals immediately. No charges are attempted while the subscription is paused. The billing date is recalculated when you resume.
  </Card>

  <Card icon="play" title="Resume">
    `POST /subscription/{id}/resume`

    Restores a paused subscription (recalculates the next billing date) or re-charges a suspended subscription immediately to bring it back to active.
  </Card>

  <Card icon="x" title="Cancel">
    `POST /subscription/{id}/cancel`

    Permanently cancels the subscription. Therius fires the `subscription.cancelled` webhook event. Cancelled subscriptions cannot be reactivated.
  </Card>

  <Card icon="arrow-right-arrow-left" title="Change Plan">
    `POST /subscription/{id}/change-plan`

    Migrates the subscriber to a different plan. Pass `"applyAt": "now"` for a prorated immediate switch, or `"applyAt": "next_billing"` to change at the end of the current period.
  </Card>

  <Card icon="credit-card" title="Update Payment Method">
    `POST /subscription/{id}/payment-method`

    Replaces the stored card and its mandate. Changing the card is a CIT and may need 3D Secure — either **record** a card you already CIT'd elsewhere (`card.tokenData`, no charge, no 3DS here), or run a zero-value CIT here (`card.nonceData` / `card.cardData`). Only the CIT-mode path can reactivate a `suspended` subscription. See the [endpoint reference](/api-reference/subscriptions/update-payment-method).
  </Card>

  <Card icon="rotate" title="Retry Payment">
    `POST /subscription/{id}/retry-payment`

    Queues an immediate dunning attempt for a `past_due` subscription without waiting for the next scheduled retry.
  </Card>

  <Card icon="calendar" title="Move Billing Day">
    `POST /subscription/{id}/update-billing-day`

    Sets the day-of-month on which renewals are charged. Accepts values 1–28.
  </Card>
</CardGroup>

***

## Usage-based and hybrid billing

On top of a plan's flat `amount`, you can charge for metered consumption — a hybrid of a fixed base fee and a variable usage fee. There are three pieces:

**1. Meters.** A meter is a named counter scoped to your merchant account, e.g. `api_requests` or `data_stored_gb`. Each meter has an aggregation mode that decides how a period's events collapse into one billable quantity:

| Aggregation | Billable quantity for the period      |
| ----------- | ------------------------------------- |
| `sum`       | Sum of every event's `quantity`       |
| `max`       | Highest single `quantity` reported    |
| `last`      | The most recently reported `quantity` |
| `count`     | Number of events reported             |

**2. Per-plan pricing.** Each meter is priced on a plan with a scheme:

* `per_unit` — a flat price per unit.
* `volume` — every unit is priced at the rate of the single bracket the total quantity falls into.
* `graduated` — each bracket prices only the units that fall within its own range.

An `includedUnits` allowance is subtracted before pricing each period.

<Note>
  Meters and their per-plan pricing are managed in the Dashboard under **Subscriptions → Usage Meters**. There is no API for meter or price configuration.
</Note>

**3. Reporting usage.** Throughout the billing period, call [`POST /subscription/usage`](/api-reference/subscriptions/usage) for each usage event:

```json theme={"dark"}
POST /subscription/usage
Idempotency-Key: hourly-rollup-2026-09-02T10:00Z

{
  "subscriptionId": "sub_abc123def456",
  "meterCode": "api_requests",
  "quantity": 500
}
```

Send an `Idempotency-Key` header if your reporting job might retry — a duplicate `(meterCode, Idempotency-Key)` returns the original event. An optional `occurredAt` sets which billing period the event counts toward (it defaults to now).

At each renewal Therius aggregates the period's events, applies the pricing, and adds the result to the invoice. A hybrid invoice carries a `lines` array — one line for the base fee plus one per metered add-on — and the invoice `amount` is their sum. See [View Invoices](/api-reference/subscriptions/invoices#line-items).

***

## Dunning

When a scheduled renewal fails — for example due to insufficient funds or an expired card — Therius automatically moves the subscription to `past_due` and begins the dunning sequence. Retries are spaced according to card scheme retry caps and your merchant configuration.

Once all retries are exhausted, the subscription moves to `suspended` and Therius fires a `subscription.suspended` webhook. See the [webhook events catalog](/webhooks/events) for every subscription event you can subscribe to. To reactivate a suspended subscription, the customer must provide a new payment method:

```json theme={"dark"}
POST /subscription/{id}/payment-method

{
  "card": {
    "nonceData": { "nonce": "<fresh nonce from customer>" }
  }
}
```

Therius immediately attempts a recovery charge. If it succeeds, the subscription returns to `active`.

<Note>
  This reactivation path runs its own CIT and **cannot complete a 3D Secure challenge**. If
  the new card requires 3DS, run a 3DS-capable CIT + recovery charge yourself via
  `/payment/purchase`, then record the resulting token with `card.tokenData` — see the
  [Update Card on File reference](/api-reference/subscriptions/update-payment-method).
</Note>

***

## Parent / Child Subscriptions

You can link subscriptions together by passing `parentSubscriptionId` when creating a child. This is useful for add-ons, seat expansions, or any billing model where multiple line items should share a lifecycle.

```json theme={"dark"}
POST /subscription

{
  "merchantCode": "MERCHANT_001",
  "planId": 99,
  "parentSubscriptionId": "sub_abc123",
  "propagateLifecycle": true,
  "card": {
    "nonceData": { "nonce": "<fresh nonce>" }
  }
}
```

Set `propagateLifecycle: true` to cascade status changes — pause, resume, and cancel — from the parent subscription to all its children automatically. When `propagateLifecycle` is `false` (the default), each subscription manages its lifecycle independently.
