FEATURES
Subscriptions
Recurring stablecoin charges. Publish a plan, share a link, the subscriber approves the router once. Tab's executor handles every charge after that. Useful for paid Discord communities, creator memberships, SaaS billing, and recurring tips.
How it works
- Creator publishes a plan. From
/dashboard/subscriptions, you set name, price, period (daily / weekly / monthly / quarterly / yearly) and chain. One tx publishes it onTabSubscriptionRouter. - Subscriber visits the share link. At
/subscribe/[planKey]they see the plan card and tap subscribe. Their wallet signs two txs: anapprove()for the lifetime allowance and onesubscribe()that charges the first period. - Tab cron charges each period.
POST /api/cron/chargewalks active subs and submits the contract'scharge(planKey, subscriber). The contract enforceslastChargedAt + period <= nowso double-charging is impossible. - Subscriber can cancel anytime. One on-chain tx flips the sub to inactive. Allowance revocation or zero balance also halts charges. Tab catches the revert and marks the sub
halted.
Trust model
- Funds never leave the subscriber's wallet until each charge fires. There's no escrow and no custody by Tab.
- The router can never increase the price. Plan
amountandperiodare immutable on-chain, to change pricing, the creator publishes a new plan and migrates subscribers. - Anyone can call
charge.The schedule, allowance check, and active-sub check live in the contract, so a malicious caller can't over-charge, only settle a legitimately-due period. - Protocol fee. Business-workspace plans take a 1% fee per charge; personal-workspace plans are 0%. Net goes to the creator.
Plan lifecycle
pending_create: staged off-chain, on-chain createPlan tx in flight.active: accepting new subscribers and charges.deactivated: no new subscribers; existing subscribers can still cancel but the cron won't advance their charges.
Subscription lifecycle
active: being charged each period.cancelled: subscriber or creator stopped it. No further charges; allowance left in place until the subscriber revokes manually.halted: last cron pass reverted (allowance or balance insufficient). The dashboard surfaces this so the creator can ping the subscriber to top up + resume.
Webhooks
Each lifecycle transition fires a webhook to any subscription registered for it:
subscription.plan_createdsubscription.startedsubscription.chargedsubscription.cancelledsubscription.halted
Gasless plan deactivate / reactivate
Creators pause and resume plans without holding native gas. POST /api/subscriptions/manageroutes the call through the creator's existing 7702 tipping delegation — the delegation's AllowedMethods caveat was extended to whitelist TabSubscriptionRouter.deactivatePlan(bytes32) and reactivatePlan(bytes32)(in addition to ERC-20 transfer). Tab's executor submits the tx, the router sees msg.sender == plan.creator via the 7702 authority chain, and the plan flips state. No new contract deploy was needed; this is purely a delegation caveat extension.
Users whose delegations pre-date the extension (signed before the caveat update) get a clean fallback: the endpoint returns 409 delegation_legacyand the dashboard signs the deactivate / reactivate tx with the user's key directly, same as before. They can re-enable gasless tipping at /dashboard/tabbot to upgrade.
Solana plans
Subscriptions on Solana use a dedicated Tab Subscriptions Program (separate program IDs for the 0%-fee Personal tier and the 1%-fee Business tier). The dashboard's "new plan" flow handles both: pick Solanaas the chain and the creator's plan is published through Tab's fee-payer relay so the publisher never spends SOL. Subscribers pay USDC under a single one-time approval, and each recurring charge is submitted by Tab so renewals stay gasless too.
Contract reference
EVM: see TabSubscriptionRouter. Solana program IDs are listed in /docs/chains/solana.