API

Subscriptions

Plans and subscribers around TabSubscriptionRouter. All endpoints are session-authed via the dashboard (cookie); the public subscribe link bypasses auth for the read path only.

List my plans

GET /api/subscriptions/plans
Cookie: tab-session=…

→ 200 OK
{
  "plans": [
    {
      "id": "plan_xxx",
      "planKey": "0x…",
      "name": "Silver tier",
      "amount": "9.99",
      "currency": "USDC",
      "chain": "base",
      "period": 2592000,
      "status": "active"
    }
  ]
}

Look up a plan by key (public)

GET /api/subscriptions/plans?planKey=0x…
→ 200 OK
{ "plans": [ { /* …same shape… */ } ] }

Create a plan

Stages an off-chain record and returns onChainArgs for the creator's browser to call createPlan on-chain.

POST /api/subscriptions/plans
{
  "name": "Silver tier",
  "description": "Discord access, early features.",
  "amount": "9.99",
  "chain": "base-sepolia",
  "period": 2592000
}

→ 201 Created
{
  "plan": { "id": "plan_xxx", "status": "pending_create", … },
  "onChainArgs": {
    "router": "0x…",
    "planId": "plan_xxx",
    "amount": "9990000",
    "period": 2592000
  }
}

Confirm plan creation

POST /api/subscriptions/plans/{plan_id}
{
  "action": "confirm_create",
  "txHash": "0x…",
  "planKey": "0x…"
}

Deactivate / reactivate a plan

POST /api/subscriptions/plans/{plan_id}
{ "action": "deactivate" }   // or "reactivate"

Send the on-chain deactivatePlan / reactivatePlan call first, then notify the server with this endpoint.

Subscriber: list my subscriptions

GET /api/subscriptions/subs
→ 200 OK
{
  "subscriptions": [
    {
      "id": "sub_xxx",
      "planKey": "0x…",
      "subscriberHandle": "alice",
      "creatorHandle": "designstudio",
      "status": "active",
      "startedAt": 1759999999000,
      "lastChargedAt": 1759999999000,
      "chargesPaid": 3
    }
  ]
}

Creator: list subscribers for one of my plans

GET /api/subscriptions/subs?planId=plan_xxx
→ 200 OK
{ "subscriptions": [ … ] }

Subscribe (record off-chain mirror)

Call after the subscriber's wallet has run subscribe(planKey) on-chain. The first-period charge happens as part of the on-chain call.

POST /api/subscriptions/subs
{
  "planKey": "0x…",
  "txHash":  "0x…"
}

Cancel a subscription

POST /api/subscriptions/subs/{sub_id}
{ "action": "cancel" }

Subscriber or creator can call. Run the on-chain cancel first; the server marks the rowcancelled for the dashboard.

Cron: charge due subscriptions

POST /api/cron/charge
Authorization: Bearer $ADMIN_SECRET
Content-Type: application/json

{ "chain": "base" }

→ 200 OK
{
  "chain": "base",
  "scanned": 14,
  "charged": 9,
  "skippedEarly": 5,
  "skippedFailed": 0,
  "errors": []
}

Schedule this once an hour. The contract enforces the per-sub period so frequent runs are safe.

Webhooks

Subscribe to these event types from /dashboard/webhooks:

  • subscription.plan_created
  • subscription.started
  • subscription.charged
  • subscription.cancelled
  • subscription.halted