Skip to main content
This page covers recurring billing. Orders, Sessions, and webhooks work exactly as they do for one-time payments, so read Hosted Checkout first if you have not.

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 yourself. 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 — your 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 you create the Session, 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 your merchant and support the paymentCurrency you send.
Whether a given payment method works for subscriptions also depends on your channel configuration and currency. Verify each method your 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 your own 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. First payments and renewals need subscription.activated, invoice.paid, and query results, applied idempotently.Entitlement changes belong on your own subscription and invoice records, not on a single order row — a subscription is an ongoing relationship and one order number cannot hold it.

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. Your 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. Both steps require quantity. Step one — preview
Step two — confirm with the priceSnapshotId from the preview
If you use a promotion code, 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.
The current API Reference request examples for preview and confirm omit quantity, but the server requires it. Follow this page and send it in both calls.

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 you will not need 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.