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

> ## Agent Instructions
> Money is always a decimal string at the currency's precision (for example "29.00"), paired with an ISO 4217 currency field. Never use minor units.
> Build against the sandbox first: base URL https://sandbox-api.bachs.io with sk_sandbox_ keys. Production is https://api.bachs.io with sk_live_ keys; going live is a key swap.
> Treat webhooks (for example collection.succeeded) as the source of truth for fulfilment, never client-side events or redirects.
> Subscriptions are created by completing a checkout for a recurring product. There is no direct create-subscription endpoint.
> IDs carry resource prefixes (cust_, prod_, sub_, chk_, inv_, ref_) and timestamps are ISO 8601 UTC.

# Subscription lifecycle and events

> Every stage of a subscription from checkout to cancellation, the status it moves to, and the webhook that tells you.

A subscription moves through a few statuses over its life, and Bachs sends a webhook at every step. This page puts them in order, so you know which event to expect at each stage and what your app should do with it. Access in your app should follow the subscription's `status`, and the webhooks are how you learn it.

![The subscription statuses and how one moves between them: checkout starts trialing or active, a failed renewal makes it past\_due, a retry or new card returns it to active, and when retries run out it becomes canceled, or unpaid if set, from which a later payment returns it to active](https://mintcdn.com/bachs/tAleFDe9Pc2hTEv3/images/product/subscription-lifecycle.webp?fit=max\&auto=format\&n=tAleFDe9Pc2hTEv3\&q=85\&s=fbcfb8984eafe0a19d78940f65a74aa4)

## Stage by stage

**1. The customer subscribes**

A subscription starts when a customer completes a checkout for a product with a `billing_cycle`. There is no call to create one directly. You receive [`customer.subscription.created`](https://docs.bachs.io/guides/webhooks/events/customer-subscription-created) with `status: "active"`, or `"trialing"` if the product has a [free trial](https://docs.bachs.io/guides/subscriptions/trials.md).

**Your app:** save the subscription and its `customer_id`, and give access.

**2. The trial ends**

Bachs charges the card saved at checkout for the first cycle. You receive [`invoice.paid`](https://docs.bachs.io/guides/webhooks/events/invoice-paid) and the subscription becomes `active`, or [`invoice.payment_failed`](https://docs.bachs.io/guides/webhooks/events/invoice-payment-failed) and recovery starts. Adding, extending or ending a trial sends [`customer.subscription.updated`](https://docs.bachs.io/guides/webhooks/events/customer-subscription-updated).

**Your app:** nothing changes for the customer if the charge succeeds.

**3. It renews**

At the end of each period, Bachs moves the subscription to the next cycle, opens an invoice and charges the saved card. You receive [`invoice.created`](https://docs.bachs.io/guides/webhooks/events/invoice-created) when the invoice opens, then `invoice.paid` when it is collected.

**Your app:** optionally record the payment or send your own receipt. Access carries on.

**4. A renewal fails**

The subscription moves to `past_due` and Bachs retries the charge three times: 1 day after the failure, 3 days after that, and 5 days after that. Each failed attempt sends `invoice.payment_failed`, and Bachs emails the customer a link to a hosted page where they can update their card. If a retry or a new card succeeds, the invoice is paid, the subscription is `active` again, and you receive `invoice.paid`.

**Your app:** optionally ask the customer to update their card. Whether they keep access while retries run is your choice.

**5. Recovery runs out**

If the third retry fails, your subscription settings decide what happens. By default the subscription is `canceled` with the reason `payment_failed`, and you receive [`customer.subscription.deleted`](https://docs.bachs.io/guides/webhooks/events/customer-subscription-deleted). If you chose to keep unpaid subscriptions, it moves to `unpaid` instead, you receive `customer.subscription.updated`, and a later payment can still bring it back. See [Recover failed payments](https://docs.bachs.io/guides/subscriptions/failed-payments.md).

**Your app:** remove access.

**6. It changes**

A plan change, a new seat count, a trial change, a new card or a cancellation scheduled for the period end each send `customer.subscription.updated`. See [Change or cancel a subscription](https://docs.bachs.io/guides/subscriptions/manage.md) and [How proration works](https://docs.bachs.io/guides/subscriptions/proration.md).

**Your app:** save the new state. Access follows `status`.

**7. It's canceled**

Canceled at the period end, the subscription keeps working until `current_period_end` with `cancel_at_period_end: true`, then becomes `canceled`. Canceled immediately, it is `canceled` at once. Either way, you receive `customer.subscription.deleted` when it is `canceled`. That status is final: a later payment can't revive it, and the customer starts a new subscription.

**Your app:** remove access when you receive `customer.subscription.deleted`, not when the cancellation is scheduled.

## Access by status

| `status` | Being charged? | Give access? |
| - | - | - |
| `trialing` | Not yet. The first charge comes when the trial ends. | Yes |
| `active` | Yes, each cycle. | Yes |
| `past_due` | Bachs is retrying a failed renewal. | Your choice. The [SaaS starter](https://docs.bachs.io/build/use-cases/saas-subscriptions.md) keeps access while retries run. |
| `unpaid` | No. Retries ran out, and your account keeps unpaid subscriptions. | Usually no. A later payment makes it `active` again. |
| `canceled` | No, ever again. | No |

## Handling the events well

* **Decide access from the webhook, never from the checkout redirect.** The customer can close the tab before your success page loads, and anyone can open the URL.
* **Save the status and read it from your own database.** Each `customer.subscription.updated` carries the full subscription, so save it whole rather than applying a diff.
* **Deduplicate by event ID.** Each event's `id` is unique; a retried delivery repeats it. See [Set up webhooks](https://docs.bachs.io/guides/webhooks/overview.md#retries).
* **Verify the signature** of every delivery before you act on it. See [Verify your webhooks](https://docs.bachs.io/guides/webhooks/overview.md#verifying-your-webhooks).

## Next steps

* [Sell SaaS subscriptions](https://docs.bachs.io/build/use-cases/saas-subscriptions.md): the whole loop, built end to end in the sandbox.
* [Recover failed payments](https://docs.bachs.io/guides/subscriptions/failed-payments.md): the retry schedule and the recovery email in detail.
* [Set up the customer portal](https://docs.bachs.io/guides/customer-portal/overview.md): let customers change plans, cards and cancellations themselves.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.