Plans and seats
What each plan allows, and how seats are counted.
Billing shows your plan and its payment and renewal state. Below that sit what it last cost and your usage against the plan, then every invoice. Three people free. Machines never take a seat.
What each plan allows
| Free | Pro | Enterprise | |
|---|---|---|---|
| Price per seat each month | $0 | $18 billed yearly, $22 billed monthly | Custom |
| Seats | 3 | Unlimited | Unlimited |
| Projects | 3 | Unlimited | Unlimited |
| Environments per project | 3 | Unlimited | Unlimited |
| Free | Pro | Enterprise | |
|---|---|---|---|
| Integration connections | 1 | Unlimited | Unlimited |
| Custom roles | 0 | Unlimited | Unlimited |
| Reads per minute | 240 | 480 | Custom |
| Writes per minute | 60 | 120 | Custom |
| Free | Pro | Enterprise | |
|---|---|---|---|
| Record kept for | 7 days | 90 days | Set by contract |
| Your own encryption key | No | No | Yes |
| Streaming the record | No | No | Yes |
| Company login and directory sync | No | No | Yes |
Encrypted, each value under its own key, on every plan. No plan gets weaker encryption than another. What plans differ on is isolation and who holds the wrapping key, along with retention and the paperwork.
Rate limits are per machine identity and split into two buckets, so a busy read path never eats the write budget.
Seats
The seat count is the people in this workspace whose membership is active. Machines never take a seat, so no identity is counted and none appears on the invoice.
A membership a directory closed stops counting the moment it is marked, without the row being deleted, so a closed seat is never a refund conversation.
The usage meters cover seats and machines, then projects and connections. The machine meter is there to be read rather than paid for.
A seat gap notice appears when you have more members than seats you pay for. Fixing it is Manage seats, which changes the quantity on the subscription.
Changing plan
/settings/billing/upgrade confirms your billing country and the seat count, then hands you to Stripe or Paystack. Which one you get follows the country. The browser never sees the price book, because checkout is started on the server.
/settings/billing/return is where you land afterward, polling until the payment webhook lands the new plan.
Your plan is read from penv's own records rather than from the payment processor. A contract grant on top of what the processor sold resolves to whichever is more generous, so an invoiced Enterprise workspace gets its plan with no subscription behind it.
Canceling lives on Danger Zone.
What each action needs
| Action | Needs | Confirm it is you | Recorded as |
|---|---|---|---|
| Open the billing page | billing:read | No | not recorded |
| Open the upgrade page | billing:manage | No | not recorded |
| Start a checkout | billing:manage | No | billing.checkout.started |
| Change the seat count | billing:manage | No | not recorded from the console |
| Cancel the plan | billing:manage | Yes | billing.subscription.cancel_requested |
Changing the seat count moves the quantity on the subscription and writes nothing locally, so nothing about it is attributed to you here. The payment webhook that moves the ledger is what records the change.
Billing Admin is the built-in role for somebody who handles the invoice and reaches no values.
The billing.* entries are written to the record but are missing from the action picker on the Audit Log page. Narrow by actor and date, then export. See browsing the record.
Next: Danger Zone.