Plans & Roles

Your pricing model is your business model. The roles you define determine who can do what — both on your team and inside your customers' teams. This page lets you configure all of it from the admin panel, without writing code or redeploying.

Subscription Plans

Plans define what your customers get and what they pay. Every SaaS pricing page is backed by a set of plan objects that control which features are unlocked, what resource limits apply, and how the payment gateway charges for them.

The AdminPlanController provides full CRUD so you can create, modify, and retire plans as your product evolves.

Why This Matters

Most SaaS products change their pricing 3-5 times in the first two years. Maybe you launch with two plans and realize you need a free tier to drive adoption. Maybe an enterprise customer needs a custom plan with higher limits. With plan management in the admin panel, your product manager can make these changes without waiting for an engineering deploy.

Endpoints

GET    /api/admin/plans           # List all plans
POST   /api/admin/plans           # Create a new plan
PUT    /api/admin/plans/{id}      # Update an existing plan
DELETE /api/admin/plans/{id}      # Delete a plan (only if no active subscribers)

When you update a plan, changes apply to new subscriptions immediately. Existing subscribers remain on their current terms until renewal — you will not accidentally change what a paying customer is getting mid-cycle.

Plans with active subscribers cannot be deleted. Deactivate them instead by setting is_active: false. Deactivated plans stop appearing on the pricing page but existing subscribers continue uninterrupted.

Plan Fields Explained

  • name — Display name shown on the pricing page and in invoices (e.g., "Pro", "Business", "Enterprise").
  • slug — URL-safe identifier used in code and API calls (e.g., "pro", "business"). Cannot be changed after creation because it may be referenced in feature checks throughout your codebase.
  • type — The billing model. Five options, each suited to different business models:
    • free — No payment required. Use for freemium tiers that let users try the product with limited features. Good for driving signups and building a user base.
    • recurring — Fixed monthly or yearly subscription. The most common SaaS model. Predictable revenue, easy for customers to understand.
    • lifetime — One-time payment for permanent access. Useful for AppSumo-style launches or early-bird deals. Generates cash upfront but no recurring revenue.
    • usage — Charges based on consumption (API calls, storage, messages sent). Good for infrastructure products and APIs where usage varies wildly between customers.
    • per_seat — Charges per team member. Good for collaboration tools where value scales with team size. Each seat beyond the included count is billed at the extra_seat_price.
  • price_monthly / price_yearly — Display prices for the pricing page. The actual charge amount is determined by the gateway_prices configured in your payment gateway.
  • features — An array of feature keys that this plan unlocks. These are the strings you check in your application code (e.g., api_access, priority_support, custom_domain). When a user tries to access a feature, the system checks whether their organization's plan includes it.
  • limits — Resource caps that the plan enforces. Unlike features (which are binary on/off), limits are numeric thresholds. Common limits include:
    • members — Maximum team members in the organization.
    • storage_gb — Maximum file storage.
    • api_requests_per_month — API call quota.
    • projects — Maximum number of projects, boards, or whatever your primary resource is.

    When a limit is reached, the application blocks further resource creation and prompts the user to upgrade.

  • gateway_prices — Maps plan intervals to price IDs in your payment gateway. This is what connects your plan to the actual charge. You create the price in Stripe (or your gateway's dashboard), copy the price ID, and paste it here.
    "gateway_prices": {
        "stripe": {
            "monthly_price_id": "price_1abc...",
            "yearly_price_id": "price_2def..."
        }
    }

    If you support multiple gateways, add price IDs for each one. The system uses whichever gateway is active in your billing settings.

  • trial_days — Number of days of free access before payment is required. Set to 0 for no trial. 14 days is standard for B2B SaaS. Trials let customers experience the product before committing — they significantly improve conversion rates.
  • included_seats — Number of team members included in the base price (for per-seat plans). For example, if your plan costs $49/month and includes 5 seats, the first 5 members are free.
  • extra_seat_price — Price per additional seat beyond the included count (for per-seat plans). For example, $10/month per extra member.
  • is_active — Whether this plan is visible on the pricing page and available for new subscriptions. Set to false to retire a plan without affecting existing subscribers.

Example: Creating a Pro Plan

{
    "name": "Pro",
    "slug": "pro",
    "type": "recurring",
    "price_monthly": 29.00,
    "price_yearly": 290.00,
    "features": [
        "unlimited_projects",
        "api_access",
        "priority_support",
        "custom_domain",
        "advanced_analytics"
    ],
    "limits": {
        "members": 25,
        "storage_gb": 50,
        "api_requests_per_month": 100000
    },
    "gateway_prices": {
        "stripe": {
            "monthly_price_id": "price_1NxHk2...",
            "yearly_price_id": "price_1NxHk3..."
        }
    },
    "trial_days": 14,
    "included_seats": 5,
    "extra_seat_price": 10.00,
    "is_active": true
}

This creates a Pro plan at $29/month ($290/year, saving the customer ~17%). It includes 5 team seats with additional seats at $10/month each. New subscribers get a 14-day trial. The plan unlocks 5 features and sets limits on members (25), storage (50 GB), and API requests (100k/month).

Global Roles

Global roles control who on your team can access the admin panel and what they can do there. These are completely separate from organization roles — a super admin is not a member of any customer organization.

Why This Matters

Not everyone on your team needs full admin access. Your co-founder needs everything. Your support agent needs to look up users and view subscriptions, but should not be able to change billing settings or delete accounts. Your accountant might need to see invoices but nothing else.

Built-in Roles

  • super_admin — Full access to everything in the admin panel. Can manage users, organizations, plans, settings, and all other admin features. Reserve this for founders and senior engineers.
  • support_agent — Read-only access to admin views. Can look up users, view organization details, and see subscription status, but cannot modify anything. Give this to customer support team members.

When to Create Custom Roles

As your team grows, you will need more granular access. Common custom roles:

  • billing_manager — Can manage plans, view subscriptions, and issue refunds, but cannot access user management or platform settings. Good for a finance team member or billing operations person.
  • content_manager — Can edit email templates and documentation, but nothing else. Good for a marketing team member who writes customer-facing copy.
  • developer — Can view audit logs, manage feature flags, and flush cache, but cannot modify billing or user data. Good for on-call engineers who need to troubleshoot production issues.

Endpoints

GET    /api/admin/roles                    # List all global roles
POST   /api/admin/roles                    # Create a new role
PUT    /api/admin/roles/{id}               # Update a role
DELETE /api/admin/roles/{id}               # Delete a role

Assigning Permissions

POST /api/admin/roles/{id}/permissions
{
    "permissions": [
        "admin.users.view",
        "admin.users.edit",
        "admin.settings.view",
        "admin.plans.view",
        "admin.plans.edit"
    ]
}

Permissions are granular and scoped to admin features. A role can have any combination. The naming convention is admin.{resource}.{action}, making it easy to understand what each permission grants.

Organization Role Templates

Organization roles control what your customers' team members can do within their organization. You define the templates; every organization gets these roles by default.

Why This Matters

Your customers need different levels of access within their teams. The CEO needs full control. The developer needs API key access. The intern needs read-only access. If you only have one "member" role, your customers will ask for more — and you want to have the answer ready.

Default Role Templates

  • owner — Full control of the organization: billing, team management, settings, data deletion. Every organization has exactly one owner. Ownership can be transferred to another admin.
  • admin — Can do almost everything the owner can: invite members, change roles, manage projects, view billing. Cannot delete the organization or transfer ownership.
  • member — Standard access: can use the product, create and edit their own resources, but cannot manage the team or billing. This is the default role for invited users.

When to Add Custom Role Templates

Add custom roles when your customers' teams have specialized access needs:

  • viewer — Read-only access to everything. Cannot create, edit, or delete anything. Useful for stakeholders, clients, or auditors who need visibility without the ability to make changes.
  • developer — Member access plus the ability to create and manage API keys. Useful for technical team members who need programmatic access but should not manage billing or invite users.
  • billing_admin — Can view and manage the organization's subscription, payment method, and invoices, but cannot access project data or manage team members. Useful for finance departments at larger companies.
  • guest — Limited access to specific resources (e.g., one project). Useful for external collaborators like freelancers or agency partners who need access to a subset of the organization's data.

Endpoints

GET    /api/admin/org-roles                    # List all role templates
POST   /api/admin/org-roles                    # Create a new template
PUT    /api/admin/org-roles/{id}               # Update a template
DELETE /api/admin/org-roles/{id}               # Delete a template

Assigning Permissions to a Template

POST /api/admin/org-roles/{id}/permissions
{
    "permissions": [
        "projects.create",
        "projects.edit",
        "projects.delete",
        "members.invite",
        "billing.view"
    ]
}

List Available Organization Permissions

GET /api/admin/org-permissions

Returns the complete list of permissions that can be assigned to organization roles, grouped by feature area. Use this endpoint to populate a permissions picker in the admin UI. Permissions are organized by module (e.g., projects.*, members.*, billing.*, api_keys.*), making it easy to grant access to entire feature areas at once.