Status: Active
Created: September 2025
Last Updated: October 22, 2025
Summary
Implement seat-based pricing with a single billing manager per company, assignable seats to customers, and support for both recurring subscriptions and one-time purchases. For Subscriptions: Seats renew with the subscription cycle, and pricing can be fixed or tiered. For One-Time Purchases: Seats are perpetual (never expire), each order is independent, and customers can purchase additional seats through new orders with per-order volume-based pricing.Goals
- One billing manager can purchase and manage seat quantities for an org.
- Billing managers can assign/unassign seats to customers (end-users) in that org.
- Support both subscription-based (recurring) and order-based (one-time) seat purchases.
- For subscriptions: Support tiered pricing and seat count changes with proration.
- For one-time purchases: Perpetual seats with per-order volume pricing and independent order management.
- Clear, testable API and data model; minimal billing complexity for app engineers.
Non-Goals
- Complex multi-organization ownership, resellers/marketplace flows.
- Per-user usage-based charging; this is seat-quantity pricing.
- Handling taxes, refunds, dunning, and receivables beyond provider defaults.
- Merging seat pools across multiple one-time purchase orders.
Key Concepts & Assumptions
- Company: Top-level account that owns subscriptions or orders.
- Billing Manager: Single user per org with permission to purchase/modify seats.
- Seat: Right to use the product, assignable to a customer record in the org.
- Subscription Seat: Seat linked to a recurring subscription; renews with subscription cycle.
- Order Seat: Seat linked to a one-time purchase order; perpetual access (never expires).
- Seat Pool: Collection of seats available for assignment within a subscription or order.
- Billing Period: Monthly/annual for subscriptions; N/A for one-time purchases.
Seat Assignment Process
Requirements (by priority)
- Creating and configuring a new product with seat based pricing (subscription or one-time).
- The customer goes to the merchant site and purchases the product.
- Assign a seat to a customer.
- The customer claims their benefit (like a file download or access grant).
- The billing customer removes a seat assignment.
- For Subscriptions: The billing manager upgrades to the annual plan.
- For Subscriptions: The billing manager decreases/increases the seats.
- For One-Time Purchases: The billing manager purchases additional seats via new order.
Data Model
CustomerSeat Model (Extended for Order Support)
TheCustomerSeat model supports both subscription-based and order-based seats:
- Seats can now belong to either a subscription OR an order (mutually exclusive)
- Database constraint enforces single source per seat
- All existing subscription seats remain valid (backward compatible)
Order Model (Extended for Seat Support)
seatsfield stores purchased seat count for seat-based products- Relationship enables querying all seats for an order
- Cascade delete ensures cleanup when order is deleted
ProductPriceSeatUnit (Existing, with Tiered Pricing)
The existing model already supports volume-based tiered pricing:Implementation Flows
Subscription-Based Seats
Option 1 - Adding CustomerSeat
The idea is to add a new entity that isCustomerSeat. This entity is responsible to link the billing customer and the seat holders. So its main usage is to assign, claim, and revoke seats based on a Seat Based Pricing.
The model is the following:
seat_based and a new price that is ProductPriceSeatUnit with the following attributes:
Flows
Purchasing
Assigning seat
Claiming & Granting
One-Time Purchase Seats
For one-time purchases, seats are perpetual and linked to orders instead of subscriptions. Each order is independent with its own seat pool.Purchasing One-Time Seats
Key Points:- Checkout validates seat count against product price tiers
- Order.seats stores the purchased quantity
- Seats are created as
pendingstatus withorder_idreference - Benefits are NOT granted at purchase (only on claim)
- Each order maintains an independent seat pool
Additional Purchase Flow
When a customer wants to purchase more seats: Key Points:- Each purchase creates a new independent order
- No merging of seat pools across orders
- Each order tracked separately in billing portal
- Pricing tiers apply per-order, not across orders
API
Creating Subscription Product with Seat-Based Pricing
Creating One-Time Product with Seat-Based Pricing
Checkout with Seat Quantity
Assign Seat (Unified Endpoint)
Works for both subscription and order-based seats:List Seats for Order
Revoke Seat
Claim Seat
Tasks
- Create new CustomerSeat and PriceSeatUnit. Extend existing models.
- Create new module
seatswith basic operations (assign seat, revoke, etc) - Add the invitation system functionality
- Implement the claim seat functionality
- Avoid granting benefits on checking out seat based products
- Update the checkout to handle the seat based subscriptions
- Add quantity parameter
- Calculate the pricing
- Implement the benefit granting and revocation functionality
- Update Customer Portal for the Billing Manager
- Showing the seats with status
- Showing the functionality of sending invites
- Update Customer Portal for the Customers:
- List the benefits
- Update the Dashboard to allow creating seat based products
- Update the Checkout to allow purchasing seat based
- Update the API endpoints to allow:
- Creating products with seat based
- Assigning seats
- Revoking seats
- Create metrics and alarms
- Update the documentation
Option 2 - Using Customer and Subscriptions model only
The main difference is that the CustomerSeat is stored inside the Subscription model, where:- Billing customer: is the current
customer_idfrom Subscription - There is a JSONB column storing CustomerSeats.
- No additional model
- No FK constraints with customers
- Cannot index individual seats
- Complex queries
Option 3 - Using Customer only
We can store the seats on the Customer object itself, by creating a new customer for every seat and having a parent customer who is the owner of the subscription billing.- No need for new entities
- API is confusing with the concept of parent customer.

