Agency accounts
An agency is an ordinary organization with agency mode turned on. It creates and manages client organizations — each one a real, separate org with its own projects, services, domains, members and billing — and its team is placed into every client automatically. Nothing about a client is special: every tool that takes an organizationId works on a client org exactly as it does on any other org. What the agency relationship adds is the fan-out (one team, many orgs), a place to see all the clients at once, and one bill: clients are pooled under the agency’s own plan — no subscription of their own, their services and usage counting toward the agency’s allotments — with a hand-off path when a client should pay for itself.
The nine agency tools mirror the portal’s Settings → Organizations → Agency mode panel and the Clients page. Read tools need read and admin on the agency; mutations need manage and admin on the agency, except the three that create or remove authority or commit the agency’s money (agency mode itself, detaching a client, and adding a client to the billing pool), which need owner.
Vocabulary
- Agency — an org with
is_agency = true. Yourwhoamiand the org switcher carry the flag. - Client — an org whose
parent_organization_idpoints at an agency. A client is never itself an agency, and an agency is never a client; the tools refuse both (org_is_client,org_is_agency). - Managed membership — a membership row in a client org that the agency wrote for one of its own team. It behaves like any membership (roles, scopes, the org switcher all work unchanged) but is stamped as agency-managed, so the agency can later re-role or remove it without touching anyone the client added by hand.
- Team default — for each member of the agency, the role they should hold in every client (
owner/admin/developer/viewer), ornullfor “not fanned out”.
Enabling agency mode
Portal: Settings → Organizations → Agency mode. Owner only.
{ "name": "set_agency_mode", "arguments": { "organizationId": "…", "isAgency": true } }
Two refusals: an org that is already somebody’s client cannot become an agency (detach it first), and an agency with clients cannot turn agency mode off (detach them first, has_clients).
Team defaults
Defaults decide who lands in each new client and with what role. Every current member of the agency is listed, with their agency role and their default (null until set):
{ "name": "list_agency_team_defaults", "arguments": { "organizationId": "…" } }
{ "name": "set_agency_team_default",
"arguments": { "organizationId": "…", "principalId": "…", "defaultRole": "developer" } }
A default is a standing grant into every future client, so it is bounded by your own agency role exactly as a direct grant would be: only an agency owner can set a default of owner (role_escalation otherwise). Set defaultRole to null to stop fanning that person out. Agents are members too — an agency’s automation agent with a developer default deploys into every client with no per-client setup.
Changing a default does not by itself touch existing clients; see Applying defaults below.
Creating a client in 60 seconds
Portal: Organizations → Create organization → choose Client of and pick the agency. MCP:
{ "name": "create_client_organization",
"arguments": { "organizationId": "<agency id>", "name": "Northwind Bakery",
"clientOwnerEmail": "pat@northwind.example",
"clientOwnerDisplayName": "Pat" } }
One transaction creates the org, activates Launch on it, links it to the agency, and writes the agency’s team in as managed memberships per the team defaults (with matching Launch access). The response lists every membership written and creatorRole — you always land in the client: with your own default if you have one, and as owner if the defaults would otherwise leave the org ownerless (an org is never born without an owner).
Other products at birth. Launch is always on. Pass pillars to turn on more in the same transaction — e.g. a bookkeeping agency whose client will only ever use Tandem Profit: "pillars": ["profit"] (portal: tick Tandem Profit under Products to turn on). Every owner of the new client — you and the agency’s owners — gets owner-level access to each; other team members get Launch only until an owner grants more. An unknown or coming_soon slug refuses the whole call (pillar_not_available) and creates nothing. The response’s pillars lists each pillar with how many owners were seeded. A client that hosts nothing on Launch gets no Launch trial-ending emails.
clientOwnerEmail is optional. When given, the client’s own owner is invited after the org exists (so an invite failure never undoes the org), and clientOwner in the response tells you what happened:
membership— a brand-new email: the account was created as owner and a single-use setup link is returned for you to relay (also emailed).invitation— an existing account: a pending in-portal invitation the person must accept before they hold ownership.error— the org was created but the invite was not; invite again withinvite_member.
The new client is pooled from birth: it has no subscription of its own — its services and usage count toward the agency’s plan and it inherits the agency’s billing state (trial, paid, comped or lapsed). billing in the response is { outcome: "pooled", planCode, effectiveState } — see Billing below. The planCode / interval inputs are deprecated and ignored. Client creation is rate-limited per agency.
Listing clients
{ "name": "list_agency_clients", "arguments": { "organizationId": "…" } }
One row per client: id, slug, name, tier (effective — a pooled client reports its agency’s), member and managed-member counts, project/service counts, last deploy time and state, plus a billing block (pooled, effectiveState, share of the pool this month, pendingHandoff, and — for handed-off clients — plan, cadence, discount, period end) — the same table the portal’s Clients page shows, so an agent can spot “which client has a failed deploy” or “which client is the pool’s biggest consumer” without walking each org. The response also carries the agency’s pool, the hand-off tier and agencyHasPaymentMethod (see Billing).
Linking an existing org
Already manage a client’s org that predates agency mode? Adopt it rather than recreate it:
{ "name": "link_client_organization",
"arguments": { "organizationId": "<agency id>", "clientOrganizationId": "<existing org id>" } }
You need admin on the agency and owner of the org being linked — linking hands the agency standing access to that org, so only its owner can consent. Preconditions: the target is not already a client (already_has_parent), is not an agency and has no clients of its own (org_is_agency), and is not the agency itself (self_link). On success the team defaults are applied immediately and the response carries the applied summary. Memberships the org already had stay unmanaged — the client granted those itself and they are never rewritten.
Applying defaults to existing clients
{ "name": "apply_agency_team_defaults",
"arguments": { "organizationId": "<agency id>", "clientOrganizationId": "<one client, optional>" } }
Omit clientOrganizationId to sweep every client. Per client you get added, updated, removed and skipped:
- added / updated — managed rows created or re-roled to match the current defaults.
- removed — managed rows whose principal no longer has a default (set to
null) or is no longer an agency member. - skipped
hand_granted— the client already has this person as a hand-granted member. A client’s own decision about a person outranks the agency’s default, so the row is left alone. - skipped
last_owner— removing this managed row would leave the client without an owner; it stays until the client has an owner of its own.
Apply is idempotent — running it twice changes nothing the second time.
Detaching a client
{ "name": "detach_client_organization",
"arguments": { "organizationId": "<agency id>", "clientOrganizationId": "<client id>" } }
Owner of the agency. Detach removes exactly the managed memberships (and the Launch access they carried) and clears the parent link — nothing else. The client keeps every project, domain, database and hand-granted member. Two preconditions:
- the client must have at least one owner of its own (not agency-managed) — otherwise the detach would orphan it (
no_independent_owner). Invite one withinvite_member, or wait for a pending owner invitation to be accepted; - the client must be paying for itself — detach is refused while the client is pooled under the agency (
billing_not_client_paid), including while a handoff is pending. Hand billing off first.
When someone leaves the agency
Removing a member from the agency (remove_member, or the portal’s Members panel) also revokes every managed membership that person held in the agency’s clients, in the same operation. Hand-granted memberships in a client are untouched — if a client made your former colleague an owner directly, that stays. The only managed row that survives is a last_owner case, which is reported so you can fix the client’s ownership.
Demoting someone inside the agency does not change their client defaults; edit the default if the new role should not fan out.
Billing
An agency has one subscription — its own — and every client it creates is pooled under it. A pooled client has no subscription of its own: its services and metered usage count toward the agency’s plan and monthly allotments exactly as if the resources lived in the agency org, its overage lands on the agency’s invoice itemised per client, and it inherits the agency’s billing state. There is no per-client plan to buy and no coupon to earn: the pool is the agency deal, and one org per client costs the agency nothing more than one org for everything — so the org structure can follow permissions, never billing.
A client can later be handed off to pay for itself (referral_discount / commission below); that is the only time a client carries a subscription.
Pooled billing
- What pools. Services (against the plan’s included count), object storage, egress, email sends, database storage — every allotment the agency’s plan carries — are measured across the agency plus every pooled client, the allotment is subtracted once, and the overage is priced at the plan’s list rates. Domains, phone numbers and SMS a pooled client buys are pass-through and charge the agency’s card at list, as before.
- What the client inherits. Trial, paid, comped and lapsed are the agency’s states. An agency on its free trial can pool clients; when that trial ends, every pooled client’s services go offline with it (nothing is deleted), and they all come back the moment the agency subscribes. The trial’s service/database/bucket allowances are shared across the pool.
- What the agency sees.
list_agency_clients(portal: the Clients page) carriespool— the agency’s plan, state,previewExpiresAt,pooledClientCount,currentPeriod(agency + pooled clients this UTC month, per metric) andservicesused vs included — and per clientbilling.pooled,billing.effectiveState, andbilling.share(that client’s slice of the pool this month: used per metric, share of the pool, live services, its apportioned share of the projected overage). The portal shows the same as the Pool tile and the per-client pooled · counts toward Pro · 2 services · $0.00 overage share line. - The invoice. When the overage engine closes a window, the agency gets one overage invoice with one line per client and metric (“egress_gb overage for Northwind Bakery (12 GB × $0.12) — 2026-09”), so re-billing clients is a matter of reading the invoice. Each client’s overage history (
get_usage→overagePeriods, portal Billing) lists those windows withscope: "pooled", the pool’s total, and its ownshareCents.
{ "name": "list_agency_clients", "arguments": { "organizationId": "…" } }
What the client sees
A pooled client’s own Billing page — and get_billing_status on the client org — says Billed by agency name: this organization’s services and usage count toward agency name’s Pro plan. There is no trial banner (the trial, if any, is the agency’s), no subscribe or change-plan control, and no per-client plan: the subscription card shows the agency’s plan, its state, the pool’s included services and the current period. Usage stays the client’s own, shown against the pool’s allotment, and the projected-overage card is titled Your share of projected overage this period.
get_billing_status reports the effective values: tier, previewExpiresAt, billingVerified and deployBlockers are inherited from the agency when agencyBilling.pooled is true, subscription is null, billingScope names the agency, and agencyBilling.pool carries the agency’s plan, state, previewExpiresAt, servicesIncluded, currentPeriodEnd and whether the agency has a card. hasBillingCustomer / hasPaymentMethod describe the client’s own card — which a pooled client needs only for a handoff. whoami shows the same inheritance per org as billedBy and phrases nextStep in terms of the agency (“subscribe the AGENCY”). create_subscription and change_plan on a pooled client are refused with agency_paid_requires_agency_action: the agency’s plan is the one to change.
Adding an existing org to the pool
create_client_organization pools a client from birth, so nothing further is needed for a new client. link_client_organization pools an adopted org too — unless it is paying for itself on a live subscription, in which case it stays self_paid (billing.outcome in the link response) and list_agency_clients shows billing.mode null. To move such a client into the pool:
{ "name": "activate_client_billing",
"arguments": { "organizationId": "<agency id>", "clientOrganizationId": "<client id>" } }
Portal: Clients → Add to pool on the client’s row (owner only). It is idempotent — an already-pooled client comes back as already_active — and makes no charge. A client still on its own trial joins now; a client on a live subscription of its own has that subscription cancelled at period end and joins the pool at that instant (joinsAt, cancelledSubscriptionId) — it already paid for the current period, so the pool neither takes over early nor leaves a gap. It refuses with not_a_client for an org that is not this agency’s client, already_client_paid for a client whose billing was handed off (the agency can never take a client’s billing back), already_self_paid when its own subscription could not be wound down (no payment provider, or the provider refused), and handoff_pending while a handoff is scheduled. activate_client_billing requires owner on the agency, because it commits the agency’s allotments and invoice. Its planCode / interval inputs are deprecated and ignored.
Hand-off rate
The client-count rate card applies only to clients whose billing the agency hands off — as their referral discount or the agency’s commission. Pooled clients never carry a coupon. The band is computed from the agency’s number of active clients: pooled clients running at least one service plus handed-off clients on a live subscription.
| Active clients | Rate |
|---|---|
| 1 – 4 | 10 % |
| 5 – 14 | 20 % |
| 15 + | 30 % |
list_agency_clients reports it as tier (activeClients, discountBps, nextBand); the portal’s Clients page shows Hand-off rate: 20% off · 6 active clients · 9 more to reach 30%. When the agency moves into a new band, every referral-discount client’s subscription is re-priced at its next renewal (coupons only touch future invoices).
Handing billing to the client
A client can be moved to paying for itself. handoff_client_billing (portal: Clients → Hand off on the client’s row, owner only) puts the client on a subscription of its own, on its own card, and takes it out of the pool.
Preconditions. The client is pooled (list_agency_clients: billing.pooled true and no billing.pendingHandoff) and the client has a card of its own (billing.hasOwnCustomer). Without one the call refuses with client_card_required: the client org must add a payment method — set_payment_method with the client’s organizationId, or the Add your own card button on its own Billing page — and then the handoff is retried. It is always the client’s card that is missing here, never the agency’s.
The month boundary. A pooled client has no plan of its own, so the handoff names one (planCode, default the pool’s plan; interval month or year — the portal modal has the same picker). The new subscription is created on the client’s card right away but first charges at the start of the next UTC calendar month (startsAt = leavesPoolAt); until that instant the client stays in the pool and its usage keeps counting toward the agency’s plan, and from it the client’s own subscription takes over. No month is split between two payers and nothing is billed twice. Meanwhile list_agency_clients shows the client as pooled with billing.pendingHandoff (mode, startsAt, plan), the client’s Billing page says Billing moves to this organization’s own card on <date>, and the client can be neither detached nor re-pooled. If the agency’s pool is lapsed (its trial ended) or its subscription is unpaid, the client leaves immediately instead (immediate true) — nobody should wait a month to escape a dead pool.
The two modes. The handoff names the pricing mode the client lands in:
referral_discount— the client pays list minus the agency’s hand-off rate on its own card (the rate in force at handoff), and the agency earns nothing further on it. The client’s Billing page shows Partner discount −20% via agency name.commission— the client pays list price on its own card, and the agency earns the hand-off rate as a commission on every paid plan invoice, recorded in the ledger below. The client’s Billing page shows Referred by agency name · list price; the commission is the platform’s to pay, never the client’s.
{ "name": "handoff_client_billing",
"arguments": { "organizationId": "<agency id>", "clientOrganizationId": "<client id>",
"mode": "commission", "planCode": "pro", "interval": "month" } }
The response carries mode, newSubscriptionId, startsAt, leavesPoolAt, immediate, planCode, interval, discountBps and commissionBps (oldSubscriptionId is set only for a client that still carried a pre-pooling subscription on the agency’s customer, which is moved at that subscription’s period end as before). The client’s human owners receive an email — Billing for org name is now on your card — naming the agency, the plan, the rate and the date.
One-way. The agency can never take a client’s billing back (activate_client_billing refuses with already_client_paid). What can change is the mode: set_client_pricing_mode (portal: the referral / commission select on a self-paying client’s row, owner only) flips a handed-off client between referral_discount and commission from its next invoice — nothing is re-billed. It refuses with not_client_paid for a pooled client (hand it off instead, which takes the mode) and same_mode when nothing would change.
{ "name": "set_client_pricing_mode",
"arguments": { "organizationId": "<agency id>", "clientOrganizationId": "<client id>",
"mode": "referral_discount" } }
Once a client pays for itself it can be detached (detach_client_organization); while it is pooled or a handoff is pending, detach is refused with billing_not_client_paid.
Commission ledger and payouts
Commission accrues only for clients in commission mode paying for themselves: when one of their plan invoices is paid, one accrual row lands in the agency’s ledger with the invoice amount, the rate (commissionBps, snapshotted for that client at handoff — a later tier change does not rewrite past or future rows for an existing client) and the resulting commissionCents. Referral-discount clients earn nothing here; domains, SMS and overage never accrue.
{ "name": "list_agency_commissions",
"arguments": { "organizationId": "<agency id>", "unpaidOnly": true, "limit": 100 } }
The response is balance (unpaidCents, paidCents, currency), commissions (newest first; kind accrual or adjustment, payoutId set once paid) and payouts. The portal’s Clients page shows the same as the Commissions card: unpaid / paid-out totals, the ledger (date, client, kind, base, rate, commission, paid/unpaid) and the payouts list. list_agency_commissions requires admin on the agency.
Payouts are manual. There is no payout rail in this release: the platform team sends the money out of band (bank transfer, Stripe transfer, credit) and records it, which settles every unpaid row in one payout for exactly their sum. A negative balance (from an adjustment) carries forward and nets against future accruals. Adjustments are signed corrections the platform team enters with a note the agency can read — a clawback for a refunded client invoice, or a goodwill credit.
Platform admins have three tools for this: admin_list_agency_commissions (every agency’s balance, or one agency’s full ledger), admin_record_agency_payout (method, optional reference / note; refuses nothing_unpaid) and admin_adjust_agency_commission (signed non-zero commissionCents, required note, optional clientOrganizationId). Their portal twin is the admin console’s organization view.
Related
- Managing members and roles — roles, invitations, guests, and the last-owner rule the agency tools inherit.
- Limits and policies — cap an agency agent’s authority across every client with one action mask.