- A subscription offering — a reusable, customer-agnostic “plan” built from one of your catalog prices. It has its own checkout link.
- A subscription — one customer’s enrollment in an offering, tied to a specific payment instrument.
Subscriptions are fixed-amount only. There’s no usage-based or metered billing today, and pricing is fully snapshotted from the offering at creation time — changing or archiving the underlying price never affects subscriptions that already exist.
Creating an offering
An offering wraps aRECURRING-type catalog price into something customers can subscribe to.
Why
priceId and not productId here? A price already points at exactly one product, so the offering endpoint only needs the price — it looks up the product through it rather than asking you to pass both. In other words, productId isn’t a separate input to this call; it’s metadata the price carries along and the offering inherits.string
required
string
required
A catalog price with
type: "RECURRING" and a valid interval (day, week, month, or year). The offering snapshots the price’s amount, currency, interval, and retry policy at creation time.integer
Optional. If set, subscriptions created from this offering run for a fixed number of billing periods and then move to
completed automatically. Omit it for an open-ended subscription that bills until canceled.checkoutUrl built from the offering’s slug — anyone who completes checkout at that URL mints their own subscription against this offering, without you calling POST /subscriptions yourself. This is the simplest way to sell a recurring plan without building your own checkout.
Creating a subscription directly
If you’re collecting payment details yourself (rather than using the hosted checkout link), enroll a customer directly against an existing offering:string
required
string
required
string
required
The offering to enroll the customer in — not a raw price. Create an offering first (see above).
string
required
A previously-vaulted payment instrument to charge on each billing cycle.
Response
The first period is due immediately at creation, but if you created the subscription via the direct API, that first charge is collected on the next daily billing run — not synchronously in the create response. If a customer subscribes through the hosted checkout link instead, the first charge is collected synchronously as part of checkout, so the page can show a result immediately.
Listing and fetching subscriptions
status and cursor pagination (limit, cursor).
Canceling a subscription
Status lifecycle
string
One of:
active— billing normally.completed— reached the offering’stotalPeriodsand finished successfully. Only applies to fixed-duration offerings.failed— the retry policy was exhausted after a payment failure. Terminal; there’s no reactivation flow.canceled— you (or the customer) canceled it.
There’s no
trialing, past_due, paused, or incomplete status. If you’re porting logic from another billing platform, map any “grace period” handling to watching for SubscriptionFailed instead of a distinct status.Billing and retries
Charges are collected by a daily billing run, not in real time as periods roll over. Each subscription bills on an anchor schedule derived from its interval — for month/year intervals, a day that doesn’t exist in a shorter month clamps down and then reverts in later months (e.g. a Jan 31 anchor bills Feb 28, then Mar 31). If a charge attempt fails, it’s retried according to the offering’s retry policy (default: 3 attempts, 1 day apart). If every attempt fails, the subscription moves tofailed and a subscriptions.SubscriptionFailed event is published — there’s no further automatic retry after that.
An ACH return on an already-succeeded charge is handled separately from a decline: it reopens that period’s billing so a fresh attempt cycle runs (with a full new retry budget), rather than immediately failing the subscription.
Events
Subscription lifecycle events are published as integration events (via the transactional outbox):Authentication and idempotency
Subscription endpoints use the sameAuthorization: Bearer API key as the rest of the Merchant API. POST /subscriptions and POST /subscriptions/offerings both require an Idempotency-Key header — a retried request with the same key returns the original record rather than creating a duplicate. PUT /subscriptions/{id}/cancel doesn’t need one, since cancellation is idempotent by nature.