Reseller

Resell browser automation with sub-tenants

Provision a customer as a real tenant with its own key and library, decide whether they pay or you do, and reconcile the ledger — in two endpoints.

If you are embedding browser automation inside your own product, your customers are your tenants, not ours. Sub-tenant provisioning makes that structural rather than something you simulate with one shared key and your own bookkeeping. This guide covers the one call that creates a customer, the billing decision you cannot change afterwards, and the four errors that mean you got something wrong.

You need a scoped key first

Provisioning requires the tenants:provision scope. A default key carries run, skills:read and skills:write and nothing else, so calling POST /api/v1/tenants with one is a 403 rather than a silent failure. That separation is the point: the key your product runs on should not be able to create billing relationships.

Decide the billing mode before you call

It is fixed at creation. "self" gives the sub-tenant its own balance, which you can seed with an opening grant transferred from yours — they run out when they run out. "parent" bills your pool for everything they do, with each charge attributed back to the sub-tenant that made it. Choose "self" for a self-serve product where the customer buys credits; choose "parent" when browser automation is an included feature of your own plan.

Provision the customer

One call creates the tenant, mints its key and fixes the relationship. The API key appears once, in the 201 response, and there is no endpoint that returns it again — write it to your own secret store as you read the response.

Create a parent-billed sub-tenantbash
curl -X POST https://twin-browser.com/api/v1/tenants \
  -H "Authorization: Bearer $TWIN_RESELLER_KEY" \
  -H "content-type: application/json" \
  -d '{ "name": "Acme Corp", "billing": "parent" }'

# → 201 {
#     "tenant": { "id":"…", "name":"Acme Corp", "slug":"acme-corp",
#                 "plan":"…", "billing":"parent" },
#     "apiKey": "ab_live_…",      ← shown ONCE
#     "granted": 0
#   }

# Self-billed with an opening grant from your balance instead:
#   { "name": "Acme Corp", "billing": "self", "credits": 5000 }

The four errors, and what each means

Each of these is a distinct mistake, and the status code tells you which.

  • 403 — your key lacks the tenants:provision scope. Use the reseller key.
  • 400 — `credits` on a parent-billed sub-tenant. An opening grant only makes sense when they have their own balance.
  • 402 — you do not have the credits to fund the grant you asked for. The grant is a transfer from your balance.
  • 409 — nested resellers. A sub-tenant cannot provision sub-tenants of its own; the hierarchy is one level deep.

Reconciling the ledger

GET /api/v1/tenants lists your sub-tenants with their balances and what they have billed to you. Under "parent" billing that second number is your cost of goods for that customer, and it is the input to whatever you invoice them — the platform does not set per-sub-tenant pricing, so your margin is the difference between the rate card and your own.

The reseller ledgerbash
curl "https://twin-browser.com/api/v1/tenants" \
  -H "Authorization: Bearer $TWIN_RESELLER_KEY"

# { "subtenants": [
#     { "id":"…", "name":"Acme Corp", "billing":"parent",
#       "balance": 0, "billed_to_parent": 1240 },
#     { "id":"…", "name":"Globex",    "billing":"self",
#       "balance": 3760, "billed_to_parent": 0 }
# ] }

What a sub-tenant does and does not share with you

A sub-tenant is a tenant: its own API key, skill library, credential vault and run history, isolated under default-deny row-level security. It cannot see your skills or secrets and you cannot see its vault. The only surface that crosses any tenant boundary anywhere is the sanitized shared skill corpus, which carries action structure and nothing else.

Making their runs cheap is your margin

Under "parent" billing, every credit a sub-tenant spends is yours. That makes the cost engine a margin lever rather than a nice-to-have: routing their work through dispatch so repeat intents replay at 2 credits instead of compiling at 10 is a direct reduction in what you pay. GET /api/v1/cache/stats on each sub-tenant's key tells you where that is and is not happening.

Keep going

The capabilities behind it

Vocabulary

Common questions

Can I change who pays after creating a sub-tenant?
The billing mode is chosen at creation and is not a field on an update. Decide before you call — "self" for a customer buying credits, "parent" when automation is bundled into your own plan.
How do I retrieve a sub-tenant's API key later?
You do not. It is returned once in the 201 response and there is no read endpoint for it. Capture it as you read the response.
Can a sub-tenant see my skills or secrets?
No. Each sub-tenant is a full tenant with its own library, vault and runs under default-deny row-level security. The only cross-tenant surface anywhere is the sanitized shared skill corpus, which carries action structure and no tenant data.
Can my customers provision their own customers?
No — nesting is rejected with a 409. The hierarchy is deliberately one level deep.

Delegate the work. Keep the decision.

Hand off a real task, set the guardrails, and let repeated work compile into a skill that replays deterministically at near-zero cost.