Skip to Content
CheckoutAPI ReferenceSubscription CheckoutsOverview

Subscription Checkouts

A subscription checkout is a single-use intent that lets a customer authorize a recurring stablecoin subscription. It mirrors the one-shot Checkout flow: the merchant creates an intent on the server, redirects the customer to a hosted subscribe page, and receives webhooks when the customer signs and the first charge confirms on-chain.

Why intents

The subscription plan (price, feature gating, billing day) lives on the merchant side. Exodus only records the per-subscriber intent and captures the resulting on-chain state. One intent maps to one subscriber and one resulting subscription — same lifecycle as one-shot Checkout.

This means there are no reusable /subscribe/:planId URLs in v1: every subscribe flow starts with a fresh merchant-server call to POST /subscription-checkouts.

Atomic first charge

When the customer signs at the hosted page, the on-chain subscribeAndCharge transaction registers the subscription and performs the first charge in a single transaction. The intent never transitions to completed without a confirmed first charge — there is no “subscribed but not charged” state.

Lifecycle

StatusMeaning
pendingIntent created; no on-chain subscribe yet.
completedCustomer signed subscribeAndCharge; first charge confirmed; Subscription materialized.
cancelledMerchant called PATCH /subscription-checkouts/:id/cancel, or customer dismissed the hosted page.
expiredexpires_at elapsed without a successful subscribe. Default expiry is 5 minutes; maximum is 24 hours.

Race resolution — on-chain truth wins

If a subscribeAndCharge transaction is already in the mempool when the merchant cancels (or when the cron sweeper marks the intent expired), and that transaction later confirms on-chain, the indexer flips the intent back to completed and dispatches a subscription_checkout.completed webhook.

Merchants must handle late subscription_checkout.completed webhooks that arrive after a .cancelled or .expired event. The latest webhook is authoritative.

Chain identifiers (CAIP-2)

The subscription surface uses CAIP-2 chain identifier strings throughout — eip155:1 for Ethereum mainnet, eip155:137 for Polygon, etc. The merchant publishes supported_chains: string[] at intent creation; the customer picks one at sign time based on their wallet balances.

V1 supports the eip155: namespace only (EVM chains).

CAIP-2Network
eip155:1Ethereum mainnet
eip155:137Polygon
eip155:42161Arbitrum One
eip155:8453Base
eip155:10Optimism

Amounts, budget, and cap

The merchant prices the plan in a fiat currency at intent creation. A crypto-settled business may use any currency its FX source can quote; a fiat-settled business must use its own settlement currency. The subscriber picks the settlement stablecoin (and chain) at subscribe time — never the merchant, and never at creation.

Every fiat amount is an integer in hundredths of the currency unit. price, cap, and budget all follow this rule, so "150000" is 1,500.00 ARS and "9999" is 99.99 ARS. Send them as strings, with no decimal point and no thousands separator. Every currency the API accepts today has two decimal places, so the conversion is always the amount multiplied by 100.

FieldMeaning
priceRequired. The recurring per-cycle amount, in price_currency minor units (e.g. "150000" = 1,500.00 ARS).
price_currencyRequired. ISO-4217 currency code, which must be quotable by the business’s FX source. For a crypto-settled business that source is the Exodus pricing server, which quotes ARS only today; anything else is rejected with 422 price_currency_not_quotable. A fiat-settled business quotes through its settlement provider instead, so it must have a complete fiat settlement configuration (422 fiat_settlement_required) and must price in its own settlement currency (422 currency_mismatch) — the quotable set there is whatever that provider supports, currently ARS.
budgetOptional. The per-cycle spend ceiling — the maximum total that can be charged within a single billing window (supports multiple metered charges per cycle), in price_currency minor units. Defaults to 10x price when omitted.
capOptional. Per-charge maximum — the most a single charge can pull, in price_currency minor units. Defaults to budget (provided or derived) when omitted.
period_durationSeconds between scheduled charges. Minimum 1 hour. Day-of-month and other calendar billing is the merchant’s responsibility — schedule the API call when desired.

price, cap, and budget all share price_currency’s minor units, so the ordering always holds: price <= cap <= budget. budget bounds the total pulled per billing window; cap bounds any single charge. At subscribe time, the quote converts all three to the settlement token’s smallest units at one rate, rounding up — the subscriber signs those converted amounts, not the fiat figures directly. A cycle whose charges would exceed the converted budget, or a single charge above the converted cap, reverts on-chain — never a silent over-pull. Headroom above price accommodates later price increases without re-subscribing.

subscription_options enumerates every stablecoin configured on every supported chain the subscriber can pay with — always iterate it as an array, never assume a single entry. It’s present only on this direct API response (POST/GET /subscription-checkouts), not on any subscription_checkout.* webhook — by the time a webhook fires, the subscriber has already chosen, and that choice shows up on data.subscription instead. See List subscription checkouts for an example payload.

🔐

The customer’s wallet approves the on-chain contract for uint256.max allowance at subscribe time (industry standard pattern matching Uniswap, Aave, etc.). budget and cap enforcement happens in the contract, not the ERC-20 allowance.

Hosted pages

Two hosted pages live on checkout.exodus-int.com:

  • https://checkout.exodus-int.com/subscribe/:subscription_checkout_id — the subscribe page returned in checkout_url. The customer connects a wallet, picks a chain, optionally signs an EIP-2612 permit, and signs subscribeAndCharge.
  • https://checkout.exodus-int.com/cancel/:subscription_id — a customer-facing cancel page keyed on the on-chain subscription_id. The customer connects the subscribing wallet and signs cancel(subscriptionId) directly. Surface this URL in your customer account UI for self-service cancellation.

Both pages pick up your branding colors automatically.

Available Endpoints

After the customer signs, the resulting on-chain subscription is managed via the Subscriptions endpoints.

Start building

XO

Request Demo

Schedule a call with our team

Select a product
Arrow right

Start building
Grateful

Contact Us

We're here to help