- Create a product.
- Add one or more prices.
- Attach the product to a hub checkout display.
- Start checkout as a member.
- Webhooks update orders, subscriptions, payments, and entitlements.
Admin setup
Products are team-scoped:Member checkout
The member-facing checkout route starts a hosted checkout session:checkout_session_statuses resource carries payment_status and status. Once the session is complete and this hub holds an order for it, it also carries email: the buyer’s address with the local part cut to its first character (jane.doe@example.com comes back as j***@example.com), enough to say “we sent a link to j***@example.com”. email is null when the address can’t be resolved, and it never contains the full address. It comes only from the order this hub holds for that session, never from Stripe. For a session that hasn’t completed, or one this hub has no order for, the key is left out, so treat a missing email the same as null.
After payment, use the member billing routes:
When a renewal payment fails
When a renewal payment fails, Stripe keeps retrying it for a while and the subscription’sstatus is past_due. The member keeps access during that time. Each subscriptions resource tells you when to ask the member to update their payment method:
payment_update_neededistruewhile the status ispast_dueand payment collection is not paused. A paused collection is the hub owner’s choice and leaves the member nothing to fix, so it readsfalse. Once the payment goes through, the status returns toactiveand the flag tofalse. When the retries run out and Stripe moves the subscription on (tounpaidorcanceled, depending on the account’s retry settings), the flag isfalsetoo, by design: the prompt covers the retry window only, and by then the member’s access has ended, whichstatusshows.last_payment_failed_at,last_payment_failed_amount(in the currency’s minor units) andlast_payment_failed_currencydescribe the latest failed attempt. They arenullunlesspayment_update_neededistrueand the attempt is not older thancurrent_period_start: an older attempt belongs to an earlier renewal that was settled. There is no upper bound. An attempt aftercurrent_period_endis a later renewal that failed before the subscription’s new period was recorded, and the member still owes it. The details can also benullwhilepayment_update_neededistrue, when no failed attempt is on record yet. Amount and currency arenulltogether.
payment_update_needed as false and missing details as null.
Send the member to the billing portal to update their card:
return_url must be on an origin the hub allows, and must not contain a # fragment. While the hub’s billing is being migrated, this route returns 409 cutover_not_open (see below).
Errors to handle
409 cutover_not_open
A team’s billing can be in the middle of a migration. While it is, these actions are unavailable and return 409 with the error code cutover_not_open:
- Starting or resuming checkout (
POST /api/v1/hub/{hub_id}/checkout), including replaying a request that already completed under the sameIdempotency-Key. - Opening the billing portal (
GET /api/v1/hub/{hub_id}/me/billing-portal-link). - Cancelling a subscription, as an admin or as the member.
- Refunding a payment.
- A member deleting their account (
POST /api/contact-auth/me/hubs/{hub_id}/delete-account) while they have a live subscription. - Deleting a product or a price, detaching a product from a hub, and adopting products from Stripe (
POST /api/v1/teams/{team_id}/products/adopt-from-stripe).
GET /api/v1/me/billing-portal-links leaves out the entry for a team in this state rather than failing. Creating and editing products, prices and coupons keeps working.
The same applies to the MCP tools. start_test_checkout fails with a cutover_not_open tool error, and get_checkout_link reports a cutover_not_open blocker instead of a link.
503 checkout_deadline_exceeded
Starting or resuming checkout has a 120-second limit. If the limit passes before the payment provider is called, the request returns 503 with the error code checkout_deadline_exceeded and no checkout session is created. It is safe to retry. If you send the same Idempotency-Key again, checkout resumes the earlier attempt instead of starting a new one (see Idempotency). If the payment provider does not answer within 60 seconds, the request returns the usual 503 payment_provider_error, and you can retry the same way.
The MCP tool start_test_checkout has the same limit: past it, the tool fails with a checkout_deadline_exceeded tool error (status 503), and you can retry it the same way.