Skip to main content
Subscriptions let you charge a customer on a recurring interval — daily, weekly, monthly, or yearly — against a fixed price, without building your own billing scheduler. The model has two parts:
  1. A subscription offering — a reusable, customer-agnostic “plan” built from one of your catalog prices. It has its own checkout link.
  2. 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 a RECURRING-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.
The response includes a 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.
There’s no way to create a subscription straight from a priceId — you always go through an offering first, even for a one-off direct API integration.

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

Both support filtering by status and cursor pagination (limit, cursor).

Canceling a subscription

Cancellation is immediate and idempotent — calling it again on an already-canceled subscription is a no-op. Any not-yet-collected charge for the current period is abandoned; there’s no partial refund or proration of the current period.
There’s currently no pause/resume, and no plan-change or upgrade/downgrade endpoint. To move a customer to a different price, cancel their subscription and create a new one against a different offering.

Status lifecycle

string
One of:
  • active — billing normally.
  • completed — reached the offering’s totalPeriods and 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 to failed 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):
These events are not yet available as webhooks — they aren’t in the webhook-eligible event list (see Webhooks). If you need to react to subscription billing outcomes today, you’ll need to poll GET /subscriptions or GET /subscriptions/{id}. Let us know if webhook support for these events would unblock your integration.

Authentication and idempotency

Subscription endpoints use the same Authorization: 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.