Skip to content

List active plans

GET
/api/plans/
curl https://api.ai.aikynetix.app/api/plans/

Returns every active plan: name, price in minor units (amount_cents), billing interval (month / year), per-period session_quota, ceilings (max_seats / max_client_profiles), and the FE rendering hint (display_mode). Public endpoint — no auth required so a marketing-page iframe can render the pricing grid before sign-in.

Plan catalog (v1)

role name Monthly Sessions/mo Seats Clients display_mode
free Free $0 3 1 1 default
starter Starter $39 20 1 50 default
professional Professional $99 75 3 250 default
organization Organization $249 250 10 1000 default
strategic Strategic (custom) (custom) (custom) (custom) custom

Notes

  • Free is the registration default — every new team lands on it automatically without touching Stripe. There is no Stripe SKU for Free; signup creates an is_managed_externally=True Subscription locally and the monthly cron refreshes its 5-session quota.
  • Strategic ships with display_mode="custom" and zero price / quota / ceilings. The FE branches on this value and renders a sales-contact CTA in place of price + quota + ceiling rows.
  • Yearly variants exist for Starter / Pro / Org only (10× monthly = two months free). Free + Strategic are monthly-only.
  • Plans live in Postgres and are kept in sync with Stripe by manage.py sync_plansnever hardcode prices in client code. Pass stripe_price_id (from this serializer) to POST /api/billing/checkout/ + GET /api/billing/portal/update-plan/?price=<id>.
limit
integer

Number of results to return per page.

offset
integer

The initial index from which to return the results.

Media typeapplication/json
object
count
required
integer
next
string format: uri
nullable
previous
string format: uri
nullable
results
required
Array<object>
object
id
required

Plan UUID. Internal identifier — stable across price + name changes within a tier. For Stripe-touching endpoints (POST /api/billing/checkout/, GET /api/billing/portal/update-plan/), pass stripe_price_id instead.

string format: uuid
stripe_price_id
required

Stripe price_… id that uniquely identifies this (tier × interval) row in Stripe. This is what POST /api/billing/checkout/ + GET /api/billing/portal/update-plan/?price=<id> consume — pass this value, not the Plan UUID. Free + Strategic carry synthetic non-Stripe ids (bundled_free_month / bundled_strategic_month) because they have no Stripe SKU; the FE should never POST those to the Stripe-touching endpoints (filter on role !== 'free' && role !== 'strategic' client-side, mirroring how the BE views 400 those targets).

string
<= 128 characters
name
required

Display name shown to the buyer (Free, Starter, Professional, Organization, Strategic).

string
<= 100 characters
interval
required

Billing cadence — month or year. Free + Strategic are monthly-only (Decision H + I); yearly variants exist only for Starter / Pro / Org.

  • month - Monthly
  • year - Yearly
string
Allowed values: month year
amount_cents
required

Price in minor units (cents/kopecks). Divide by 100 for the headline price. 0 for Free and Strategic — for Strategic the actual contract is sales-managed off-platform and display_mode=custom tells the FE not to render the price block.

integer
<= 2147483647
currency

ISO 4217 currency code (lowercase, e.g. usd). v1 ships USD-only; multi-currency is a v1.6 follow-up.

string
<= 3 characters
role
required

Canonical tier identifier: free / starter / professional / organization / strategic. Use name for display and role for tier-based feature gating server-side. Stable across price + name changes within a tier.

string
<= 50 characters
display_mode

How the FE should render this row. default shows price + quota + ceilings (the four paid tiers + Free). custom skips those fields and shows the sales-contact CTA (Strategic). Branching key for the pricing grid and SubscriptionPanel — see ADR-0019 §2 + §3.

  • default - Default
  • custom - Custom
string
Allowed values: default custom
session_quota

Sessions per billing period. Quota refreshes to exactly this number each renewal — leftover does NOT roll over (Decision 6). 0 for Strategic (engagement-specific, off-platform).

integer
<= 2147483647
max_seats

Team-membership ceiling. Inviting past this returns 402 with detail="seat_limit_reached". Free=1, Starter=1, Pro=3, Org=10, Strategic=0 (gate skipped for display_mode=custom).

integer
<= 2147483647
max_client_profiles

Client-row ceiling. Creating past this returns 402 with detail="client_limit_reached". Free=1, Starter=50, Pro=250, Org=1000, Strategic=0 (gate skipped for display_mode=custom).

integer
<= 2147483647
allowed_segments

Reserved for downstream pricing differentiation (segment-aware plans, ADR-0019 Decision J). Ships inert in v1.5PlanListView does NOT filter by team segment, so the field is informational only. Empty list = applies to all segments. v1.6 will activate the filter without an additional migration.

Examples
ExampleCatalog(everyTier,Monthly)

Catalog (every tier, monthly)

{
"count": 123,
"next": "http://api.example.org/accounts/?offset=400&limit=100",
"previous": "http://api.example.org/accounts/?offset=200&limit=100",
"results": [
[
{
"id": "11111111-1111-1111-1111-111111111111",
"stripe_price_id": "bundled_free_month",
"name": "Free",
"interval": "month",
"amount_cents": 0,
"currency": "usd",
"role": "free",
"display_mode": "default",
"session_quota": 5,
"max_seats": 1,
"max_client_profiles": 1,
"allowed_segments": []
},
{
"id": "22222222-2222-2222-2222-222222222222",
"stripe_price_id": "price_1TX5ja3IwNiuriuFdQ5MTTcZ",
"name": "Starter",
"interval": "month",
"amount_cents": 3900,
"currency": "usd",
"role": "starter",
"display_mode": "default",
"session_quota": 20,
"max_seats": 1,
"max_client_profiles": 50,
"allowed_segments": []
},
{
"id": "33333333-3333-3333-3333-333333333333",
"stripe_price_id": "price_1TX5kL3IwNiuriuFabcdefgh",
"name": "Professional",
"interval": "month",
"amount_cents": 9900,
"currency": "usd",
"role": "professional",
"display_mode": "default",
"session_quota": 75,
"max_seats": 3,
"max_client_profiles": 250,
"allowed_segments": []
},
{
"id": "44444444-4444-4444-4444-444444444444",
"stripe_price_id": "price_1TX64W3IwNiuriuFlSDw2Scy",
"name": "Organization",
"interval": "month",
"amount_cents": 24900,
"currency": "usd",
"role": "organization",
"display_mode": "default",
"session_quota": 250,
"max_seats": 10,
"max_client_profiles": 1000,
"allowed_segments": []
},
{
"id": "55555555-5555-5555-5555-555555555555",
"stripe_price_id": "bundled_strategic_month",
"name": "Strategic",
"interval": "month",
"amount_cents": 0,
"currency": "usd",
"role": "strategic",
"display_mode": "custom",
"session_quota": 0,
"max_seats": 0,
"max_client_profiles": 0,
"allowed_segments": []
}
]
]
}