# Plans for Users vs Plans for Workspaces

Sell a plan to a person, to a workspace, or to both, and check each one by naming whose plan you mean.

A plan belongs to one of two subjects. A **workspace** plan is billed to the workspace and covers what the workspace holds: its members, seats and data. A **user** plan is billed to one person, on their own card, and covers what is theirs: their API keys, the agents they connect, their devices. BuildBase never merges the two. Every check says whose plan it reads, and a screen that cares about both asks twice.

> **Note:**

**Requires `@buildbase/sdk` 1.0.0 or later.** The entitlement hooks and gates on this page, `usePreviewSubscriptionChange` and `useUpcomingInvoice` arrive with it.

```tsx
import { useEntitlement } from '@buildbase/sdk/react';

function ProjectToolbar({ workspaceId, projectCount }: ToolbarProps) {
  // The person's own plan: their assistant goes wherever they work.
  const assistant = useEntitlement('ai-assistant', {
    subject: { user: true },
  });
  // The workspace's plan: how many projects this workspace may hold.
  const projects = useEntitlement('max-projects', {
    subject: { workspaceId },
    current: projectCount,
  });

  return (
    <>
      {projects.allowed ? <NewProjectButton /> : <UpgradeWorkspaceCard />}
      {assistant.allowed ? <AssistantButton /> : <UpgradeAccountCard />}
    </>
  );
}
```

> **Note:**
  Personal plans live in a pricing group whose payer type is `user`. In the
  console, go to **Billing > Pricing groups**, press **Create pricing group** and
  set **Who pays** to **A person** (it locks once a version is published), then
  press **Set default** on its row to make it the group people buy from by
  default; the default's Free plan is also the one a person with no plan is
  given. With the admin API, it is `POST /api/subscriptions/groups` with
  `"payerType": "user"` and `"isDefault": true`. A `<PricingPage for="user" slug="...">` offers its own group in the
  person's Billing whether or not it is the default, and an org with exactly
  one published group for people needs no default for people to buy from it.
  Items on it take `"scope": "user"`. A group's payer type is fixed once a
  version of it is published.

> **Note:**
  The `{ user: true }` subject on the SDK hooks needs an SDK release with user
  plans. The routes under `/api/v1/public/me` answer today, for the signed-in
  person.

## The rules

Six rules decide every case on this page. When a question is not covered below, one of these answers it.

1. **A credential is never the thing that pays.** An API key, an OAuth grant or a device belongs to whoever holds it. The plan that counts is the plan of whoever owns what the credential touches.
2. **Every check names its subject.** `{ user: true }` is the signed-in person and `{ workspaceId }` is a workspace. Leave the subject out and the SDK answers for the selected workspace, never for the person. With a person's credential, check the person's plan for what is theirs (keys, connected agents, devices) and the workspace's plan for what belongs to the workspace. Nothing is inferred from the credential alone.
3. **Plans do not bleed across subjects.** A personal upgrade does not lift a workspace, and a workspace plan does not lift its members. The one bridge is one you set yourself: a workspace plan can grant its owner or every member a personal item (`ownerGrants`, `memberGrants`), written on each person for as long as the workspace's plan grants it. When you want "either plan will do", ask both and combine the answers.
4. **Cap free containers per person; sell paid ones on their own plan.** `workspaces-owned-per-user` on a personal plan caps how many workspaces a person owns, and with `countRule.unpaidOnly` it counts only the ones without a paid plan. That is the usual shape, not a constraint: you can cap paid ones too. Unlimited (`-1`) sets no ceiling from the plan and no per-day creation limit. The org's **Max workspaces per user** setting (workspace settings in the console) still caps it at the lower of the two, and binds the first workspace created for a person at sign-up too, so set it when a free workspace costs you something.
5. **A consumer app sells to people.** Declare a pricing group for users. Personal mode is a UI preset that gives each person one workspace and hides the switcher; it does not move any plan. See [Personal mode](#personal-mode).
6. **A plan is not a permission.** No role check reads a plan, no plan feature becomes a token scope, and a role a person holds never answers false because their plan lacks a feature. When an action needs both a role and a plan feature, check both.

## Checking a plan

`useEntitlement` and `useLimit` (see [Entitlement checks](/billing/entitlements)) take the subject as an option:

| Option                                  | Answers for                                                        |
| --------------------------------------- | ------------------------------------------------------------------ |
| none                                    | The selected workspace                                             |
| `subject: { workspaceId }`              | That workspace                                                     |
| `subject: { user: true }`               | The signed-in person's own plan                                    |
| `subjects: [...]` with `resolve: 'any'` | Allowed when any named subject allows it (a feature)               |
| `subjects: [...]` with `resolve: 'max'` | The higher of the named ceilings (a limit)                         |
| `member: true`                          | A per-member allowance inside the workspace plan, against the pool |

`subjects` is how you say "either plan": a feature a person can have on their own plan or through their team's. A plan that does not mention the feature does not outvote one that turns it off: a person whose plan locks it, in a workspace whose plan never names it, reads locked. When no named plan mentions it, the answer is `unknown`, which allows (unless `strictAbsent`).

```tsx
const exports = useEntitlement('pdf-export', {
  subjects: [{ user: true }, { workspaceId }],
  resolve: 'any',
});
```

`source` on the answer says which layer decided it, as it does for a workspace. The subscription and billing hooks take the same subject beside their workspace forms: `useSubscription({ user: true })`, `usePreviewSubscriptionChange({ user: true })`, `useUpcomingInvoice({ user: true })` and `useCreateCheckoutSession({ user: true })`.

```tsx
import { useCreateCheckoutSession } from '@buildbase/sdk/react';

function UpgradeAccountButton({ planVersionId }: { planVersionId: string }) {
  const { createCheckoutSession, loading } = useCreateCheckoutSession({
    user: true,
  });

  return (
    <button
      disabled={loading}
      onClick={() => createCheckoutSession({ planVersionId })}
    >
      Upgrade your account
    </button>
  );
}
```

The checkout is on the person's own Stripe customer, so the card and the invoices are theirs, not a workspace's.

On your server, `bb.entitlements.check({ slug, workspaceId, user, current, wanted })` answers once per subject you name, in the order you name them, each with its `source`, and `bb.entitlements.get({ user })` returns the person's whole answer. `bb.me.*` is the signed-in person's billing (with `bb.me.subscription.preview` and `bb.me.subscription.getUpcomingInvoice`) and `bb.users.*` a named person's, under an org API token you pass as `BuildBase({ apiToken })`. On the MCP server the person's tools are `get_user_subscription`, `get_user_entitlement`, `get_user_plans`, `check_user_limit`, `claim_user_limit`, `release_user_claim`, `preview_user_plan_change` and `get_user_upcoming_invoice`.

### The routes underneath

| Route                                              | What it answers                                                          |
| -------------------------------------------------- | ------------------------------------------------------------------------ |
| `GET /api/v1/public/me/subscription`               | The person's subscription, `standing` and `entitlement`                  |
| `GET /api/v1/public/me/subscription/entitlement`   | The entitlement alone                                                    |
| `GET /api/v1/public/me/subscription/plan-group`    | The plans a person can pick from; 404 when you sell none                 |
| `POST /api/v1/public/me/subscription/checkout`     | A checkout on the person's own Stripe customer                           |
| `POST /api/v1/public/me/entitlements/check`        | One slug for `subjects` in order, each `{ "user": true }` or a workspace |
| `GET /api/v1/public/me/limits/:slug`               | The ceiling, `claimed` and `remaining`                                   |
| `GET /api/subscriptions/users/:userId/entitlement` | A named person's entitlement, under an org API token                     |
| `GET /api/subscriptions/users/entitlements/check`  | `?slug=&subjects=user:<id>,workspace:<id>`, answered in that order       |

`GET .../plan-group` answers the group of the person's own plan; else the group `?slug=` names, which must be a published, non-archived group whose payer type is `user` (anything else is the same 404); else your default group for people; else, when you publish exactly one group for people, that one. `<PricingPage for="user">` sends its `slug`, so the person's Billing offers the plans the page listed.

The `/me` routes also carry the plan change, cancel, resume, pause, billing portal, invoices and the person's credit wallet, each the twin of the workspace route with the same body. A workspace the person is not a member of is refused on the check with 403 before anything about it is read.

## Who checks what

Most of the work is deciding which subject a resource belongs to. Ask who owns the thing being created or spent, not who is signed in.

| Resource                                                        | Subject       | Check                                                   |
| --------------------------------------------------------------- | ------------- | ------------------------------------------------------- |
| A personal API key                                              | The person    | `api-keys-per-user` on `{ user: true }`                 |
| An agent the person connects through OAuth                      | The person    | `connected-agents-per-user`, claimed for you at consent |
| A project, record or file in a workspace                        | The workspace | The workspace's limit or quota                          |
| A call made with a personal key that spends a workspace's quota | Both          | The key on the person, the quota on the workspace       |
| A workspace the person creates                                  | The person    | `workspaces-owned-per-user`, counted at create          |
| A seat                                                          | The workspace | The workspace plan's seat settings                      |

A user plan is never a seat in a workspace. Seats, seat counting and seat prices stay on the workspace plan, and publishing a plan for people refuses seat pricing other than a price per installation. Its quotas may bill overage, as a workspace plan's do: a person's overage is metered to their own Stripe customer on their own subscription, and held to their own spend cap when they set one with `PATCH /api/v1/public/me/quotas/:slug`.

A plan sold to a person cannot grant credits yet. Publishing a person's plan that grants credits is refused with `400` and `field: 'creditGrant'`: turn the credit grant off, or sell the plan to workspaces. A person still holds a credit wallet of their own and buys credit packs into it. An operator's credit grant or revoke takes a workspace only for now; the console shows a person's balance, holds and automatic top-up on their page, read only.

### Personal API keys

BuildBase counts a person's API keys; it does not issue them. Your app mints and stores the key, and holds a slot for it on `api-keys-per-user` with the key's id as the `resourceId`, released when the key is revoked. That is the prerequisite: until your app has its own key object, there is nothing to count. A call made with that key is then checked twice, as the table says: the key against the person's plan, and what it spends against the workspace that owns it.

The routes for a named person, `/api/subscriptions/users/:userId/...`, take an org API token from **Settings > API keys** in the console, never your end user's session. See [Admin API](/reference/admin-api).

## Claims

A claim holds one named thing against a limit: a key, an installed app, a project. Claim when it is created, release when it goes, and BuildBase keeps the count, so two requests one below the ceiling never both get in.

```bash
curl -X POST https://api.console.buildbase.app/api/v1/public/me/limits/api-keys-per-user/claim \
  -H "x-session-id: $SESSION_ID" \
  -H "Content-Type: application/json" \
  -d '{"resourceId": "key_7Hc2"}'
```

| Answer                      | When                                                                          |
| --------------------------- | ----------------------------------------------------------------------------- |
| `201`                       | A new claim; the body has the `claim`, `claimed` and the `limit`              |
| `200` with `existing: true` | That resource already held a claim; claiming it again is the same claim       |
| `402 PLAN_LIMIT_REACHED`    | At the ceiling; `upgradeTarget` is `user` or `workspace`, whose plan to raise |
| `403 CEILING_REACHED`       | A workspace limit you marked as an end-user boundary; the body names no plan  |
| `409 CLAIM_LINKED`          | A slug BuildBase claims itself, such as `connected-agents-per-user`           |
| `429 CLAIM_CAP_REACHED`     | Too many active claims on a slug with no ceiling                              |
| `400` naming `metadata`     | `metadata` takes more than 2,048 bytes as JSON                                |
| `404`                       | No LIMIT item declares the slug and no plan limits it                         |

`POST .../release` with the same `resourceId` gives the slot back, and answers `409 RELEASE_BUDGET_EXHAUSTED` with `resetsAt` when the item's `claimPolicy.maxReleasesPerPeriod` is spent. `GET .../claims` lists them, active by default (`?status=released` or `all`). The workspace twins are under `/api/v1/public/workspaces/:workspaceId/limits/:slug`: claim and release need permission to record usage, and the reads need billing view.

`resourceId` and `partitionKey` match `^[A-Za-z0-9._:-]{1,128}$`. With a `partitionKey`, the ceiling counts per partition: three installed apps per project, rather than three per workspace.

**Count each slug one way.** A slug is counted by claims or by the `current` your app passes, never both. An app that claims some keys and also passes its own key count counts them twice.

## What unlimited means

A limit of `-1` reads as unlimited: `limit: null` and `unlimited: true`. Three bounds stay on behind it, so a bug in a loop is slowed and seen rather than billed:

- Claim and release requests are limited to 120 a minute per subject.
- A slug with no ceiling holds at most 10,000 active claims per subject, slug and partition by default; past that, `429 CLAIM_CAP_REACHED`.
- Creating a workspace takes a per-person lease for up to ten seconds, so parallel creates are counted one at a time; the second answers `409 BUSY` and can retry.

## Webhook payloads

Every `subscription.*` and `payment.*` event names its `subject`, so one handler serves both kinds of plan:

```json
{
  "event": "subscription.created",
  "timestamp": 1760000000,
  "data": {
    "subject": { "type": "user", "id": "66f0c1d2e3a4b5c6d7e8f901" },
    "subscriptionId": "sub_1Q2w3E4r",
    "planVersionId": "66f0c1d2e3a4b5c6d7e8f9aa",
    "amount": 1200,
    "currency": "usd"
  }
}
```

A person's plan carries `subject` with `type: "user"` and no `workspaceId`. A workspace's carries both, `subject.type` being `workspace`. Branch on `subject.type`, not on whether `workspaceId` is there.

| Event                         | Key fields                                                     |
| ----------------------------- | -------------------------------------------------------------- |
| `subscription.*`, `payment.*` | `subject`, plus `workspaceId` for a workspace                  |
| `oauth.consent_granted`       | `userId`, `clientId`, `actorId`, `scope`                       |
| `oauth.consent_revoked`       | `userId`, `clientId`, `reason`                                 |
| `entitlement.ceiling_reached` | `workspaceId`, `slug`, `limit`, `current`, `userId`, `at`      |
| `quota.threshold_reached`     | `subjectType`, `subjectId`, `quotaSlug`, `percent`, `resetsAt` |

`oauth.consent_granted` fires the first time a person allows an agent; widening the scopes of a consent they hold is not a new one. `reason` on `oauth.consent_revoked` is `user_disconnected`, `consent_revoked` (your app revoked it for them) or `client_deleted`. `entitlement.ceiling_reached` fires when one of your end users meets a workspace limit you marked `endUserBoundary`: they get a neutral refusal, and you hear about it at most once an hour per workspace and slug. See [Webhooks](/webhooks/overview).

## Two customers, one email

A person who holds a plan of their own and owns a workspace with a plan of its own appears in your Stripe account as two customers with the same email. Tell them apart by metadata: the person's customer carries `subjectType: "user"` and `userId`, the workspace's carries `workspaceId`. A promotion code limited to first-time customers treats the person as new once per customer, so they can use it once on each.

## Flags are not entitlements

A user feature flag (`WhenUserFeatureEnabled`, `useUserFeatures()`) is a switch you target from the dashboard: on or off for a person, a list or an audience, with no value, no expiry and no source. It never carries a plan or a grant. A user plan's features and limits are read with `useEntitlement(slug, { subject: { user: true } })`. Use the flag to roll out a feature; use the entitlement to sell it.

## When the answer is unknown

Each gate fails one way on purpose, and nothing locks while the answer loads.

| Gate or hook                       | While unknown   | Why                                                                                                                                |
| ---------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `useEntitlement`, `<WhenEntitled>` | Allows          | A slug an older plan version never named, or a failed read, must not lock a customer out. The server still enforces the real limit |
| `<WhenNotEntitled>`                | Renders nothing | No upsell for something the person may already have                                                                                |
| `<WhenLimitReached>`               | Renders nothing | Never a wall before the answer                                                                                                     |
| `<WhenWithinLimit>`                | Renders         | The same rule from the other side                                                                                                  |
| `WhenWorkspaceFeatureEnabled`      | Renders nothing | A flag your users toggle; unset means off                                                                                          |
| `WhenUserFeatureEnabled`           | Renders nothing | A flag targets people explicitly; not targeted means off                                                                           |

Pass `whileUnknown="deny"` to an entitlement gate, or `whileUnknown="allow"` to `WhenWorkspaceFeatureEnabled`, to make them agree. `strictAbsent` reads a slug the plan does not mention as locked; see [Fails open](/billing/entitlements#fails-open).

## Personal mode

Personal mode is a UI preset; a B2C plan belongs on the user subject. The preset gives each person one workspace and hides the switcher, and it moves no plan: a plan bought inside it still belongs to that workspace. A consumer app sells to the person instead, from a pricing group whose payer type is `user`, through `POST /api/v1/public/me/subscription/checkout`, so the plan is billed to their own card and covers them wherever they work. A person can also hold a plan of their own beside a workspace plan: they are two subjects, and nothing stops both.

An app that already sold plans on Personal-mode workspaces moves each customer's plan onto the person with `POST /api/v1/public/workspaces/:workspaceId/subscription/move-to-user`: the plan moves to whoever calls it, and they need permission to manage the workspace's billing. It opens a checkout on the person's own customer that starts when the workspace's period ends, with no proration and no trial, so nothing already paid for is charged again. The workspace plan stays live until that checkout completes, so for a while the person holds both; then the workspace plan is cancelled and its usage moves with the person. A paused subscription cannot be moved, nor one within an hour of renewing.

## Several workspaces on one card

A workspace can be paid for by someone other than itself: the signed-in person, or another workspace they manage billing for. Pass `payer` on the workspace checkout, and the subscription is created on the payer's Stripe customer, so one card pays for every project. Each workspace keeps its own plan and its own subscription, and its entitlement still comes from its own plan: a payer's plan never lifts a workspace it pays for.

```tsx
import { useCreateCheckoutSession } from '@buildbase/sdk/react';

function PayWithMyCard({
  workspaceId,
  me,
}: {
  workspaceId: string;
  me: string;
}) {
  const { createCheckoutSession } = useCreateCheckoutSession(workspaceId);
  return (
    <button
      type="button"
      onClick={() =>
        createCheckoutSession({
          planVersionId: 'plan-version-id',
          payer: { type: 'user', id: me },
        })
      }
    >
      Pay for this project with my card
    </button>
  );
}
```

The rules, each checked before anything reaches Stripe:

- A person pays only as themselves, and a workspace pays only for someone holding its billing-manage permission (403 `PAYER_NOT_ALLOWED`; a payer that does not exist is 404 `PAYER_NOT_FOUND`).
- One level: a workspace someone else pays for cannot pay for others, and a workspace that pays for others cannot be paid for (409 `PAYER_IS_PAID_FOR`, `SUBJECT_IS_PAYER`). A person's own plan is never paid for by someone else (400 `PAYER_SUBJECT_KIND`).
- One payer pays for at most `maxChildren` workspaces, 100 unless the org sets another number with `PATCH /api/organizations/settings/payers` (409 `PAYER_CHILD_LIMIT`).
- A Stripe customer holds one currency, so a payer billed in one currency is refused a plan in another (409 `PAYER_CURRENCY_MISMATCH`).
- Two plans on one payer may not bill overage on the same quota: usage is counted per customer, so each would be billed both (409 `PAYER_METERED_OVERLAP`).

`GET /api/v1/public/billing/payers/:payerId/rollup` lists what one payer pays for, each workspace with its plan and status. `POST /api/v1/public/workspaces/:workspaceId/subscription/transfer-payer` moves a workspace's subscription to another payer, or back to the workspace itself: a checkout on the new payer's customer, on the same plan, that starts when the current period ends, with nothing charged twice. It is refused during a trial, for a paused or ending subscription, within an hour of renewal, and while the subscription carries recurring add-ons (409 `TRANSFER_NOT_POSSIBLE`; `NO_PAID_SUBSCRIPTION` when there is no Stripe subscription to move, `SAME_PAYER` when that payer already pays). Every payer code is listed in [Error codes](/reference/error-codes#payers). The webhook events `payer.subject_added` and `payer.subject_removed` say when a payer starts and stops paying for a workspace.

A seat-priced plan can bill a person once across everything one payer pays for: set `seatScope: 'payer'` on its seat pricing, and a member who already takes a seat in an earlier workspace of the same payer takes none in the next.

## Licensing your own self-hosted product

If you sell a product your customers run on their own servers, count installations with claims: one claim per installation id, on a limit such as `installations-per-workspace`. The check your product runs on the box is advisory. Activation, heartbeats and grace periods for a box that goes quiet stay in your product; BuildBase counts the installations and enforces the ceiling when a new one is claimed.

## What each scenario needs

The 34 ways products bill people and teams, and where each stands today.

| Id  | Scenario                                                         | Today     | How                                                                                                                                                                                                                                      |
| --- | ---------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| S01 | Personal API keys owned by the person                            | Supported | Claims on `api-keys-per-user`; your app issues the key                                                                                                                                                                                   |
| S02 | Workspace-owned keys and service accounts                        | Supported | Claims on `service-accounts-per-workspace`; your app issues the credential                                                                                                                                                               |
| S03 | One person owns several separately billed workspaces             | Supported | One subscription per workspace                                                                                                                                                                                                           |
| S04 | An agent or connector the person authorizes through OAuth        | Supported | `connected-agents-per-user`, claimed at consent; the consent events                                                                                                                                                                      |
| S05 | An app installed into a workspace                                | Supported | Claims on a slug you declare, such as `installed-apps-per-workspace`; more slots or a paid app sold as a recurring add-on. See [Apps installed into a workspace](/billing/installed-apps)                                                |
| S06 | Plans for people and plans for teams in one app                  | Supported | A pricing group per payer type, items scoped to each                                                                                                                                                                                     |
| S07 | The owner of a resource supplies the plan                        | Supported | An explicit subject on every check                                                                                                                                                                                                       |
| S08 | One metric capped per person and per workspace                   | Supported | `useLimit` with `subjects` and `resolve: 'max'`                                                                                                                                                                                          |
| S09 | A team plan gives each member something of their own             | Supported | `memberGrants` and `ownerGrants` on the workspace plan                                                                                                                                                                                   |
| S10 | Seat plans with seat types and included seats                    | Partly    | Seat counting by role, a minimum and seat blocks; a second seat type sold as a [seat class](/billing/add-ons#seat-classes); a seat true-up is not supported                                                                              |
| S11 | A per-member allowance inside a team plan                        | Supported | `perMember` quotas, with a fallback to the pool                                                                                                                                                                                          |
| S12 | A per-person cap on free workspaces                              | Supported | `workspaces-owned-per-user` with `countRule.unpaidOnly`                                                                                                                                                                                  |
| S13 | A container count sold on the paying container's plan            | Partly    | Claims on a workspace limit; modules that count their own children adopt it one by one                                                                                                                                                   |
| S14 | Licensing installations of your self-hosted product              | Partly    | Claims count installations; the lifecycle on the box is yours                                                                                                                                                                            |
| S15 | Device slots on a personal plan                                  | Supported | A claim per signed-in device, with a release budget                                                                                                                                                                                      |
| S16 | Projects or sites under one person's card                        | Supported | One workspace per project, each paid for by the person's card (`payer`)                                                                                                                                                                  |
| S17 | A parent account paying for many child organizations             | Supported | A workspace pays for others (`payer`), one level deep, up to `maxChildren`, read together through the payer roll-up                                                                                                                      |
| S18 | A person spending a personal allowance inside a workspace        | Partly    | An operator can grant the workspace the item; a person moving their own allowance into a workspace is not supported                                                                                                                      |
| S19 | Moving between workspaces; a team seat replacing a personal plan | Partly    | `supersedes` on `memberGrants` keeps or cancels the personal plan; `move-to-user` moves a plan to a person; moving a plan into a workspace means cancelling and buying again                                                             |
| S20 | A family or group plan run by one person                         | Supported | A workspace plan with `memberGrants`, `perMember`, `maxMembers` and a change cooldown. See [Family and group plans](/billing/family-plans)                                                                                               |
| S21 | An allowance, then refuse, wait or meter under a spend cap       | Supported | [Spend caps](/credits/spend-caps), `resetsAt` and `quota.threshold_reached` on a workspace or a person; a personal plan's quota bills overage like a workspace's, capped through `PATCH /api/v1/public/me/quotas/:slug`                  |
| S22 | A trial once per person                                          | Supported | Trial records per person, on their own plans                                                                                                                                                                                             |
| S23 | Over the ceiling and past a failed payment                       | Partly    | `lapsePolicy`: `free` and `suspend` apply today; `read_only` is stored and reported as the lapse's `then` on a lapsed subject's answer, but does not yet stop a workspace's writes                                                       |
| S24 | Agent and service identities beside people                       | Supported | Never counted as seats; `agent-identities-per-workspace` claimed when one joins, and sold per agent as an [add-on](/billing/add-ons#selling-it-per-agent)                                                                                |
| S25 | Operator grants, overrides and promotional credit                | Partly    | Operator grants per item with a reason and an optional end, and comped plans; operator credit grants and revokes take a workspace only for now                                                                                           |
| S26 | One check naming both subjects, saying which decided             | Supported | `bb.entitlements.check` and `POST /me/entitlements/check`                                                                                                                                                                                |
| S27 | An end user meets a workspace ceiling                            | Supported | `endUserBoundary` on the item, a neutral refusal, `entitlement.ceiling_reached`                                                                                                                                                          |
| S28 | A consumer app with no teams                                     | Supported | A pricing group for users; `move-to-user` for plans bought on a workspace                                                                                                                                                                |
| S29 | A personal account kept beside team membership                   | Supported | `defaultWorkspaceId` on the profile                                                                                                                                                                                                      |
| S30 | One person in several paid workspaces                            | Supported | `seatScope: 'payer'` bills a person once across the workspaces one payer pays for                                                                                                                                                        |
| S31 | A payer who is not the entitled subject                          | Supported | `payer` on the workspace checkout; `transfer-payer` moves it                                                                                                                                                                             |
| S32 | Prepaid credits on a person or a workspace                       | Supported | Credit wallets on either subject, with automatic top-up; consumable add-ons with `consumeOrder` and `rolloverCapUnits`; a plan sold to a person cannot grant credits yet                                                                 |
| S33 | Who buys, who sees billing, whether the payer takes a seat       | Supported | Billing permissions, seat counting that leaves roles out, billing contacts                                                                                                                                                               |
| S34 | Spend caps for parts of one bill                                 | Partly    | Caps in units per `partitionKey`: `partitionCaps` on the workspace's `PATCH /api/v1/public/workspaces/:workspaceId/quotas/:slug`, each partition held to the lesser of its cap and the workspace's room; caps in money are not supported |

## Next steps

- [Entitlement checks](/billing/entitlements) - The answer, its `source`, and the gates.
- [Webhooks](/webhooks/overview) - Every event and its payload.
- [Add-ons](/billing/add-ons) - Offers sold to a workspace or to a person, and the grants they write.
- [Credits & usage](/credits/overview) - Credit wallets, which a person can hold too.
