Subscription plans
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"
}
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Plan name (1–64 chars, unique per application) |
description | string | — | Optional description (max 500 chars) |
features | object | {} | Free-form JSON feature flags |
isDefault | boolean | false | Auto-assign to new users granted access to the app |
stripeProductId | string | — | Link 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"
}
| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Display name (1–64 chars) |
amount | integer | required | Amount in the smallest currency unit (e.g. cents) |
currency | string | "usd" | ISO 4217 3-letter currency code |
interval | "month" | "year" | "one_time" | "month" | Billing cycle |
stripePriceId | string | — | Stripe 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.