Agent provisioning
Stand up new billable child tenants with a single authenticated API call — no human, no inbox.
Normal account creation needs a human to read an OTP email. That's a wall for an agent, a CI job, or a fleet that needs to provision its own isolated workspaces on demand. Agent provisioning removes the human from every account after the first: one human-onboarded, payment-backed parent tenant mints a provisioning key once, and from then on an agent can create as many child tenants as it needs with a single API call.
This is Tier-1: operator/internal provisioning. Provisioning keys are minted
by an org owner/admin (SETTINGS_UPDATE) for that org's own use — it is not
yet a self-serve, publicly documented reseller product.
Minting a provisioning key
A provisioning key (tt_prov_…) is minted from an already-authenticated,
cookie-based session — never from a PAT or another provisioning key (that
privilege-chaining path is closed):
mutation {
createProvisioningKey(
input: { name: "sandbox-fleet", maxTenants: 50, expiresInDays: 90 }
) {
id
secret # shown once
}
}maxTenants and expiresInDays are both optional; omitting maxTenants
means unlimited child tenants for that key. The key carries exactly one
scope, provision:write, and is bound to the tenant and user that minted
it. Treat it as a high-privilege, server-side-only secret — it can mint
billable tenants and must never ship in client code.
Provisioning a tenant
curl https://api.aisetu.ai/provision/tenant \
-H "Authorization: Bearer $AI_SETU_PROVISIONING_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Child Co",
"region": "in",
"billingMode": "delegated"
}'{
"tenantId": "...",
"workspaceId": "...",
"apiKey": "tt_live_...",
"topUpUrl": "https://..."
}region (eu | in) defaults to the pod's own region if omitted.
billingMode defaults to delegated. Secrets in the response are shown
once — there is no way to retrieve apiKey again after this call.
Programmatically, the same call is admin.tenants.provision({...}) from
@ai-setu/admin, which throws a clear error if the client wasn't
constructed with a provisioning key — a PAT alone cannot provision tenants,
by design. An MCP provision_tenant tool is also available and registers
only when AI_SETU_PROVISIONING_KEY is set on the MCP server; see
MCP for the full tool list and setup.
Billing modes
| Mode | Status | Who pays |
|---|---|---|
delegated | Default, fully wired | The child tenant funds itself via the returned topUpUrl — identical to a normal new org that starts at $0. |
pooled | Not implemented in Tier-1 | Would draw on the parent's balance/payment method across the tenant boundary. Calling with billingMode: "pooled" returns a validation error; cross-tenant balance draw is deliberately not half-wired. |
Quotas
maxTenants on the key (if set) is enforced per call, counting only
tenants previously created by that specific key — never a parent-wide or
cross-key count. The N+1 call over the limit is refused before any tenant is
created.
Scope and isolation
- The provisioning verifier only ever accepts the
tt_prov_prefix; it is a separate credential kind from both workspace API keys (tt_live_/tt_test_) and PATs (tt_pat_). Att_prov_key is rejected on every ordinary workspace/PAT-authenticated route, and a PAT is rejected onPOST /provision/tenant. - The
provision:writescope is checked twice — once when the credential is resolved, once again inside the provisioning service — so a bug in one layer can't turn into a privilege escalation in the other. - Every child tenant is tagged with the provisioning key and parent tenant that created it, and every provisioning call writes an audit log entry.
What's next
Key rotation/revocation UI, an IP allowlist and per-key rate limit, a
parent-side list/suspend view of provisioned children, and pooled billing
are all Tier-2 work and not available yet.