CRUMB a card from devarno-cloud

Billing Through TOLL: One Capability, traceo.team

traceo intermediate 7 min read

ELI5

Traceo doesn’t take money itself. It asks TOLL, the estate’s payment gate, to open a checkout. Once the customer pays, TOLL says the customer’s tenant “has traceo.team”. The only visible change is that a gentle “you’re over the Solo limit” banner goes away. Nothing is ever locked for not paying.

Technical Deep Dive

Source: traceo-cat/web/apps/client/src/lib/toll/client.ts:1-171, lib/toll/config.ts:1-114, lib/billing/handlers.ts:80-248, lib/billing/actor.ts:1-48, app/api/billing/{checkout,change-plan,entitlements,metrics}/route.ts; decisions 0101, 0103, 0106, 0117 (all accepted); .petrova/gates.yaml G-22-3, G-22-7, G-22-9 (G-22-3 at 8bbe2d5); traceo_mcp_server/MILESTONES.md M-22; traceo-cat origin/main 7d647ab, read 2026-10-25

The product rules

RuleDecision
Sold per tenant, flat, via one TOLL capability traceo.team granted to subject traceo:<tenant_id>0101
Team is $39/tenant/month list and $29 founding, USD. Annual is 10× ($390 / $290)0101, 0117
Solo limits (1 seat, 3 concurrent baselines) are advisory: shown and warned, never enforced. Team’s only code effect is removing the advisory0101
Founding = a codeless Polar discount traceo_founding, 2564 bp (25.64%), repeating 12 months, on the one list product. A separate founding product is retired0103
The founding discount is redeemable once per customer. A returning customer pays list, decided by TOLL before the checkout exists0106
Annual is a second Polar product with the same capability. Customers switch in place via the TOLL verb toll.change_plan0117

0101 was composed as proposed. All four records now carry status: accepted (0101 and 0103 countersigned 2026-10-10).

The client

lib/toll/client.ts is Traceo’s own server-only HTTP client, not the shared toll-client package. It authenticates with x-toll-key-id plus Authorization: Bearer <secret> and calls three verbs:

VerbBodyNotes
toll.checkout{productId, subject, env, discount?}env must match the gate’s Polar environment. DISCOUNT_FORBIDDEN (403) and DISCOUNT_UNPROVISIONED (503) surface as codes
toll.change_plan{subject, env, productId, apply: true}dry-run by default in TOLL, so Traceo always sends apply: true and treats anything but dryRun: false as not done
toll.entitlements{subject}returns {capabilities: string[]}

lib/toll/config.ts is the only reader of TOLL_BASE_URL, TOLL_KEY_ID (= traceo), TOLL_SECRET and TOLL_ENV (sandbox or production, with no default), plus TRACEO_TEAM_PRODUCT_ID, optional TRACEO_TEAM_ANNUAL_PRODUCT_ID, and the TRACEO_FOUNDING_OFFER switch. Missing configuration is reported as “not configured” rather than crashing.

The handlers

Every billing write runs the same check ladder (managingActor): no session gets 401, no tenant gets 409, and a role below owner/admin gets 403. That 403 is the module’s only one, and it is a role gate, never a plan gate. The actor is resolved live from the database ("user" → workspace.tenant_id), so a just-demoted admin on a cached cookie cannot start a checkout.

sequenceDiagram
participant O as Owner browser
participant W as /api/billing/checkout
participant DB as Postgres
participant T as TOLL gate
participant P as Polar
O->>W: POST interval month
W->>DB: session user to workspace.tenant_id and role
W->>W: 401, 409, 403 ladder, product for interval
W->>T: toll.checkout productId, subject traceo:tenant, env, discount traceo_founding if offer on
T->>P: create checkout (once-per-customer check)
T-->>W: url
W->>W: refuse unless https
W-->>O: url
O->>P: pays
P-->>T: subscription
O->>W: GET /api/billing/entitlements
W->>T: toll.entitlements subject
T-->>W: capabilities traceo.team
W-->>O: state team, advisory none

If TOLL refuses or cannot provision the founding discount, checkout returns 502 CHECKOUT_DISCOUNT_REFUSED or CHECKOUT_DISCOUNT_UNAVAILABLE and does not retry at list price. The page promised $29, so silently charging $39 is ruled out.

flowchart TD
E["GET entitlements"] --> S{"state from TOLL"}
S -- "team" --> NONE["no advisory"]
S -- "solo" --> CNT["count seats: users whose workspace is in the tenant"]
CNT -- "count failed" --> UNK["caps shown, no warning"]
CNT -- "over 1 seat" --> WARN["advisory warning, nothing blocked"]
S -- "TOLL did not answer" --> UNR["state unreachable: never cached, never read as solo"]
S -- "TOLL vars missing" --> NC["state not_configured: no TOLL call"]

Only team and solo answers are cached (plan-cache.ts). Each response carries checkedAt and cached, so a cached answer is never presented as a fresh one.

Where M-22 stands

Live payments are out of reach until TOLL’s production phase (P3), per MILESTONES.md M-22. G-22-7, a reviewed, unmerged live-switch PR, is discharged by #330. G-22-9 (PLAY-010 demand gate) needs N external prospects confirming they would pay, with dated contacts. It is blocked on an operator ruling of the threshold and has no evidence yet. The M-22 verification round (G-22-8) is blocked on G-22-9. G-22-3 (the sandbox revenue drill) was discharged on 2026-10-25 by the plan-change drill finding (traceo-cat 8bbe2d5, #386). The gates.yaml comment on that gate says traceo-web Production carries the sandbox annual product id, to be swapped for the live id at the live switch. The Vercel env itself was not read for this card, so production TOLL_ENV = sandbox is UNVERIFIED.

Key Terms

  • Capability → a named entitlement TOLL grants to a subject. Traceo uses exactly one, traceo.team.
  • Subject → traceo:<tenant_id>. Billing attaches to the tenant, not to a user.
  • Advisory limit → a Solo cap that is displayed and warned about but never enforced in code.
  • Codeless discount → a Polar discount applied by TOLL at checkout without the customer typing a code.

Q&A

Q: What is the TOLL subject for a Traceo customer, and which capability does Team grant? A: traceo:<tenant_id> (namespace traceo), and the capability is traceo.team, the same for monthly and annual.

Q: What does Traceo do in code differently for a Team tenant? A: It stops showing the Solo advisory. That is the only code effect (0101). There is no plan gate anywhere, and the only 403 in the billing module is the owner/admin role check.

Q: How is the $29 founding price delivered, and why is it not a separate product? A: TOLL applies a codeless 25.64% Polar discount (traceo_founding), repeating for 12 months, to the $39 list product, once per customer. A separate founding product would carry a price that never expires. With the discount, Polar reverts to list on its own after 12 months (0103).

Q: Who may start a checkout, and what happens if TOLL refuses the founding discount? A: Only an owner or admin of a tenant. On a refused or unprovisioned discount, the handler returns 502 with a distinct code and starts no checkout. It never falls back to list price.

Examples

The operator closes the founding window by setting TRACEO_FOUNDING_OFFER off. Nothing observes first production checkout automatically, so closing is an operator act. New checkouts then go to list price with no discount. Tenants already on the discount keep it until their 12 months run out, because Polar holds that state, not Traceo.

neighbors on the map