Subscriptions & plans¶
Endpoints for plan discovery, sizing recommendations, and the subscription
lifecycle. Backed by plans.py and subscription.py, using the
saas.plan, saas.subscription, and saas.configuration models.
GET /api/plans¶
Public — no bearer token required. Returns the active plan catalog (used by the marketing pricing page as well as the portal).
GET /api/demo/plan¶
Public. Query param: plan_id (optional). Returns details for the trial
plan (or a specific plan) used to pre-fill the demo signup flow.
GET /api/subscription/recommendation¶
Requires auth. Given desired sizing, returns a recommended plan plus a full price breakdown — this is what powers the “choose a plan” sizing calculator.
Query params:
Param |
Type |
Default |
Notes |
|---|---|---|---|
|
int |
|
Clamped to a minimum of 1 |
|
float |
|
GB requested beyond the plan’s included storage |
|
|
|
Any other value falls back to |
Response (200):
{
"success": true,
"configuration_id": false,
"requested": { "users": 5, "storage": 10, "billing_cycle": "monthly" },
"trial": { "available": true, "plan": {"...": "..."}, "pricing": {"...": "..."} },
"demo": { "available": true, "plan": {"...": "..."} },
"recommended_plan": {"...": "..."},
"pricing": {
"plan_id": 3,
"plan_name": "Growth",
"users": 5,
"storage": 10,
"storage_limit": 20,
"extra_storage": 0,
"storage_overage_price": 0,
"extra_storage_monthly": 0,
"extra_storage_annual": 0,
"base_monthly": 0,
"base_annual": 0,
"monthly_total": 0,
"annual_total": 0,
"annual_saving": 0,
"annual_saving_percentage": 0,
"effective_monthly_annual": 0,
"billing_cycle": "monthly",
"total": 0,
"currency": "KES"
}
}
(Field values above are illustrative — see _format_plan() in
subscription.py for the exact plan serialization, and the pricing dict
construction just above this response block for the pricing math.)
GET /api/subscription/current¶
Requires auth. Returns the caller’s tenant’s active saas.subscription
(plan, status, renewal date, configuration), or a 404-style
{"success": false, ...} if none exists.
POST /api/subscription/select-plan¶
Requires auth. Body:
{
"plan_id": 3,
"users": 5,
"storage": 10,
"workers": 2,
"instances": 1,
"billing_cycle": "monthly"
}
Persists a saas.configuration record for the tenant ahead of checkout —
this is the step the recurring saas.configuration “not persisted” bug
(see Troubleshooting) affects.
POST /api/subscription/subscribe¶
Requires auth. Body: {"plan_id": 3, "billing_cycle": "monthly"}. Creates
or updates the tenant’s saas.subscription against the selected plan.
POST /api/subscription/confirm¶
Requires auth. Body: {"plan_id": 3}. Finalizes the subscription after
payment/checkout — call after M-Pesa (or other payment) confirmation. See
M-Pesa integration.
POST /api/subscription/demo¶
Requires auth. Body: {"plan_id": 3}. Starts a time-boxed trial on the
given (trial-flagged) plan without going through payment.
All five subscription-mutation routes only accept POST/OPTIONS — there’s
no PUT/PATCH variant; re-POST with new values to change a selection
before checkout.