Manage Subscriptions
Activate, list, inspect, and cancel MPP subscription delegations.
A subscription in pay is an MPP subscription delegation: an on-chain authority that lets a specific server (puller) pull a fixed stablecoin amount from your account on a recurring schedule. Once activated, the server charges you on its own — you don't sign each renewal.
That convenience means active subscriptions can drain your wallet quietly. Audit them with pay subscriptions list regularly.
How a subscription gets activated
You don't usually run pay subscriptions new to start one — activation happens automatically as a side effect of a paid request.
pay --sandbox curl https://example.gateway/v1/premium- The server returns
402 Payment Requiredwith an MPPsubscriptionchallenge: a Plan PDA, a mint, a recipient, a per-period amount, and a period (e.g.30d). paydecodes the challenge, builds an activation transaction signed by the active account, and retries the request with the credential.- The server verifies the on-chain
SubscriptionDelegation, returns the response, and includes aPayment-Receiptheader with the newsubscription_id(the PDA in base58). paypersists the subscription locally under that account in~/.config/pay/accounts.yml, so subsequentpay subscriptions listcalls show it.
From here on, the server pulls funds on its own each period. No further action on your side until you want to inspect or cancel.
List your subscriptions
pay subscriptions # default: list + show available subcommands
pay subscriptions list # same, scriptable
pay subscriptions list --account work
pay subscriptions list --network mainnet
pay subscriptions list --jsonlist walks every account in accounts.yml, dereferences each subscription's local record, and prints the schedule, recipient, amount per period, status, and base58 subscription_id. Use --account or --network to scope.
Inspect a single subscription
pay subscriptions status <subscription_id>
pay subscriptions status <subscription_id> --jsonThe subscription_id is the SubscriptionDelegation PDA — the same base58 string list shows, and the one returned in the server's Payment-Receipt header at activation time.
status prints the plan, mint, puller, recipient, amount per period, period unit, activation signature, and current cancellation state.
Backfill on-chain data
If the local record is missing a field (e.g. activation_signature because the call was interrupted), reconcile it from the chain:
pay subscriptions refresh
pay subscriptions refresh --account work --network mainnetrefresh reads the on-chain SubscriptionDelegation for each local entry and fills in any gaps. Read-only — never mutates on-chain state.
Cancel a subscription
pay subscriptions cancel <subscription_id>Cancellation flips the subscription's local state to cancelled and broadcasts an on-chain cancel transaction so the puller can no longer charge you. Two paths reach the same final state.
Direct (requires SOL)
Default path. The subscriber signs and pays the Solana network fee from its own SOL balance. Fast and simple — but the account needs at least 50,000 lamports (~0.00005 SOL) on hand.
Via the pay-api gateway (no SOL needed)
If the subscriber has less than 50,000 lamports, pay automatically routes through the pay-api gateway. The gateway returns a 402 charge challenge, you settle a small stablecoin fee, and the gateway broadcasts the cancel on your behalf as fee-payer.
Force the gateway path even when you have enough SOL:
pay subscriptions cancel <subscription_id> --via-gateway https://pay-api.gateway-402.com| Flag | Description |
|---|---|
--via-gateway <URL> | Force routing through the named pay-api gateway. Auto-applied when subscriber SOL < 50,000 lamports. |
--gateway-fee-payer <BASE58> | Gateway operator pubkey signing as fee-payer. Required when --via-gateway is set. Defaults to $PAY_API_FEE_PAYER. |
--rpc-url <URL> | RPC override. Defaults to $PAY_RPC_URL or the canonical RPC for the subscription's network. |
Local-only cancel
If the on-chain side is already cancelled out of band (e.g. you closed the SubscriptionAuthority directly), skip the broadcast and just clean up the local record:
pay subscriptions cancel <subscription_id> --local-onlyManual activation (advanced)
You normally don't run this — the 402 flow above is the user-facing path. Use subscriptions new when you already have the Plan PDA from another source and want to activate without making a request to the server.
pay subscriptions new \
--plan <PLAN_PDA_BASE58> \
--mint <USDC_MINT_BASE58> \
--puller <SERVER_PULLER_BASE58> \
--recipient <WALLET_BASE58> \
--amount 1000000 \
--period 30dEvery required flag corresponds to a field the on-chain program checks. --amount is in mint base units (1 USDC = 1,000,000 base units). --period accepts forms like 30d or 2w; month is rejected per the Solana subscription profile. See Commands → subscriptions new for the full flag list.
Where subscriptions live on disk
Each account's row in ~/.config/pay/accounts.yml has an optional subscriptions: map keyed by subscription_id:
version: 2
accounts:
mainnet:
work:
keystore: apple-keychain
pubkey: 96WoyH3JmANSMsQLGC3MKyiGiXCymZyM9SLaWjcRrKuD
subscriptions:
4xZk…:
plan: 7nP…
mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
amount: '1000000'
period_count: 30
period_unit: day
status: active
activation_signature: 5Hk…pay subscriptions refresh is what reconciles this map with the chain. If you pay account remove <name>, the subscription entries go with it — but the on-chain authority survives until you cancel each one.
See also
- Commands → subscriptions — full flag reference.
- Manage Accounts — switching accounts and moving stablecoins between them.
- Pass-through commands — making the paid request that triggers activation.