Agent-native onboarding — zero to first routed completion in 2 API calls, no invite, no email, no human

2026-07-11

onboardingregistersignupcommunity-tierprovisioning

LOCKSTEP TRACEABILITY MATRIX --- api_endpoints: [ "POST /v1/register (invite_code now optional — sandbox path)", "POST /v1/account/verify-email", "POST /v1/account/verify-email/confirm", "POST /v1/signup (typed 503 when email delivery unavailable)", ] sdk_methods_updated: [ "BrainstormRouter.register() — inviteCode/adminEmail now optional (TS + Python, sync + async)", "system.account_verify_email.* — auto-generated via gen:contract", ] mcp_tools_updated: ["none — registration is pre-auth; discovery surfaces updated instead"] ---

What We Built

An autonomous agent can now go from discovering BrainstormRouter to a successful routed completion in two API calls with no human in the loop: POST /v1/register {tenant_name, accept_tos} returns an instantly-active sandbox tenant with a community-tier br_live_ key, and the first POST /v1/chat/completions succeeds with zero BYOK (a simulated completion until a provider key is registered). The response advertises its own limits and a graduated upgrade ladder (email verify → $5/day standard tier via the new /v1/account/verify-email endpoints; claim URL → full dashboard account; invite code → full tenant at registration).

Every previously-dead signup path was also fixed: /v1/signup now returns a typed 503 ErrorEnvelope (email_delivery_unavailable, recovery.action: "use_alternate_endpoint") instead of claiming "code sent" when the deployment can't deliver email — and the email send is now awaited, never fire-and-forget. Dashboard OAuth provisioning auto-activates new tenants as sandbox when BRAINSTORMROUTER_OPEN_SIGNUP=1 (one-env-var rollback to the legacy pending-approval flow), finally matching what the quickstart docs have promised since March.

Why It Matters

Before this, all three signup paths hard-stopped an unattended agent: /v1/register demanded an admin-minted invite code, /v1/signup needed an email inbox AND silently never sent the code in production (no RESEND_API_KEY in the task definition, log.warn only), and dashboard OAuth parked new tenants in manual pending_approval — contradicting the docs. Agents discovering BR through its own MNI surfaces were led straight into these dead ends and gave up. For a platform whose persona is AI agents, onboarding IS the product surface.

How It Works

No new systems — the sandbox path composes what already existed. The one real plumbing gap was createApiKeyInTransaction having no tier parameter despite the column and enforcement middleware existing since March:

  • Community-tier middleware (src/api/middleware/community-tier.ts) enforces 100 req/day, 100K tokens/day, 2 concurrent, cost-first routing on tier="community" keys — sandbox keys are minted with that tier (limits now exported as COMMUNITY_TIER_LIMITS so the register response advertises the same numbers the middleware enforces).
  • Budget killswitch bounds spend via budget_limit_usd: 2.00 / daily on the key.
  • Sandbox simulator answers unkeyed completions (a completion-shaped mock, model brainstorm/sandbox) — no real provider is ever touched without BYOK.
  • Claim tokens (identity bridge) remain the dashboard upgrade path.
  • ErrorEnvelope gained one recovery action (use_alternate_endpoint) and one code (email_delivery_unavailable).

Abuse is bounded in Redis with the same pattern as the existing register limiter: 5/h per IP overall, 3/day per IP for no-invite registrations, and a global daily circuit breaker (BRAINSTORMROUTER_SANDBOX_DAILY_CAP, default 200) that 429s with quota_reset_at_iso. Platform fleet-spend exposure from unkeyed sandbox tenants: $0 — verified live 2026-07-12: without BYOK, completions return a simulated response and never touch a real provider. The $2/day key budget only governs the tenant's own BYOK spend. No CAPTCHA/PoW — those block exactly the persona being onboarded.

The tier upgrade reuses email_verification_tokens (new nullable tenant_id column, migration v64): POST /v1/account/verify-email sends a code bound to the calling tenant; /confirm promotes all community keys to standard tier ($5/day) and the tenant from plan=sandbox to free in one transaction.

Discovery surfaces were rewritten so the FIRST path an agent reads is the one that works unattended: llms.txt bootstrap front-matter now points at /v1/register, agents.json declares requires_invite_code: false with a full sandbox block, and the agent playbook leads with the one-call sandbox flow.

The Numbers

  • 2 API calls from zero to first routed completion (was: impossible without human intervention)
  • 3 signup paths fixed (register, signup, OAuth provision)
  • $0 platform fleet-spend exposure (unkeyed sandbox completions are simulated); per-tenant $2/day budget on their own BYOK spend
  • 1 plumbing change unlocked it all (tier param on key creation)

Lockstep Checklist

  • [x] API Routes: /v1/register (optional invite), /v1/account/verify-email + /confirm (new), /v1/signup (typed 503)
  • [x] TS SDK: RegistrationParams.inviteCode/adminEmail optional, RegistrationResult.limits/upgrade_paths; generated resources via pnpm gen:contract
  • [x] Python SDK: register() sync + async signatures updated; generated resources via pnpm gen:contract
  • [x] MCP/agents.json: agents.json registration + sandbox blocks; llms.txt + agent playbook reordered (registration is pre-auth, so no MCP tool changes)
  • [x] Migration: v64 — email_verification_tokens.tenant_id
  • [x] Ops: terraform/fargate-minimal — RESEND_API_KEY secret, BRAINSTORMROUTER_OPEN_SIGNUP, BRAINSTORMROUTER_SANDBOX_DAILY_CAP
  • [x] Tests: src/api/capabilities/system/register.test.ts — sandbox registration, tier/budget minting, invite rejection envelope, signup 503