Skip to main content
This page covers recurring billing. Orders, Sessions, and webhooks work exactly as they do for one-time payments, so Hosted Checkout is worth reading first.

Pick a path

Three situations, three different endpoints:
POST /checkout/session and POST /subscription are not two spellings of the same call. The first gives the customer an interactive checkout; the second is for a server that already has both a customer and a payment instrument.

1. Prepare a recurring product and price

Subscriptions require a pre-registered Product and a recurring price. The inline priceDataList approach cannot create one.
interval accepts day, week, month, year, quarter, half_year, and custom.
quarter and half_year are resolved by the platform — do not convert them into 3 or 6 months. intervalCount only means a number of days when interval is custom.
Examples on this page use a monthly flat_rate price. For other pricing models, see the field reference in Create price. The shape is identical to a one-time payment — the backend creates the Session, the frontend opens checkout, webhooks update local state. Only the item changes, from an inline product to a recurring price.
Amount and currency are still required, and the backend checks them against the Product and Price.
At the moment the Session is created, no subscription exists and no money has been collected.Once the customer submits payment, checkout follows the recurring price and produces a Subscription, an Invoice, and an Order. Returning to successUrl only means the interaction ended — read the final state from the API and from signature-verified webhooks.

3. Server-side: create the subscription directly

Use this when the customer already has a Clink Customer record and a usable payment instrument.
Supply at least one of customerId, customerEmail, or referenceCustomerId. The recurring price must belong to the current merchant and support the paymentCurrency sent.
Whether a given payment method works for subscriptions also depends on channel configuration and currency. Verify each method the account has actually enabled rather than assuming a shared list.
The numeric status in the response describes this payment attempt: The response also returns subscriptionId, sessionId, paymentInstrumentId, orderId, and invoiceId. Store all five on the local subscription record — one field alone will not be enough for later queries and reconciliation.

4. Mapping subscription status to access

This is where subscription integrations most often go wrong. Payment status and entitlement status are two different things.
Do not use subscription.created as the signal to grant paid access. It only means the subscription record exists.Free trials are driven by subscription.trialing. Starting paid access and renewing it are two different signals — do not conflate them:subscription.activated means the subscription entered or returned to active, not “first activation only”. Moving from incomplete, free_trial, or past_due into active all fire it. An ordinary active to active renewal fires only subscription.updated.renewed and does not repeat activated.Getting this wrong in either direction hurts: watch only activated and renewals will never fire it, so access looks expired at the end of the second period; watch only renewed and both dunning recovery and the trial-to-paid transition are missed.Entitlement changes belong on the local subscription and invoice records, not on a single order row — a subscription is an ongoing relationship and one order number cannot hold it. Make both activation and renewal idempotent on invoiceId, so a given period is fulfilled exactly once.

5. Events to subscribe to

The order.* events from one-time payments still apply. Subscriptions add these. Lifecycle subscription.created, subscription.trialing, subscription.activated, subscription.past_due, subscription.incomplete_expired, subscription.cancelled Plan changes subscription.updated.plan_changed, subscription.updated.plan_change_canceled, subscription.updated.renewed, subscription.updated.cancel_at_period_end_set, subscription.updated.cancel_at_period_end_revoked Invoices invoice.open, invoice.paid, invoice.void
Subscribe with full event names. Wildcards like subscription.* and invoice.* are not values the API accepts.
Events are retried and arrive out of order. The handler must at least:
  • Deduplicate atomically on event.id
  • Resolve out-of-order arrivals by subscription status precedence, so an older event cannot overwrite a newer state
  • Run fulfillment for a given invoiceId exactly once
Signature verification, deduplication, and order matching are covered in Hosted Checkout.

6. Cancellation

reason is required, 1 to 255 characters. cancelReasonCode is optional and accepts too_expensive, need_more_features, found_alternative, no_longer_needed, poor_customer_service, poor_usability, poor_quality, and other_reasons. Omitting cancelImmediately or sending false cancels at the end of the current period. Sending true cancels immediately.
Cancelling is not refunding. An immediate cancellation does not return money already collected for the current period. Refunds go through the refund API separately.

7. Plan changes

Preview first, then confirm. Step one — preview
Step two — confirm with the priceSnapshotId from the preview
When a promotion code is used, send the same code in both calls or the amounts will not line up. immediate: true may produce a prorated charge and return an action. false schedules the change for the next billing boundary. A pending change can be withdrawn with POST /subscription/{id}/update/cancel.
quantity is the number of units of the target price — optional, an integer, minimum 1. Omit it and the server uses 1; an explicit null or a value below 1 returns a parameter error. The examples still spell it out so the quantity is visible at a glance.

8. Scheduled phases

scheduledPhases lets a subscription switch plans automatically at future renewal boundaries, up to 10 phases, with sequence and effectiveCycle both starting at 1 and strictly increasing. This is an advanced capability, not needed for a first integration. Field details are in Checkout Session.

Next

Discounts and promotion codes

Adding discounts, and how many periods they last.

Go live

Subscription checks before production.