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

# Subscriptions

> A subscription is a recurring billing relationship between a customer and a plan.

When you subscribe a customer to a plan, you're telling us: charge this customer this amount on this schedule, automatically, until further notice. That's a subscription. It handles renewals, state transitions, and collection — you just listen to webhooks and gate access accordingly.

## Lifecycle

```
created
  └─→ trialing ──────────────────────────────→ active
  └─→ active ──→ past_due ──→ grace ──→ delinquent
                    └─→ active (recovered)
  └─→ active ──→ paused
  └─→ active ──→ canceled
```

## State reference

| State        | What's happening                           | Customer access | What's next                                 |
| ------------ | ------------------------------------------ | --------------- | ------------------------------------------- |
| `incomplete` | Strict activation — awaiting first payment | No access       | Activates when the checkout payment settles |
| `trialing`   | Free trial running, no charge yet          | Full access     | Converts to `active` when trial ends        |
| `active`     | All good, payments collecting              | Full access     | Renews at `next_bill_at`                    |
| `past_due`   | First renewal attempt failed               | Full access     | Retry schedule starts                       |
| `grace`      | All retries exhausted, grace window open   | Full access     | Grace expires → `delinquent`                |
| `delinquent` | Grace expired, no payment                  | No access       | Customer needs to pay to restore            |
| `paused`     | Manually paused                            | No access       | Resume when ready                           |
| `canceled`   | Ended                                      | No access       | Final state                                 |

During `past_due` and `grace`, the customer still has access — cutting them off immediately for one failed payment loses customers unnecessarily. Once `delinquent`, access is cut until they pay.

## Two billing rails

**Card billing** — You tokenise the customer's card once via Nomba, pass the token when creating the subscription. On each renewal, we charge the card automatically.

**Transfer billing** — The customer has a dedicated virtual account number (bank name + account number). On renewal, instead of charging a card, we open an invoice and wait for the customer to send a bank transfer. When it arrives, we match it automatically and close the invoice.

Set the rail on subscription creation:

```bash theme={null}
# Card billing
{
  "payment_method": "card",
  "payment_method_id": "tok_abc123"
}

# Transfer billing
{
  "payment_method": "transfer"
}
```

## Create a subscription

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.useplinth.com/v1/subscriptions \
    -H "Authorization: Bearer sk_test_DOCS_SHARED_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: create-sub-acme-pro-jun26" \
    -d '{
      "customer_id": "cus_01JXYZ...",
      "plan_id": "plan_pro_01...",
      "payment_method": "card",
      "payment_method_id": "tok_test_visa"
    }'
  ```

  ```typescript Node.js theme={null}
  const subscription = await client.subscriptions.create({
    customerId: 'cus_01JXYZ...',
    planId: 'plan_pro_01...',
    paymentMethod: 'card',
    paymentMethodId: 'tok_test_visa',
  }, {
    idempotencyKey: 'create-sub-acme-pro-jun26',
  });
  ```
</CodeGroup>

```json Response theme={null}
{
  "id": "sub_01JABC...",
  "customer_id": "cus_01JXYZ...",
  "plan_id": "plan_pro_01...",
  "state": "active",
  "payment_method": "card",
  "billing_mode": "advance",
  "current_period_start": "2026-06-01T00:00:00Z",
  "current_period_end": "2026-07-01T00:00:00Z",
  "next_bill_at": "2026-07-01T00:00:00Z",
  "cancel_at_period_end": false,
  "canceled_at": null,
  "has_card": true,
  "scheduled_change": null,
  "created_at": "2026-06-01T00:00:00Z"
}
```

<ResponseField name="cancel_at_period_end" type="boolean">
  `true` when the subscription is set to cancel at period end (still active until then). Toggle with
  [cancel](/api-reference/cancel-subscription) / [reactivate](/api-reference/reactivate-subscription).
</ResponseField>

<ResponseField name="has_card" type="boolean">
  Whether a card token is on file. Transfer-funded subscriptions have none, so renewals need manual
  payment — a cue to nudge the customer to add a card.
</ResponseField>

<ResponseField name="scheduled_change" type="object | null">
  A pending period-end plan change (e.g. a scheduled downgrade): `{ id, new_plan_id, new_quantity,
      scheduled_for }`. The current plan runs until `scheduled_for`, then switches.
</ResponseField>

## One subscription per customer

By default a customer can have **one live subscription per plan-group**. Creating a second while one
is still live (any non-`canceled` state) returns `409 customer_already_subscribed` with the existing
subscription's id — reuse it (change its plan, or cancel it) rather than creating a duplicate:

```json 409 theme={null}
{ "error": { "code": "customer_already_subscribed",
  "message": "Customer already has a live subscription (sub_01JABC...) in this plan group.",
  "existing_id": "sub_01JABC..." } }
```

Integrators that genuinely need concurrent subscriptions can set `allow_multiple_subscriptions: true`
in their [policy](/presets/overview).

<Note>
  Use idempotency keys on subscription creation. If your request times out and you retry, the same key returns the original response instead of creating a duplicate subscription.
</Note>
