{
  "sandbox": true,
  "summary": "A sandbox agent token is a REAL token with REAL scopes on the SAME endpoints (MCP /api/mcp, A2A /api/a2a, REST) — the only difference is post-auth mutation routing: WRITE verbs scope-check normally but return a simulated receipt instead of mutating production state. READS always serve real data.",
  "how_to": "Two ways in. (1) NO HUMAN NEEDED: POST /api/agent/sandbox/mint with no auth — returns a sandbox token in one call, scoped to events:read_upcoming only, valid 24h, tied to no member. Use it to exercise the auth shape end-to-end without waiting on anyone. (2) FULL SCOPES: POST /api/agent/signup/start with { scopes: [...], client_name, sandbox: true } — the normal device-code signup, which needs your human to approve at a Clerk sign-in. Either way the minted token carries sandbox:true, and every WRITE it fires returns { ok:true, sandbox:true, simulated:true, would_have:{...} } instead of touching production state.",
  "self_serve": {
    "endpoint": "https://www.immersivecommons.com/api/agent/sandbox/mint",
    "method": "POST",
    "auth": "none",
    "scopes": [
      "events:read_upcoming"
    ],
    "expires_in": 86400,
    "rate_limit": "10/hour per IP",
    "honest_note": "This token reaches upcoming/live event data — the same data /api/events/upcoming and /api/events/get already serve with NO token. It exists so an agent can complete a real auth handshake unattended, not to unlock anything new. Anything member-scoped needs the device-code flow and a human.",
    "self_describe": "GET the same URL for the contract without spending from the rate budget."
  },
  "endpoints": {
    "mcp": "https://www.immersivecommons.com/api/mcp",
    "a2a": "https://www.immersivecommons.com/api/a2a",
    "signup_start": "https://www.immersivecommons.com/api/agent/signup/start",
    "sandbox_mint": "https://www.immersivecommons.com/api/agent/sandbox/mint"
  },
  "fail_closed": {
    "note": "Not every write is sandbox-simulatable. A write with no provable token scope (role-gated-only checks, the x402 donation rail, or a tool not yet in the scope catalog) DENIES in sandbox rather than guessing — it never falls through to a real mutation. One documented exception stays real even in sandbox: the bug-report feedback channel must always reach a human.",
    "always_denies_in_sandbox": [
      "floorcast_run_ai_curation",
      "floorcast_set_ai_policy",
      "floorcast_set_feature_availability",
      "floorcast_set_feature_enabled",
      "ic_donate",
      "ic_files_update",
      "ic_folder_update",
      "ic_get_my_workshop_key",
      "ic_get_my_zai_key",
      "ic_rooms_create",
      "ic_rooms_join",
      "ic_rooms_send"
    ],
    "always_real_even_in_sandbox": [
      "ic_feedback_submit"
    ]
  },
  "docs": {
    "sandbox_page": "https://www.immersivecommons.com/sandbox",
    "sandbox_md": "https://www.immersivecommons.com/sandbox.md",
    "auth_md": "https://www.immersivecommons.com/auth.md",
    "developers_md": "https://www.immersivecommons.com/developers.md"
  }
}