Guides

Subscription plans

Create per-application subscription plans with feature flags and optional Stripe billing.

Subscription plans

Each application can have one or more subscription plans. Plans carry a free-form features JSON object that is injected into the id_token as the features claim when the user completes an OAuth flow for that application.

All endpoints require an admin or superadmin session.

Plans

List plans

GET /api/admin/applications/:appId/plans

Returns each plan with its price tiers and the count of active subscribers.

Create a plan

POST /api/admin/applications/:appId/plans
Content-Type: application/json

{
  "name": "Pro",
  "description": "Full access",
  "features": {
    "maxProjects": 50,
    "exportEnabled": true,
    "storageGb": 100
  },
  "isDefault": false,
  "stripeProductId": "prod_abc123"
}
FieldTypeDefaultDescription
namestringrequiredPlan name (1–64 chars, unique per application)
descriptionstringOptional description (max 500 chars)
featuresobject{}Free-form JSON feature flags
isDefaultbooleanfalseAuto-assign to new users granted access to the app
stripeProductIdstringLink to a Stripe Product for webhook sync

Update a plan

PATCH /api/admin/applications/:appId/plans/:planId
Content-Type: application/json

{ "features": { "maxProjects": 100 } }

Delete a plan

DELETE /api/admin/applications/:appId/plans/:planId

Returns 400 SUB_003 if any users currently hold an active subscription to this plan.

Plan prices (Stripe billing)

Price tiers define the amounts charged for a plan and map to Stripe Price objects. They are used for display and for webhook reconciliation — Stripe sends customer.subscription.updated events that update the subscription isActive flag automatically.

Add a price tier

POST /api/admin/applications/:appId/plans/:planId/prices
Content-Type: application/json

{
  "name": "Monthly",
  "amount": 999,
  "currency": "usd",
  "interval": "month",
  "stripePriceId": "price_xyz"
}
FieldTypeDefaultDescription
namestringrequiredDisplay name (1–64 chars)
amountintegerrequiredAmount in the smallest currency unit (e.g. cents)
currencystring"usd"ISO 4217 3-letter currency code
interval"month" | "year" | "one_time""month"Billing cycle
stripePriceIdstringStripe Price ID for webhook reconciliation

Delete a price tier

DELETE /api/admin/applications/:appId/plans/:planId/prices/:priceId

Assign a subscription to a user

POST /api/admin/applications/:appId/users/:userId/subscription
Content-Type: application/json

{
  "planId": "<uuid>",
  "expiresAt": "2027-01-01T00:00:00Z"
}

expiresAt is optional; when omitted the subscription does not expire.

Revoke a subscription

DELETE /api/admin/applications/:appId/users/:userId/subscription

Default plan auto-assignment

When you mark a plan as isDefault: true, it is automatically assigned to every user who is granted access to the application (via POST /api/admin/applications/:id/users). Only one default plan per application is supported; creating a new default plan does not de-activate the old subscribers — it only affects future grants.

Copyright © 2026