{"openapi":"3.1.0","info":{"title":"Sandbox Platform","description":"Cloud **sandboxes, browsers and desktops for AI agents** — plus CLIs to drive them.\n\n**New here? Read [`/llms.txt`](/llms.txt) first** — the agent bootstrap with the full getting-started flow. Quick version:\n\n**1. Get an API key (headless, no browser):**\n- `POST https://auth.service.ab0t.com/organizations/sandbox-platform-users/auth/register` `{email,password,name}` → returns a JWT. (Use this **org-scoped** URL, *not* bare `/auth/register` — that lands you in a permission-less personal org.)\n- Try minting immediately — it often works right away. If it returns `403 \"permissions you don't hold\"`, perms are still provisioning (async, sometimes a few minutes) = *not ready yet*, not wrong — poll with backoff until it succeeds.\n- `POST https://auth.service.ab0t.com/api-keys/` with `Authorization: Bearer <JWT>` and `{\"permissions\":[\"sandbox.create.sandboxes\",\"sandbox.read.sandboxes\"]}` → `ab0t_sk_...`.\n\n**2. Call this API** with either header (both accepted on every route):\n`X-API-Key: ab0t_sk_...` (agents) or `Authorization: Bearer <JWT>`. WebSocket terminal uses `?token=<JWT>`; auth-fail closes with code 4001.\n\n**3. First calls:** `POST /api/sandboxes` (perm `sandbox.create.sandboxes`), `GET /api/sandboxes`, `POST /api/sandboxes/{id}/execute`, `DELETE /api/sandboxes/{id}`.\n\n**CLIs:** `GET /cli` lists them — `browser-agent` (web automation) and `computer-agent` (desktop GUI automation). Note the two distinct lanes: provisioning **workflows** run headless step DAGs via `/workflow/execute`, while **desktop automation** drives a live GUI on a desktop sandbox — a separate execution path. Per-CLI docs at `/cli/{name}/llm.txt`.\n\nMulti-tenant: you only see your own org's resources. Cost tracking is automatic.","version":"1.0.0"},"paths":{"/api/billing/balance":{"get":{"tags":["Billing"],"summary":"Get Balance","operationId":"get_balance_api_billing_balance_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingBalanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/usage/summary":{"get":{"tags":["Billing"],"summary":"Get Usage Summary","operationId":"get_usage_summary_api_billing_usage_summary_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingUsageSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/usage/records":{"get":{"tags":["Billing"],"summary":"Get Usage Records","operationId":"get_usage_records_api_billing_usage_records_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingUsageRecordsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/transactions":{"get":{"tags":["Billing"],"summary":"Get Transactions","operationId":"get_transactions_api_billing_transactions_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingTransactionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/subscriptions":{"get":{"tags":["Payments"],"summary":"Get Subscriptions","operationId":"get_subscriptions_api_payments_subscriptions_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/invoices":{"get":{"tags":["Payments"],"summary":"Get Invoices","operationId":"get_invoices_api_payments_invoices_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/invoices/{invoice_id}/pdf":{"get":{"tags":["Payments"],"summary":"Get Invoice Pdf","description":"Returns JSON `{url, expires_in}` where `url` is a short-lived signed PDF URL (S3 pre-sign, or Stripe-hosted invoice_pdf for mirrored subscription invoices). The browser GETs the signed URL directly — no Authorization header needed on the signed URL itself.","operationId":"get_invoice_pdf_api_payments_invoices__invoice_id__pdf_get","parameters":[{"name":"invoice_id","in":"path","required":true,"schema":{"type":"string","title":"Invoice Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/methods":{"get":{"tags":["Payments"],"summary":"Get Payment Methods","operationId":"get_payment_methods_api_payments_methods_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/subscriptions/{subscription_id}":{"delete":{"tags":["Payments"],"summary":"Cancel Subscription","operationId":"cancel_subscription_api_payments_subscriptions__subscription_id__delete","parameters":[{"name":"subscription_id","in":"path","required":true,"schema":{"type":"string","title":"Subscription Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSubscriptionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/methods/{method_id}/default":{"put":{"tags":["Payments"],"summary":"Set Default Method","operationId":"set_default_method_api_payments_methods__method_id__default_put","parameters":[{"name":"method_id","in":"path","required":true,"schema":{"type":"string","title":"Method Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodSetDefaultResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/methods/{method_id}":{"delete":{"tags":["Payments"],"summary":"Remove Method","operationId":"remove_method_api_payments_methods__method_id__delete","parameters":[{"name":"method_id","in":"path","required":true,"schema":{"type":"string","title":"Method Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/topup":{"post":{"tags":["Payments"],"summary":"Create Topup","description":"Create a Stripe Checkout session for an account balance top-up (one-time payment, USD). The browser must be redirected to the returned `url` to complete payment. Capped at $10,000 per call. Admin-gated: a top-up immediately initiates a charge against the org's saved payment method, so this is a write operation, not a read.","operationId":"create_topup_api_payments_topup_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_create_topup_api_payments_topup_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/plans":{"get":{"tags":["Payments"],"summary":"Get Plans","operationId":"get_plans_api_payments_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlansResponse"}}}}}}},"/api/payments/checkout/init":{"post":{"tags":["Payments"],"summary":"Init Checkout","description":"Issue an anti-fraud session token + browser fingerprint hash that the client must replay to the anonymous checkout endpoint. Public — intended to be called from the pricing page before the user has an account.","operationId":"init_checkout_api_payments_checkout_init_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutInitResponse"}}}}}}},"/api/payments/checkout/anonymous/{plan_id}":{"post":{"tags":["Payments"],"summary":"Create Anonymous Checkout","description":"Account-first anonymous checkout: provisions the customer's account (when `auth_url`/`auth_org_slug` are configured), creates a Stripe checkout session for the chosen plan, and returns the Stripe URL to redirect the browser to. When account creation succeeds, the response also includes the new `org_id` and a JWT `access_token` so the client can sign the user in once they return from Stripe. Sets a `checkout_intent` cookie used by the success page to recover state.","operationId":"create_anonymous_checkout_api_payments_checkout_anonymous__plan_id__post","parameters":[{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_create_anonymous_checkout_api_payments_checkout_anonymous__plan_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnonymousCheckoutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/checkout/complete":{"post":{"tags":["Payments"],"summary":"Complete Checkout","description":"Verify a returned Stripe checkout session and synchronise the customer's tier with the billing service. Idempotent — safe to call multiple times. When `tier_synced` is false but a tier was resolved, the Stripe webhook will retry the sync (`tier_pending=true`). Requires authentication: the caller's `org_id` must match the session's `metadata.org_id` (or the DynamoDB-tracked account org for anonymous-checkout flows). Without auth, anyone holding a Stripe `session_id` could read the customer's email and tier — audit ticket 20260428 finding A3.","operationId":"complete_checkout_api_payments_checkout_complete_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_complete_checkout_api_payments_checkout_complete_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutCompleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/checkout/{plan_id}":{"post":{"tags":["Payments"],"summary":"Create Checkout","operationId":"create_checkout_api_payments_checkout__plan_id__post","parameters":[{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/payments/portal":{"post":{"tags":["Payments"],"summary":"Create Portal","operationId":"create_portal_api_payments_portal_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli":{"get":{"tags":["CLI Downloads"],"summary":"List available CLI tools","description":"Discover which CLIs this sandbox-platform serves and where to get them.","operationId":"list_clis_cli_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/cli/{name}":{"get":{"tags":["CLI Downloads"],"summary":"CLI info + download URLs","operationId":"get_cli_info_cli__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/version":{"get":{"tags":["CLI Downloads"],"summary":"Current version (plain text)","operationId":"get_cli_version_cli__name__version_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/checksums.txt":{"get":{"tags":["CLI Downloads"],"summary":"Per-platform sha256 checksums (if shipped)","operationId":"get_cli_checksums_cli__name__checksums_txt_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/llm.txt":{"get":{"tags":["CLI Downloads"],"summary":"LLM-readable usage doc for the CLI","operationId":"get_llm_txt_cli__name__llm_txt_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/skills":{"get":{"tags":["CLI Downloads"],"summary":"List skills available for this CLI","operationId":"list_cli_skills_cli__name__skills_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/skills/{skill_name}.zip":{"get":{"tags":["CLI Downloads"],"summary":"Download a skill as a zip","operationId":"download_skill_zip_cli__name__skills__skill_name__zip_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"skill_name","in":"path","required":true,"schema":{"type":"string","title":"Skill Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/install.sh":{"get":{"tags":["CLI Downloads"],"summary":"Install script for the named CLI","operationId":"get_install_script_cli__name__install_sh_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/cli/{name}/{platform_arch}":{"get":{"tags":["CLI Downloads"],"summary":"Download the platform-specific binary","operationId":"download_cli_binary_cli__name___platform_arch__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"platform_arch","in":"path","required":true,"schema":{"type":"string","title":"Platform Arch"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/login":{"get":{"tags":["Auth"],"summary":"Login Redirect","description":"Trigger the OAuth login flow directly.\n\nRenders a minimal page that calls auth.loginWithRedirect() immediately.\nThe return_to param is saved in sessionStorage so the user lands on the\nright page after login. No landing page detour.","operationId":"login_redirect_login_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/signup":{"get":{"tags":["Auth"],"summary":"Signup Redirect","description":"Trigger the OAuth flow with `screen_hint=signup` so the hosted IdP\npage opens in registration mode rather than login mode.\n\nThe AuthMesh SDK threads `screen_hint` through to the auth service as\na query param (see `loginWithRedirect` in `authmesh.global.js`). The\nhosted login org renders the registration form when it sees this hint.\n\nFalls back gracefully if the user is already authenticated — they\njust go to `return_to` (default /dashboard) instead.","operationId":"signup_redirect_signup_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/pricing/rates":{"get":{"tags":["Pricing"],"summary":"Get Pricing Rates","description":"Public endpoint: current customer-facing pricing rates for all products (sandbox, browser, desktop, GPU variants, etc.). Served from quota-config.json — the single source of truth — and intended for the pricing widget. Internal cost figures are never exposed here.","operationId":"get_pricing_rates_api_pricing_rates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PricingRatesResponse"}}}}}}},"/terms":{"get":{"tags":["HTML"],"summary":"Terms Page","description":"Terms of Service (HTML page).\n\nReturns the Terms of Service as an HTML page for browser viewing.","operationId":"terms_page_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/privacy":{"get":{"tags":["HTML"],"summary":"Privacy Page","description":"Privacy Policy (HTML page).\n\nReturns the Privacy Policy as an HTML page covering data collection,\nusage, GDPR/CCPA rights, and data retention practices.","operationId":"privacy_page_privacy_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/faq":{"get":{"tags":["HTML"],"summary":"Faq Page","description":"Frequently Asked Questions (HTML page).\n\nReturns the FAQ as an HTML page with common questions about the platform,\nAPI usage, billing, and troubleshooting.","operationId":"faq_page_faq_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/sandboxes":{"post":{"tags":["Sandboxes"],"summary":"Create Sandbox","description":"Create a new isolated Linux sandbox environment.\n\n## Who Should Use This\nExternal API users who need isolated Linux environments for AI agents, code execution,\nor development workloads. Requires `sandbox.create.sandboxes` permission.\n\n## When To Use\n- Starting a new AI agent that needs a persistent Linux environment\n- Running untrusted code in isolation\n- Creating development/testing environments\n- Setting up long-running automation tasks\n\n## How To Use\n1. Call this endpoint with desired instance_type and optional docker_image\n2. Receive sandbox_id and wait for status to become \"running\"\n3. Use `/api/sandboxes/{sandbox_id}/execute` to run commands\n4. Use `/api/sandboxes/{sandbox_id}/files` to save files\n5. When done, call DELETE `/api/sandboxes/{sandbox_id}` or POST `.../stop`\n\n## Example Workflow\n```\nPOST /api/sandboxes {\"name\": \"my-agent\", \"instance_type\": \"t3.small\"}\n→ Returns sandbox_id=\"sandbox_abc123\"\n\nPOST /api/sandboxes/sandbox_abc123/execute {\"command\": \"python script.py\"}\n→ Returns stdout, stderr, exit_code\n\nDELETE /api/sandboxes/sandbox_abc123\n→ Sandbox terminated, billing stops\n```\n\n## Related Endpoints\n- GET /api/sandboxes - List your sandboxes\n- GET /api/sandboxes/{id} - Get sandbox details\n- POST /api/sandboxes/{id}/execute - Run commands\n- POST /api/sandboxes/{id}/stop - Stop (pause billing)\n- DELETE /api/sandboxes/{id} - Permanently delete\n\n## Permissions Required\n- `sandbox.create.sandboxes` - Create new sandboxes\n\n## Technical Notes\nCreates a new sandbox for a user. Provisions cloud infrastructure and starts cost tracking.","operationId":"create_sandbox_api_sandboxes_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Sandboxes"],"summary":"List Sandboxes","description":"List all sandboxes owned by the authenticated user.\n\n## Who Should Use This\nExternal API users who need to view their sandbox inventory. Requires `sandbox.read.sandboxes` permission.\n\n## When To Use\n- Checking what sandboxes you currently have\n- Finding a sandbox_id to connect to\n- Monitoring sandbox statuses and costs\n- Building dashboards or management UIs\n\n## How To Use\n1. Call with optional `status` filter (\"running\", \"stopped\", \"pending\")\n2. Returns list of sandboxes with current costs\n3. Use sandbox_id from results with other endpoints\n\n## Example\n```\nGET /api/sandboxes?status=running&limit=10\n→ Returns {\"sandboxes\": [...], \"total\": 3}\n```\n\n## Related Endpoints\n- POST /api/sandboxes - Create new sandbox\n- GET /api/sandboxes/{id} - Get detailed info for one sandbox\n- GET /api/costs/user/current - Get cost summary\n\n## Permissions Required\n- `sandbox.read.sandboxes` - View sandboxes\n\n## Full-visibility contract (ticket 20260810 P5)\nThe DEFAULT view is active-only (clean): terminal rows (deleted, terminated)\nare hidden. Pass `include=all` (or `?state=<status>` for one explicit state)\nfor the ADVANCED / developer view, which surfaces the user's sandboxes in\nEVERY state — stopped, error, deleted, out-of-band `terminated` — each\nlabelled with `is_active` + `counts_against_quota` and NOT re-counted against\nquota. This is what lets a user find a deleted/terminated box and reach its\nretained storage. \"Listed == real\" is enforced upstream: the\nRealStateReconciler flips a phantom-running row to `terminated` (hidden from\nthe default view) as soon as resource-service confirms its cloud backing is\ngone, so the default list never shows a box that AWS already deleted.\n\n## Technical Notes\nList sandboxes for current user (authentication required). Includes real-time cost calculations for running sandboxes.","operationId":"list_sandboxes_api_sandboxes_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"include","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Include"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}":{"get":{"tags":["Sandboxes"],"summary":"Get Sandbox","description":"Get detailed information about a specific sandbox.\n\n## Who Should Use This\nExternal API users who need sandbox details. Requires `sandbox.read.sandboxes` permission.\n\n## When To Use\n- Checking if a sandbox is ready after creation\n- Getting current status and IP address\n- Monitoring costs for a specific sandbox\n- Verifying sandbox configuration\n\n## How To Use\n1. Call with sandbox_id from create response or list\n2. Check `status` field: \"pending\" → \"running\" → \"stopped\"\n3. Once \"running\", use instance_ip to connect or use execute endpoint\n\n## Example\n```\nGET /api/sandboxes/sandbox_abc123\n→ Returns {sandbox_id, status, instance_ip, hourly_cost, current_cost, ...}\n```\n\n## Related Endpoints\n- GET /api/sandboxes - List all sandboxes\n- POST /api/sandboxes/{id}/execute - Run commands (if running)\n- GET /api/sandboxes/{id}/metrics - Get CPU/memory metrics\n\n## Permissions Required\n- `sandbox.read.sandboxes` - View sandbox details (own sandboxes)\n- `sandbox.admin.sandboxes` - View any sandbox in organization\n\n## Technical Notes\nGet sandbox details. Returns real-time status and calculated current cost.","operationId":"get_sandbox_api_sandboxes__sandbox_id__get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Sandboxes"],"summary":"Delete Sandbox","description":"Permanently delete a sandbox and all its data.\n\n## Who Should Use This\nExternal API users who want to permanently remove a sandbox. Requires `sandbox.delete.sandboxes` permission.\n\n## When To Use\n- Finished with a sandbox and want to clean up\n- AI agent task is complete\n- Want to stop all billing for this sandbox\n- NOT when you just want to pause (use POST .../stop instead)\n\n## How To Use\n1. Call with sandbox_id to delete\n2. Sandbox is terminated immediately\n3. All data is permanently removed\n4. Billing stops\n\n## Warning\nThis action is IRREVERSIBLE. All files and data in the sandbox will be permanently deleted.\nIf you just want to pause billing, use POST `/api/sandboxes/{id}/stop` instead.\n\n## Example\n```\nDELETE /api/sandboxes/sandbox_abc123\n→ {\"message\": \"Sandbox deleted\", \"sandbox_id\": \"sandbox_abc123\"}\n```\n\n## Related Endpoints\n- POST /api/sandboxes/{id}/stop - Pause without deleting\n- GET /api/sandboxes - List remaining sandboxes\n- POST /api/sandboxes - Create new sandbox\n\n## Permissions Required\n- `sandbox.delete.sandboxes` - Delete own sandboxes\n- `sandbox.admin.sandboxes` - Delete any sandbox in organization\n\n## Technical Notes\nDelete a sandbox permanently. Terminates infrastructure and removes from database.","operationId":"delete_sandbox_api_sandboxes__sandbox_id__delete","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/plan":{"get":{"tags":["Sandboxes"],"summary":"Get Sandbox Plan","description":"Owner-scoped, read-only deployment PLAN / launch template for a sandbox (redacted JSON).\n\nDev transparency (ticket 20260816_launch_template_dev_visibility): \"show me\nexactly what will run on MY box.\" The JSON response IS the download.\n\nTenancy: owner / same-org admin / cross-tenant only. A missing OR foreign\nsandbox → 404 (never an existence oracle — mirrors verify_blueprint_access).\n\nRedaction: no live credential ever appears. `manager_api_token`, SSH private\nmaterial, and any secret-named metadata are dropped and listed in\n`redacted_fields`; raw SSH public keys are summarized to `ssh_key_count`.\n\nCoverage: honest — `plan_source=ec2_launch_template`, `coverage=summary`; the\nfull user-data lives in the resource service and is not reproduced here.\n\n## Permissions Required\n- `sandbox.read.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"get_sandbox_plan_api_sandboxes__sandbox_id__plan_get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/stop":{"post":{"tags":["Sandboxes"],"summary":"Stop Sandbox","description":"Stop a running sandbox to pause billing while preserving state.\n\n## Who Should Use This\nExternal API users who want to pause a sandbox without deleting it. Requires `sandbox.write.sandboxes` permission.\n\n## When To Use\n- Pausing work overnight to save costs\n- Temporarily stopping an AI agent\n- Reducing costs while keeping sandbox data intact\n- NOT when you want to permanently remove (use DELETE instead)\n\n## How To Use\n1. Call with sandbox_id of a running sandbox\n2. Sandbox stops, billing pauses\n3. Later, call POST `.../start` to resume\n\n## Example\n```\nPOST /api/sandboxes/sandbox_abc123/stop\n→ {\"message\": \"Sandbox stopped\", \"status\": \"stopped\"}\n\n# Later...\nPOST /api/sandboxes/sandbox_abc123/start\n→ {\"message\": \"Sandbox started\", \"status\": \"running\"}\n```\n\n## Related Endpoints\n- POST /api/sandboxes/{id}/start - Resume stopped sandbox\n- DELETE /api/sandboxes/{id} - Permanently delete\n- GET /api/sandboxes/{id} - Check current status\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Modify own sandboxes\n- `sandbox.admin.sandboxes` - Modify any sandbox in organization\n\n## Technical Notes\nStop a sandbox (scale to zero). Stops billing but preserves sandbox data and configuration.","operationId":"stop_sandbox_api_sandboxes__sandbox_id__stop_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/start":{"post":{"tags":["Sandboxes"],"summary":"Start Sandbox","description":"Resume a stopped sandbox and restart billing.\n\n## Who Should Use This\nExternal API users who want to resume a previously stopped sandbox. Requires `sandbox.write.sandboxes` permission.\n\n## When To Use\n- Resuming work on a paused sandbox\n- Restarting an AI agent after maintenance\n- Bringing a sandbox back online\n\n## How To Use\n1. Call with sandbox_id of a stopped sandbox\n2. Wait for status to become \"running\"\n3. Resume using execute endpoint\n\n## Important Notes\n- Will fail with 402 if your monthly cost limit is exceeded\n- Billing resumes immediately when started\n- Sandbox data is preserved from before stop\n\n## Example\n```\nPOST /api/sandboxes/sandbox_abc123/start\n→ {\"message\": \"Sandbox started\", \"status\": \"running\"}\n```\n\n## Related Endpoints\n- POST /api/sandboxes/{id}/stop - Stop sandbox\n- GET /api/sandboxes/{id} - Check status\n- GET /api/costs/user/current - Check costs before starting\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Modify own sandboxes\n\n## Technical Notes\nStart a stopped sandbox. Checks cost limits before starting to prevent overages.","operationId":"start_sandbox_api_sandboxes__sandbox_id__start_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/reboot":{"post":{"tags":["Sandboxes"],"summary":"Reboot Sandbox","description":"Reboot a running sandbox as one serialized lifecycle operation.\n\nUses a per-sandbox lock to prevent concurrent start/stop/reboot races.","operationId":"reboot_sandbox_api_sandboxes__sandbox_id__reboot_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/execute":{"post":{"tags":["Sandboxes"],"summary":"Execute Command","description":"Execute a shell command inside a running sandbox.\n\n## Who Should Use This\nExternal API users and AI agents who need to run commands in a sandbox. Requires `sandbox.execute.commands` permission.\n\n## When To Use\n- Running code or scripts in the sandbox\n- Installing packages (apt, pip, npm, etc.)\n- Managing files (ls, mkdir, rm, etc.)\n- Any shell command an AI agent needs to execute\n\n## How To Use\n1. Ensure sandbox status is \"running\" (GET /api/sandboxes/{id} to check)\n2. Send command in request body\n3. Receive stdout, stderr, and exit_code in response\n4. Check exit_code (0 = success, non-zero = error)\n\n## Example\n```\nPOST /api/sandboxes/sandbox_abc123/execute\n{\"command\": \"python3 -c 'print(1+1)'\"}\n→ {\"stdout\": \"2\\n\", \"stderr\": \"\", \"exit_code\": 0}\n\nPOST /api/sandboxes/sandbox_abc123/execute\n{\"command\": \"pip install requests && python script.py\"}\n→ {\"stdout\": \"...\", \"stderr\": \"\", \"exit_code\": 0}\n```\n\n## Common Commands\n- `ls -la /workspace` - List files in workspace\n- `cat /workspace/output.txt` - Read file contents\n- `python3 script.py` - Run Python script\n- `apt-get update && apt-get install -y <package>` - Install system packages\n\n## Related Endpoints\n- GET /api/sandboxes/{id} - Check sandbox is running first\n- POST /api/sandboxes/{id}/files - Save files before executing\n- GET /api/sandboxes/{id}/download - Download workspace after\n\n## Permissions Required\n- `sandbox.execute.commands` - Run commands in own sandboxes\n\n## Technical Notes\nExecute command in sandbox. Uses adapter pattern: tries direct manager API first, falls back to cloud provider methods.","operationId":"execute_command_api_sandboxes__sandbox_id__execute_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteCommandRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteCommandResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/workflows/execute":{"post":{"tags":["Workflows"],"summary":"Execute Workflow","description":"Dispatch a multi-step workflow to a sandbox's manager.\n\nForwards the raw `workflow` definition to the manager's /workflow/execute\n(the same DAG engine the boot-time app installer uses). Requires\n`sandbox.execute.commands` and ownership/same-org-admin of the sandbox.\nReturns the manager's execution ack; poll `.../workflows/executions/{id}`.","operationId":"execute_workflow_api_sandboxes__sandbox_id__workflows_execute_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowExecuteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowExecuteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/workflows/executions/{execution_id}":{"get":{"tags":["Workflows"],"summary":"Get Workflow Execution","description":"Get the status of a workflow execution on a sandbox (scrubbed of known values).","operationId":"get_workflow_execution_api_sandboxes__sandbox_id__workflows_executions__execution_id__get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"execution_id","in":"path","required":true,"schema":{"type":"string","title":"Execution Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowExecutionStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/workflows/executions/{execution_id}/logs":{"get":{"tags":["Workflows"],"summary":"Get Workflow Execution Logs","description":"Get the logs of a workflow execution (non-follow; scrubbed of known values).\n\nFollow/SSE is intentionally not proxied — a streaming secret backstop needs\nthe manager-side redaction (L9). Use polling on this bounded form.","operationId":"get_workflow_execution_logs_api_sandboxes__sandbox_id__workflows_executions__execution_id__logs_get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"execution_id","in":"path","required":true,"schema":{"type":"string","title":"Execution Id"}},{"name":"tail","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Tail"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowLogsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/workflows/executions/{execution_id}/cancel":{"post":{"tags":["Workflows"],"summary":"Cancel Workflow Execution","description":"Cancel a running workflow execution on a sandbox (idempotent at the manager).","operationId":"cancel_workflow_execution_api_sandboxes__sandbox_id__workflows_executions__execution_id__cancel_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"execution_id","in":"path","required":true,"schema":{"type":"string","title":"Execution Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowCancelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/workflows/history":{"get":{"tags":["Workflows"],"summary":"Get Workflow History","description":"List workflow execution history on a sandbox (scrubbed of known values).","operationId":"get_workflow_history_api_sandboxes__sandbox_id__workflows_history_get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"workflow_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Workflow Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/{listing_id}/install":{"post":{"tags":["Apps"],"summary":"Install App","description":"Install a catalog app into an existing running sandbox (day-2, LANE L4).\n\nValidates the inputs against the manifest, delivers any secrets out-of-band\ninto the box (root-owned, umask 077), dispatches the manifest's install\nworkflow to the sandbox's manager, and returns 202 with the install id.\nPoll `.../installs/{install_id}/progress`.","operationId":"install_app_api_apps__listing_id__install_post","parameters":[{"name":"listing_id","in":"path","required":true,"schema":{"type":"string","title":"Listing Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppInstallRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs":{"get":{"tags":["Apps"],"summary":"List App Installs","description":"List the caller's app installs (newest first). NO scan — GSI1 by user.","operationId":"list_app_installs_api_apps_installs_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}":{"get":{"tags":["Apps"],"summary":"Get App Install","description":"Get one app install (the caller's own; foreign/missing → 404).","operationId":"get_app_install_api_apps_installs__install_id__get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff":{"get":{"tags":["Apps"],"summary":"List Staff","description":"List the caller's claws (AI employees) — newest first. NO scan (GSI1 by user,\nsame query as /api/apps/installs); filtered to `ai-agents` listings and projected\ninto the employee grammar (name, role, channel, employment status, cta).","operationId":"list_staff_api_staff_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}":{"get":{"tags":["Apps"],"summary":"Get Staff Member","description":"Get one claw (the caller's own). Foreign/missing → 404 via the shared owner\ncheck; an install that is NOT a claw (a Service, or a delisted listing) also →\n404 here — the staff surface never leaks a non-staff install's existence.\n\nLIVE STATUS: the returned record carries the last stored health probe\n(`last_health`). A FRESH probe is the shipped `POST /api/apps/installs/{id}/status`\n(runs `claws agent ping` through the box's manager); the employee page calls it\non demand — this GET does not dispatch to the manager, so it stays fast and\nread-only. `probe_endpoint` points the page at that reused route.","operationId":"get_staff_member_api_staff__install_id__get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/presence":{"get":{"tags":["Apps"],"summary":"Get Staff Presence","description":"The proof-of-life DIGEST for one claw (ticket 20260816_t_presence_standup;\ndesign ../20260815_pmm_interaction_contract/T-PRESENCE.md). Owner-only; foreign/\nabsent/non-claw install → 404 (shared owner gate + the same claw gate as\nGET /api/staff/{id} — the staff surface never leaks a non-staff install).\n\nA READ-ONLY projection over signals the platform ALREADY records: the install's\nstored health probe (`last_health`, refreshed by POST .../status = `claws agent\nping`) and the durable WorkflowRun run rows (keyed GSI1 USER#, filtered to the\nemployee's box, NO scan). Returns `{status, last_activity, recent[], standup,\nempty, cost?}`. Best-effort: a never-probed / unreachable box degrades honestly\n(status `unknown`, empty recent, honest standup) — never a 500, never fabricated\nactivity. This GET does NOT dispatch to the manager (no autonomy, no new surface).","operationId":"get_staff_presence_api_staff__install_id__presence_get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/onboarding":{"get":{"tags":["Apps"],"summary":"Get Staff Onboarding","description":"The first-conversation / onboarding hand-off for one claw (ticket\n20260816_t_onboard_inchannel; design\n../20260815_pmm_interaction_contract/T-ONBOARD-INCHANNEL.md). Owner-only; foreign/\nabsent/non-claw install → 404 (shared owner gate + the same claw gate as\nGET /api/staff/{id} — the staff surface never leaks a non-staff install).\n\nA READ-ONLY projection over what the platform ALREADY has — the install record,\nits catalog listing, and the stored health probe (`last_health`, refreshed by the\nseparate POST .../status = `claws agent ping`). Returns `{agent_name, channel,\nchannel_action, readiness, ready, first_steps[], what_it_can_do, prominent}`:\nthe employee's name, a direct \"message it now\" action, an HONEST readiness state\n(up vs still setting up), a real first-steps checklist, and what it can do. This\ndissolves the DF-003 dead-end (after install, the operator does not know what to do\nnext). NO scan, NO manager dispatch (a fresh probe stays the separate POST .../status).","operationId":"get_staff_onboarding_api_staff__install_id__onboarding_get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/handover":{"get":{"tags":["Apps"],"summary":"Get Staff Handover","description":"The retirement HANDOVER export for one claw (ticket 20260816_t_offboard). The\nsuccession doc the operator keeps — the pack's zero-loss-succession differentiator.\nOwner-only; foreign/absent/non-claw install → 404 (shared owner gate + the claw\ngate — the staff surface never leaks a non-staff install).\n\nA READ-ONLY projection over REAL data the platform already has: the install record,\nits listing, and the caller's OWN durable WorkflowRun rows (keyed GSI1 USER#,\nfiltered to the employee's box, NO scan). Returns `{employee, tenure, scorecard,\nwhat_it_did[], workspace_pointer, memory_note, ...}` — the scorecard + what-it-did\nderive ONLY from real run rows (no runs → honest empty, NEVER invented work); the\non-box memory/workspace bytes are env-gated (files-proxy) and `memory_note` says so\nhonestly. Readable AFTER retirement (loads by PK). NO manager dispatch, NO scan.","operationId":"get_staff_handover_api_staff__install_id__handover_get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/offboard":{"post":{"tags":["Apps"],"summary":"Offboard Staff Member","description":"Offboard (retire) a claw employee (ticket 20260816_t_offboard) — a deliberate,\nCONFIRMED end-of-employment that stops the employee and shreds its secrets. This\nREUSES the SHIPPED `uninstall` workflow (`run_uninstall` — stop + shred, every step\n`|| true`-shaped so it is safe on a partial install) and the terminal-confirm\nuninstall supervisor; it is NOT a new destructive primitive and adds NOTHING to the\nmanager or resource-service. The retired state is recorded through the existing\nlifecycle (202 → uninstalling → Retiring; the supervisor soft-deletes on a completed\nexecution → deleted → Retired). The workspace + memory + git history STAY on the box\n(the handover succession record); only the employment ends and the secrets are shred.\n\n`confirm=true` is REQUIRED (a missing/false confirm → static 400) so retirement is\nnever reachable by accident. Owner-only; foreign/absent/non-claw install → 404. A\nnon-running box → 400 (the uninstall workflow needs the box to run the shred).","operationId":"offboard_staff_member_api_staff__install_id__offboard_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"confirm","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Confirm"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/memory":{"get":{"tags":["Apps"],"summary":"Get Staff Memory","description":"What a claw employee remembers (ticket 20260816_t_memory_panel; design\ntickets/20260815_pmm_interaction_contract/T-MEMORY-PANEL.md). Owner-only; foreign/\nabsent/non-claw install → 404 (shared owner gate + the same claw gate as\nGET /api/staff/{id} — the staff surface never leaks a non-staff install).\n\nREAD-MOSTLY: `enabled`/`dir`/`persist_interval`/`provenance_note` derive from the\nlisting's DECLARED memory config; `entries[]` come ONLY from a bounded, READ-only,\npath-scoped LIVE read of the on-box `memory/` dir through the SHIPPED files-proxy →\nmanager `GET /files/list` seam (env-gated `STAFF_MEMORY_LIVE_READ_ENABLED`, only\nattempted on a RUNNING box). Best-effort + HONEST: a memory-not-enabled listing, an\nunreachable / never-started box, or the read disabled → an honest empty needs-box\nstate — NEVER a 500, NEVER fabricated memory contents. NO manager step type, NO scan.","operationId":"get_staff_memory_api_staff__install_id__memory_get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/memory/forget":{"post":{"tags":["Apps"],"summary":"Forget Staff Memory","description":"Forget one memory item (ticket 20260816_t_memory_panel). A HIGHER-rung action\nthan inspection, kept scoped for the MVP: it records a durable forget-INTENT\n(a keyed audit row, NO scan) and — env-gated — applies the real on-box deletion.\n\n`target` is a workspace-relative path DOUBLE-scoped to the memory dir (the\napp-side `validate_workspace_relative_filename` + a memory-dir-prefix check here,\nthen the manager's own `ResolveWithin`). The actual delete rides the SHIPPED\nfiles-proxy `DELETE /files/delete` and is gated on BOTH\n`STAFF_MEMORY_LIVE_READ_ENABLED` AND the shipped `COMPUTER_FILE_DELETE_ENABLED`\n(a manager release). HONESTY: `deleted` is True ONLY when the on-box delete\nactually completed — otherwise this records intent and says so plainly, never\nclaiming a deletion that did not happen. Owner-only; foreign/non-claw → 404.","operationId":"forget_staff_memory_api_staff__install_id__memory_forget_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"target","in":"query","required":true,"schema":{"type":"string","title":"Target"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/interruption-budget":{"get":{"tags":["Apps"],"summary":"Get Staff Interruption Budget","description":"The claw's interruption budget (ticket 20260816_t_interrupt_budget; design\ntickets/20260815_pmm_interaction_contract/T-INTERRUPT.md). Owner-only; foreign/\nabsent/non-claw install → 404 (shared owner gate + the same claw gate as\nGET /api/staff/{id} — the staff surface never leaks a non-staff install).\n\nA READ-ONLY projection: the EFFECTIVE budget (immediate-list / batch rule / quiet\nhours / channel routing) — the user's STORED customization if present, else the role\nDEFAULT derived from the listing (`is_default` tells the UI which). Also surfaces the\nwitnessed-diff log and the last stop-intent (real record data, never fabricated). NO\nscan, NO manager dispatch.","operationId":"get_staff_interruption_budget_api_staff__install_id__interruption_budget_get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Apps"],"summary":"Update Staff Interruption Budget","description":"Edit the claw's interruption budget out-of-band (ticket 20260816_t_interrupt_budget).\nAUTHOR/owner-only (AppsInstaller → `sandbox.install.apps`). Foreign/absent/non-claw\ninstall → 404. The in-channel negotiation (\"that ping wasn't worth it\" → budget\nupdate) writes the SAME record; this is its out-of-band equivalent.\n\nNormalizes the body (a non-object → STATIC 400), diffs it against the current\nEFFECTIVE budget, appends a WITNESSED diff entry (who/when/what-changed), and persists\nthe new budget ON the AppInstall record (`update_app_install` — reuse the keying, NO\nnew store, NO scan). A keyed audit row is recorded best-effort. Returns the refreshed\nbudget projection (now `is_default: false`, with the new witness entry on top).","operationId":"update_staff_interruption_budget_api_staff__install_id__interruption_budget_put","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/staff/{install_id}/stop-current-work":{"post":{"tags":["Apps"],"summary":"Stop Staff Current Work","description":"Stop the claw's CURRENT work item (ticket 20260816_t_interrupt_budget) — DISTINCT\nfrom Pause (stop = this task; pause = the employment). AUTHOR/owner-only; foreign/\nabsent/non-claw install → 404.\n\nRecords a durable stop-current-work INTENT on the record (+ a keyed audit row, NO\nscan). The actual live mid-task stop delivery to a running claw is ENV-GATED\n(`STAFF_STOP_LIVE_DELIVERY_ENABLED`, default OFF — the real safe-point stop needs a\nrunning box and fork-2 cancel semantics for consequential actions). HONEST: `delivered`\nis True ONLY when the live stop actually fired; otherwise this records intent and says\nso plainly, never faking a live stop and never pausing the employment.","operationId":"stop_staff_current_work_api_staff__install_id__stop_current_work_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"note","in":"query","required":false,"schema":{"type":"string","default":"","title":"Note"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/progress":{"get":{"tags":["Apps"],"summary":"Get App Install Progress","description":"Progress for one install — the platform lifecycle stitched with the manager\nexecution's live step counters (DESIGN §4.1). Best-effort: if the manager\nexecution can't be fetched, returns the platform phase alone (honesty — it\nnever fabricates step progress).","operationId":"get_app_install_progress_api_apps_installs__install_id__progress_get","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/retry":{"post":{"tags":["Apps"],"summary":"Retry App Install","description":"Retry a FAILED install (DESIGN §4.2). Re-dispatches the idempotent install\nworkflow with the stored non-secret inputs; secrets are already the root-owned\nfiles in the box (not re-collected, not re-delivered).\n\nDF-003: an optional body `{\"inputs\": {...}}` re-supplies CORRECTED secrets (e.g. a\nbad API key that caused the failure) — only the re-supplied secret files are\nre-delivered to the box over the existing out-of-band path; nothing is persisted.","operationId":"retry_app_install_api_apps_installs__install_id__retry_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/AppRetryRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/status":{"post":{"tags":["Apps"],"summary":"App Install Status","description":"Run the app's `status` workflow synchronously (short timeout) and refresh the\ninstall's health telemetry (LANE L7). Returns `{healthy, detail, checked_at}`.\n\nHealth is TELEMETRY (DESIGN §6): it updates `last_health` and NEVER changes the\nlifecycle `status` — a degraded probe shows an amber badge, it does not tear the\napp down.","operationId":"app_install_status_api_apps_installs__install_id__status_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/update":{"post":{"tags":["Apps"],"summary":"App Install Update","description":"Update an installed app — run its `update` workflow (LANE L7). Transits\nthrough `installing` and converges back to `running`|`failed` (DESIGN §6); 202,\npoll `.../progress`. A listing that ships no `update` workflow → 400 (honest —\nthe verb is optional and never stubbed).","operationId":"app_install_update_api_apps_installs__install_id__update_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/uninstall":{"post":{"tags":["Apps"],"summary":"App Install Uninstall","description":"Uninstall an app — run its `uninstall` workflow (stop + shred the secret\nfiles), then soft-delete the install record (LANE L7). 202 → uninstalling; poll\n`.../progress`.\n\nReleases NOTHING else: the sandbox and any user data on it remain (DESIGN §7.5).\nTerminal-confirm — the record is not marked `deleted` until the uninstall\nexecution reports completed (the supervisor's job).","operationId":"app_install_uninstall_api_apps_installs__install_id__uninstall_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/pause":{"post":{"tags":["Apps"],"summary":"App Install Pause","description":"Pause an installed claw employee — run its `pause` workflow (`claws stop`)\nand record the `paused` state (G6). Sync; returns the paused install. Refuses\nhonestly: a listing with no `pause` workflow → 409 pause_unsupported; a\nnon-running or already-paused install → 409 not_pausable; a stop workflow that\ndid not complete → 502 (the agent is NOT claimed paused).","operationId":"app_install_pause_api_apps_installs__install_id__pause_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/installs/{install_id}/resume":{"post":{"tags":["Apps"],"summary":"App Install Resume","description":"Resume a paused claw employee — run its `resume` workflow (`claws start` +\n`claws agent ping`) and clear the `paused` state (G6). Sync; returns the resumed\ninstall. Refuses honestly: a listing with no `resume` workflow → 409\npause_unsupported; an install that is not paused → 409 not_resumable; a start\nworkflow that did not complete → 502 (still paused).","operationId":"app_install_resume_api_apps_installs__install_id__resume_post","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","title":"Install Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/workflows/templates":{"post":{"tags":["Workflows"],"summary":"Create Workflow Template","description":"Save a new workflow template. `user_id`/`org_id` are server-assigned from the\ncaller (never the body). `owner_scope`=user → private; org → shared with the org.","operationId":"create_workflow_template_api_workflows_templates_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowTemplateSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Workflows"],"summary":"List Workflow Templates","description":"The caller's \"My Workflows\" library = their own templates ∪ their org's\nshared ones. NO scan. Empty → [].","operationId":"list_workflow_templates_api_workflows_templates_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/workflows/step-manifest":{"get":{"tags":["Workflows"],"summary":"Get Workflow Step Manifest","description":"The visual builder's palette data source (C1). Describes every workflow step\nthe engine can run — DERIVED from executor.go + verbs.go, never hand-listed (an\nanti-drift golden test locks the id-set to the engine). Non-sensitive discovery\ndata: no tenant scoping, no secret values. `WorkflowReader` keeps it consistent\nwith the library it feeds. Static per engine version → cacheable with an ETag.","operationId":"get_workflow_step_manifest_api_workflows_step_manifest_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/workflows/templates/{template_id}":{"get":{"tags":["Workflows"],"summary":"Get Workflow Template","description":"Get one visible template (own or org-shared). Foreign/missing → 404.","operationId":"get_workflow_template_api_workflows_templates__template_id__get","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Workflows"],"summary":"Update Workflow Template","description":"Update a template — AUTHOR ONLY, whatever the scope. A non-author who can see\nan org-shared template still gets `not_author` (403). Bumps the version.","operationId":"update_workflow_template_api_workflows_templates__template_id__put","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowTemplateSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Workflows"],"summary":"Delete Workflow Template","description":"Delete a template — AUTHOR ONLY. Foreign/missing → 404; visible-but-not-author\n→ 403.","operationId":"delete_workflow_template_api_workflows_templates__template_id__delete","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/workflows/templates/{template_id}/run":{"post":{"tags":["Workflows"],"summary":"Run Workflow Template","description":"Run a saved template against one sandbox or a fleet (`sandbox_id` and/or\n`sandbox_ids`). Non-secret inputs are validated ONCE against the template schema\nand applied to every target as ${VAR} substitutions. Per-target partial success:\na missing/foreign/stopped box records a static error and never aborts the batch.\n\nSECRETS GATE (L11): a template that declares a secret input is dispatched ONLY\nwhen `WORKFLOW_SECRET_ENV_ENABLED` is on AND the target manager is >= the floor\n(default 0.2.6 — accepts `secret_env` + redacts it). Flag OFF, or a too-old /\nunknown / unreachable manager, fails closed (409 single-target /\n`manager_secret_channel_unavailable` per-target for a fleet). The secret VALUE\nrides `secret_env` only (redacted), NEVER a ${VAR} command string, and is never\npersisted.","operationId":"run_workflow_template_api_workflows_templates__template_id__run_post","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowTemplateRunRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkflowTemplateRunResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/runs":{"get":{"tags":["Cogs"],"summary":"List Cog Runs","description":"The cross-sandbox \"my runs\" view (cogs P0, DESIGN §Seam E). Renders the runs\nthe platform already records on every dispatch (app installs, template runs) via\nthe reserved GSI1 USER# projection — newest-first, ownership-scoped to the caller.\nNO scan. Non-terminal rows are reconciled on read against the box's manager (bounded\n— see _reconcile_runs_page) so a finished run reads `completed`, not perpetual\n`running`. Returns `{runs: [WorkflowRun.public()], count, next}`; `next` is an\nopaque cursor to pass back for the following page, or null when exhausted.","operationId":"list_cog_runs_api_cogs_runs_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/runs/{execution_id}":{"get":{"tags":["Cogs"],"summary":"Get Cog Run","description":"One run the caller owns, resolved by execution_id over their OWN runs slice\n(the reserved GSI1 USER# projection — ownership-scoped, keyed, never a scan). A\nrun that is not the caller's, or has aged out of the TTL window, → 404 (foreign\n→ 404, never an existence oracle). A non-terminal hit is reconciled on read (the\nsame bounded best-effort manager poll as the list). Returns the\n`WorkflowRun.public()` projection (same fields the list row carries, incl. the\npre-scrubbed `log_tail`).","operationId":"get_cog_run_api_cogs_runs__execution_id__get","parameters":[{"name":"execution_id","in":"path","required":true,"schema":{"type":"string","title":"Execution Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reviews":{"get":{"tags":["Reviews"],"summary":"List Reviews","description":"The review queue (T-REVIEW §A): the caller's OWN runs awaiting a human decision\nbefore they land — the anti-Willison gate. Derived from real gated run state (only\na run stamped `review.gate` at dispatch and NOT yet decided appears — a non-gated\n/ already-decided run never does, no invented items). Keyed GSI1 USER# projection —\nownership-scoped, NO scan, bounded to the newest few pages (the queue is a small,\nrecent set). Returns `{reviews: [review_item()...], count}`.","operationId":"list_reviews_api_reviews_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reviews/{execution_id}/approve":{"post":{"tags":["Reviews"],"summary":"Approve Review","description":"Approve a pending-review item — record the operator's decision to let the output\nproceed/land. Owner-only (resolved over the caller's OWN runs slice; foreign/absent\n→ static 404). The actual landing on the box is ENV-GATED (a manager-backed box +\nresume-after-approval is not wired in this slice) — the decision is recorded and the\nlanding surfaced honestly (`landing_pending`), never faked. Already-decided → 409.","operationId":"approve_review_api_reviews__execution_id__approve_post","parameters":[{"name":"execution_id","in":"path","required":true,"schema":{"type":"string","title":"Execution Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reviews/{execution_id}/reject":{"post":{"tags":["Reviews"],"summary":"Reject Review","description":"Reject a pending-review item — stop it; the output does not land. Owner-only\n(resolved over the caller's OWN runs slice; foreign/absent → static 404).\nAlready-decided → static 409.","operationId":"reject_review_api_reviews__execution_id__reject_post","parameters":[{"name":"execution_id","in":"path","required":true,"schema":{"type":"string","title":"Execution Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/schedules":{"post":{"tags":["Cogs"],"summary":"Create Cog Schedule","description":"Create a cron schedule that fires a saved workflow against a box/fleet.\n\nValidates the cron, resolves the bound template (visible to the caller),\nREFUSES a secret-carrying template (schedule_secret_unsupported), validates the\ninputs against the template schema, and verifies the caller OWNS every target\nat create-time. Computes the first `next_fire_at` and arms the schedule (unless\n`enabled=false`). `user_id`/`org_id` are server-assigned from the caller.","operationId":"create_cog_schedule_api_cogs_schedules_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCreateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Cogs"],"summary":"List Cog Schedules","description":"The caller's schedules (user + manifest-surfaced), newest config first. ONE\nkeyed query over the caller's USER# partition — NO scan. Empty → [].","operationId":"list_cog_schedules_api_cogs_schedules_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/schedules/{schedule_id}":{"get":{"tags":["Cogs"],"summary":"Get Cog Schedule","description":"One schedule the caller owns. Foreign/missing → 404.","operationId":"get_cog_schedule_api_cogs_schedules__schedule_id__get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string","title":"Schedule Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Cogs"],"summary":"Delete Cog Schedule","description":"Delete a schedule — AUTHOR ONLY. Foreign/missing → 404.","operationId":"delete_cog_schedule_api_cogs_schedules__schedule_id__delete","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string","title":"Schedule Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/schedules/{schedule_id}/pause":{"post":{"tags":["Cogs"],"summary":"Pause Cog Schedule","description":"Pause a schedule — it drops out of the due-queue (sparse GSI3) and never\nfires until resumed. Author-only; foreign/missing → 404.","operationId":"pause_cog_schedule_api_cogs_schedules__schedule_id__pause_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string","title":"Schedule Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/schedules/{schedule_id}/resume":{"post":{"tags":["Cogs"],"summary":"Resume Cog Schedule","description":"Resume a paused schedule — re-arms `next_fire_at` forward from now\n(fire-once, no backfill). Author-only; foreign/missing → 404.","operationId":"resume_cog_schedule_api_cogs_schedules__schedule_id__resume_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"string","title":"Schedule Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/mcp-servers":{"get":{"tags":["Cogs"],"summary":"List Cog Mcp Servers","description":"The grantable MCP servers the cog create/edit picker renders (ticket\n20260815_cogs_p3_mcp_grant, FRONTEND §8). MVP = a curated built-in registry (the deferred\naccount tool-locker / Connections shelf stands in): each row carries its id, display name,\nplain-words blast radius, transport, and the KEY NAMES it authenticates with — never a\nsecret value. `default_grant=none` tells the UI an unchecked list is the safe default.","operationId":"list_cog_mcp_servers_api_cogs_mcp_servers_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/harnesses":{"get":{"tags":["Cogs"],"summary":"List Cog Harnesses","description":"The coding-agent harnesses the cog create/edit picker renders (R4, ticket\n20260816_cog_multi_harness). Each row carries its id, display name, and whether it is\n`available` — only claude-code ships available today; codex/hermes/pi are registered\n`available:false` placeholders the picker shows disabled (\"coming soon\"). `default` tells the\nUI which harness to pre-select. Internal registry details (invocation template, binary, model\nkey env, install listing) are never exposed to the client.","operationId":"list_cog_harnesses_api_cogs_harnesses_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs":{"post":{"tags":["Cogs"],"summary":"Create Cog","description":"Create a cog. `user_id`/`org_id` are server-assigned from the caller. A webhook\ntrigger mints a token here — the PLAINTEXT is returned ONCE in `webhook_token`\n(write-only thereafter; only its sha256 is stored). A cron trigger binds a backing\nschedule the platform scheduler sweeps.","operationId":"create_cog_api_cogs_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CogSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Cogs"],"summary":"List Cogs","description":"The caller's cogs = their own ∪ their org's shared. NO scan. Empty → [].","operationId":"list_cogs_api_cogs_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/{cog_id}":{"get":{"tags":["Cogs"],"summary":"Get Cog","description":"Get one visible cog (own or org-shared). Foreign/missing → 404.","operationId":"get_cog_api_cogs__cog_id__get","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Cogs"],"summary":"Update Cog","description":"Update a cog — AUTHOR ONLY (a non-author who can see an org-shared cog still gets\n403). Preserves the existing webhook token hash (regenerate is a separate op), bumps\nthe version, and re-syncs the backing cron schedule.","operationId":"update_cog_api_cogs__cog_id__put","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CogSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Cogs"],"summary":"Delete Cog","description":"Delete a cog — AUTHOR ONLY. Also deletes its backing cron schedule so nothing\nkeeps firing. Foreign/missing → 404; visible-but-not-author → 403.","operationId":"delete_cog_api_cogs__cog_id__delete","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/{cog_id}/pause":{"post":{"tags":["Cogs"],"summary":"Pause Cog","description":"Pause a cog — its trigger stops firing (cron drops from the due-queue). Author-\nonly; foreign/missing → 404. Manual `/run` still works while paused.","operationId":"pause_cog_api_cogs__cog_id__pause_post","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/{cog_id}/resume":{"post":{"tags":["Cogs"],"summary":"Resume Cog","description":"Resume a paused cog — re-arms its trigger. Author-only; foreign/missing → 404.","operationId":"resume_cog_api_cogs__cog_id__resume_post","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/{cog_id}/webhook-token":{"post":{"tags":["Cogs"],"summary":"Regenerate Cog Webhook Token","description":"Regenerate (or first-issue) a cog's webhook token — AUTHOR ONLY. Returns the new\nPLAINTEXT once; only its hash is stored. Setting a token makes `/fire` live.","operationId":"regenerate_cog_webhook_token_api_cogs__cog_id__webhook_token_post","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/{cog_id}/run":{"post":{"tags":["Cogs"],"summary":"Run Cog","description":"Run a cog now (the \"Run now\" verb) — dispatches against the cog's target box with\nthe AUTHENTICATED caller as the principal (Phase-2 owner check per target). Honors\nthe cog's dry-run-first posture. Foreign/missing → 404.","operationId":"run_cog_api_cogs__cog_id__run_post","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cogs/{cog_id}/fire":{"post":{"tags":["Cogs"],"summary":"Fire Cog","description":"Webhook trigger — dispatch a cog authenticated by its TOKEN (not a bearer). The\ntoken rides the `X-Cog-Token` header; only its sha256 is stored. Rate-limited\n20/minute. TRUSTED-TASK / UNTRUSTED-PAYLOAD: the saved task is trusted; the request\nbody arrives wrapped + labeled untrusted and the task must opt in to using it.\n\nErrors are STATIC: a missing/malformed token → 401, a token mismatch → 403, a cog\nwith no webhook trigger or an unknown id → 404 (foreign → 404, never an existence\noracle). Nothing dynamic is echoed to the caller.","operationId":"fire_cog_api_cogs__cog_id__fire_post","parameters":[{"name":"cog_id","in":"path","required":true,"schema":{"type":"string","title":"Cog Id"}},{"name":"x-cog-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Cog-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/teams":{"post":{"tags":["Teams"],"summary":"Create Team","description":"Create a team. `user_id`/`org_id` are server-assigned from the caller. Validates\nthe roster shape (>=1 member, exactly one manager, distinct names). NO deploy happens\nhere — a team is composed first, then stood up via POST /api/teams/{id}/deploy.","operationId":"create_team_api_teams_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Teams"],"summary":"List Teams","description":"The caller's teams = their own ∪ their org's shared. NO scan. Empty → [].","operationId":"list_teams_api_teams_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/teams/{team_id}":{"get":{"tags":["Teams"],"summary":"Get Team","description":"Get one visible team (own or org-shared). Foreign/missing → 404.","operationId":"get_team_api_teams__team_id__get","parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"string","title":"Team Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Teams"],"summary":"Update Team","description":"Update a team — AUTHOR ONLY (a non-author who can see an org-shared team still gets\n403). Preserves the recorded deployment intent, bumps the version.","operationId":"update_team_api_teams__team_id__put","parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"string","title":"Team Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Teams"],"summary":"Delete Team","description":"Delete a team — AUTHOR ONLY. Foreign/missing → 404; visible-but-not-author → 403.\nDeleting the roster record does not touch any already-dispatched claw install (each\ninstall stays managed on its box via the shipped /api/apps lifecycle).","operationId":"delete_team_api_teams__team_id__delete","parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"string","title":"Team Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/teams/{team_id}/deploy":{"post":{"tags":["Teams"],"summary":"Deploy Team","description":"Stand up a team — AUTHOR ONLY. Reuses the SHIPPED claw install flow: builds the\nstand-up plan from the same catalog manifests the store installs (coordd + one claw\nper member), and records the deploy INTENT + per-member install refs on the team. NO\nnew manager step type, resource-service untouched.\n\nThe LIVE coordinating team (real installs answering + coordinating) needs real boxes +\nbuyer keys and is ENV-GATED (teams_live_deploy_enabled + a running box + member_inputs).\nDefault OFF → the plan is RECORDED honestly and the response says so; nothing is faked.","operationId":"deploy_team_api_teams__team_id__deploy_post","parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"string","title":"Team Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamDeployRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps":{"get":{"tags":["Apps"],"summary":"List Apps","description":"List published app listings (catalog cards — no workflow bodies).\n\nReturns `{listings: [metadata+requirements+billing+ui]}`. Empty catalog →\nempty list; a manifest that fails validation is logged + skipped, never\nrendered.","operationId":"list_apps_api_apps_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/apps/{listing_id}":{"get":{"tags":["Apps"],"summary":"Get App","description":"Get one app listing (full manifest MINUS workflow step bodies).\n\n`?include=steps` adds each workflow's step names + types (the plan preview)\n— still no step bodies. A missing/foreign/invalid listing → 404 (the static\n`AppListingUnavailableError` codes all map to not-found for the read API;\ndynamic context stays in server logs).","operationId":"get_app_api_apps__listing_id__get","parameters":[{"name":"listing_id","in":"path","required":true,"schema":{"type":"string","title":"Listing Id"}},{"name":"include","in":"query","required":false,"schema":{"type":"string","default":"","title":"Include"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/manager-status":{"get":{"tags":["Sandboxes"],"summary":"Get Manager Status","description":"Check if the Resource Manager is installed and healthy on a sandbox.\n\n## Who Should Use This\n- Debugging when commands fail with \"manager unavailable\" errors\n- Verifying sandbox setup completed successfully\n- Monitoring sandbox health\n\n## What It Returns\n- **healthy**: Whether the manager is responding\n- **status**: One of:\n  - `healthy` - Manager installed and responding\n  - `unreachable` - Cannot connect (manager not installed or firewall)\n  - `auth_failed` - Manager running but token invalid\n  - `timeout` - Manager not responding (overloaded or starting)\n  - `not_configured` - No manager token in sandbox metadata\n  - `not_ready` - Instance IP not yet available\n- **message**: Human-readable explanation\n- **details**: Version, uptime, etc. (if manager is healthy)\n- **last_fallback_reason**: Why the last command used SSM fallback\n\n## Example\n```\nGET /api/sandboxes/sandbox_abc123/manager-status\n→ {\n    \"healthy\": true,\n    \"status\": \"healthy\",\n    \"message\": \"Manager is installed and responding\",\n    \"details\": {\"version\": \"1.0.0\", \"uptime_seconds\": 3600}\n  }\n\n# If manager not installed:\n→ {\n    \"healthy\": false,\n    \"status\": \"unreachable\",\n    \"message\": \"Cannot connect to manager at 10.0.1.5:8000 - manager may not be installed\",\n    \"last_fallback_reason\": \"Cannot connect to manager at 10.0.1.5:8000\"\n  }\n```\n\n## Troubleshooting\n- If `unreachable`: Check /var/log/resource-manager-install.log on the instance\n- If `auth_failed`: Token mismatch - may need to recreate sandbox\n- If `not_ready`: Wait for sandbox status to become \"running\"","operationId":"get_manager_status_api_sandboxes__sandbox_id__manager_status_get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManagerStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/costs/user/{user_id}":{"get":{"tags":["Costs"],"summary":"Get User Costs","description":"Get detailed cost report for a specific user.\n\n## Who Should Use This\n- External API users who want to monitor their own usage and costs\n- Internal billing systems tracking user consumption\n- Admin dashboards displaying per-user cost breakdowns\n\n## When To Use\n- Displaying cost summaries in user dashboards\n- Generating usage reports for billing purposes\n- Monitoring spending before hitting cost limits\n- Auditing resource consumption patterns\n\n## How To Use\n1. Use `user_id=\"current\"` to get your own costs (most common use case)\n2. Specify a period: \"daily\", \"weekly\", or \"monthly\" (default)\n3. Parse the response to extract total costs, breakdown by resource type, etc.\n\n## Example Workflow\n```\nGET /api/costs/user/current?period=monthly\n→ Returns: {\"total_cost\": 45.50, \"period\": \"monthly\", \"breakdown\": {...}}\n\n# For admins viewing another user's costs:\nGET /api/costs/user/user_abc123?period=weekly\n```\n\n## Related Endpoints\n- GET /api/costs/organization - View org-wide costs (admin only)\n- POST /api/costs/limits/user/{user_id} - Set spending limits (admin only)\n- GET /api/sandboxes/{id}/metrics - View resource metrics driving costs\n\n## Permissions Required\n- `sandbox.read.costs` - View your own costs (user_id=\"current\")\n- `sandbox.admin.costs` - View any user's costs within your organization\n\n## Technical Notes\nUse user_id=\"current\" to get costs for the authenticated user.","operationId":"get_user_costs_api_costs_user__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","maxLength":36,"pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$","title":"User Id"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","default":"monthly","title":"Period"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostReport"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/costs/organization":{"get":{"tags":["Costs"],"summary":"Get Org Costs","description":"Get aggregated cost report for the entire organization.\n\n## Who Should Use This\n- Organization admins monitoring overall spending\n- Finance teams generating cost reports\n- Internal billing systems for invoicing purposes\n\n## When To Use\n- Generating monthly billing reports\n- Monitoring organization-wide resource consumption\n- Planning budget allocation across teams\n- Identifying high-cost users or patterns\n\n## How To Use\n1. Call this endpoint with desired period parameter\n2. Response includes total costs and breakdown by user, resource type, etc.\n3. Use for billing reconciliation or capacity planning\n\n## Example Workflow\n```\nGET /api/costs/organization?period=monthly\n→ Returns: {\"total_cost\": 1250.00, \"period\": \"monthly\", \"user_breakdown\": {...}}\n```\n\n## Related Endpoints\n- GET /api/costs/user/{user_id} - View specific user's costs\n- POST /api/costs/limits/user/{user_id} - Set user spending limits\n- GET /api/admin/users - List all users with their costs\n\n## Permissions Required\n- `sandbox.admin.costs` - Required. Only organization admins can view org-wide costs.\n\n## Technical Notes\nAggregates costs across all users in the organization. May take longer for large organizations.","operationId":"get_org_costs_api_costs_organization_get","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","default":"monthly","title":"Period"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostReport"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/costs/limits/user/{user_id}":{"post":{"tags":["Costs"],"summary":"Set User Cost Limit","description":"Set a monthly spending limit for a specific user.\n\n## Who Should Use This\n- Organization admins controlling user spending\n- Automated systems enforcing budget policies\n- Finance teams implementing cost governance\n\n## When To Use\n- Onboarding new users with predefined spending limits\n- Adjusting limits based on user needs or role changes\n- Implementing cost controls before granting resource access\n- Resetting limits at the start of billing periods\n\n## How To Use\n1. Specify the user_id and monthly_limit in the request body\n2. Limit is enforced immediately for new resource allocations\n3. Users will receive warnings as they approach limits\n\n## Example Workflow\n```\n# Set $100/month limit for a user\nPOST /api/costs/limits/user/user_abc123\n{\"monthly_limit\": 100.00}\n→ Returns: {\"message\": \"Cost limit updated\", \"user_id\": \"user_abc123\", \"limit\": 100.00}\n\n# Later, check their costs\nGET /api/costs/user/user_abc123?period=monthly\n```\n\n## Related Endpoints\n- GET /api/costs/user/{user_id} - View user's current costs\n- GET /api/costs/organization - View all org costs\n- GET /api/admin/users - List users with costs and limits\n\n## Permissions Required\n- `sandbox.admin.costs` - Required. Only organization admins can set cost limits.\n\n## Technical Notes\nWhen users exceed their limit, new sandbox creation is blocked but existing sandboxes continue running.","operationId":"set_user_cost_limit_api_costs_limits_user__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","maxLength":36,"pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$","title":"User Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCostLimitRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCostLimitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/welcome-offer":{"get":{"tags":["Dashboard"],"summary":"Get Welcome Offer Status","description":"Server-authoritative gate for the dashboard credit reveal. Returns `should_show` (whether to show the wizard now) plus `amount`/`reason`. The reveal stops permanently only on claim / paid tier / no-grant; a close/skip is a transient snooze (see /snooze) that re-nudges later.","operationId":"get_welcome_offer_status_api_me_welcome_offer_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WelcomeOfferStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/welcome-offer/acknowledge":{"post":{"tags":["Dashboard"],"summary":"Acknowledge Welcome Offer","description":"Claim the welcome offer: durably records `claimed_at` for the current user. This is the PERMANENT gate — once claimed the reveal is never shown again, across browsers / sessions / Redis flush. Does not 503 if the cache is down.","operationId":"acknowledge_welcome_offer_api_me_welcome_offer_acknowledge_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/welcome-offer/snooze":{"post":{"tags":["Dashboard"],"summary":"Snooze Welcome Offer","description":"Snooze the welcome offer (close/skip). Sets a TRANSIENT, expiring snooze so the reveal is hidden for a short window but re-nudges on a later visit — it does NOT permanently dismiss. Never 503s; if the cache is down it just shows again sooner.","operationId":"snooze_welcome_offer_api_me_welcome_offer_snooze_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/quotas/usage":{"get":{"tags":["Quotas"],"summary":"Get Quota Usage","description":"Return current quota usage for the calling user's org across every resource with a limit (current value, limit, utilization, reset window). Used by the dashboard to render usage bars.","operationId":"get_quota_usage_api_quotas_usage_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/quotas/tiers":{"get":{"tags":["Quotas"],"summary":"Get Quota Tiers","description":"Public endpoint: every tier definition (id, display name, features, per-resource limits, optional upgrade URL) for the pricing comparison page. No auth required.","operationId":"get_quota_tiers_api_quotas_tiers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaTiersResponse"}}}}}}},"/api/quotas/check/{resource_key}":{"get":{"tags":["Quotas"],"summary":"Check Quota Preflight","description":"Pre-flight quota check: can the calling user provision one more of `resource_key` right now? Returns the decision (allow/deny/warn), the current/limit values, and the operator-facing reason. Does NOT mutate any counters — use the resource lifecycle to actually consume quota.","operationId":"check_quota_preflight_api_quotas_check__resource_key__get","parameters":[{"name":"resource_key","in":"path","required":true,"schema":{"type":"string","title":"Resource Key"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/quotas/recalculate":{"post":{"tags":["Quotas"],"summary":"Recalculate Own Quota Usage","description":"Self-service 'Recalculate usage': recompute the CALLING user's org quota counters from their source of truth (live count for gauges, durable period-ledger re-sum for the monthly-spend meter) and repair any drift. Idempotent, only ever sets counters to derived truth, and returns a before->after report per resource. Rate-limited and audited.","operationId":"recalculate_own_quota_usage_api_quotas_recalculate_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaRecalculateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/quotas/recalculate/{org_id}":{"post":{"tags":["Quotas"],"summary":"Recalculate Org Quota Usage","description":"Admin 'Recalculate usage' for a specific org. Gated on the sandbox admin permission; the org-boundary check allows a same-org admin (and cross-tenant staff) to reconcile the targeted org. Recomputes each counter from its source of truth and repairs drift — idempotent and only ever sets counters to derived truth. Rate-limited and audited.","operationId":"recalculate_org_quota_usage_api_quotas_recalculate__org_id__post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuotaRecalculateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/support/tickets":{"post":{"tags":["Support"],"summary":"Create Support Ticket","description":"Submit a support ticket. Stored in DynamoDB, logged for operator triage.","operationId":"create_support_ticket_api_support_tickets_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicketRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicketCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Support"],"summary":"List Support Tickets","description":"List the current user's support tickets.","operationId":"list_support_tickets_api_support_tickets_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupportTicketListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ssh-keys":{"get":{"tags":["SSH Keys"],"summary":"List Ssh Keys","description":"List your SSH public keys for the current organization.\n\nKeys are scoped to the org you're currently signed into. Switching to a\ndifferent workspace shows that workspace's keys, not this one's.","operationId":"list_ssh_keys_api_ssh_keys_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SSHKeyResponse"},"title":"Response List Ssh Keys Api Ssh Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["SSH Keys"],"summary":"Upload Ssh Key","description":"Upload an SSH public key.\n\nThe key is associated with your current organization. Switching workspaces\nhides keys created elsewhere.","operationId":"upload_ssh_key_api_ssh_keys_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyUploadRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ssh-keys/generate":{"post":{"tags":["SSH Keys"],"summary":"Generate Ssh Key","description":"Generate a new SSH keypair scoped to the current organization.\n\nThe server creates the keypair, stores the public key in this org's\nkeyring, and returns the private key in PEM format. **Save the private\nkey immediately — it will not be shown again.**","operationId":"generate_ssh_key_api_ssh_keys_generate_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyGenerateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyGenerateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ssh-keys/{key_id}":{"get":{"tags":["SSH Keys"],"summary":"Get Ssh Key","description":"Get SSH key details for a key in the current organization.","operationId":"get_ssh_key_api_ssh_keys__key_id__get","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^([a-f0-9]{32}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$","title":"Key Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["SSH Keys"],"summary":"Delete Ssh Key","description":"Delete an SSH key from the current organization.\n\nExisting sandboxes that already have the key in `~/.ssh/authorized_keys`\nwill continue to accept it until they are destroyed; only future\nsandboxes are affected.","operationId":"delete_ssh_key_api_ssh_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^([a-f0-9]{32}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$","title":"Key Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["SSH Keys"],"summary":"Update Ssh Key","description":"Update the display name of an SSH key in the current organization.","operationId":"update_ssh_key_api_ssh_keys__key_id__put","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^([a-f0-9]{32}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$","title":"Key Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ssh/ca/public-key":{"get":{"tags":["SSH Certificates"],"summary":"Get Ssh Ca Public Key","description":"Get SSH CA metadata used for certificate-based SSH login.","operationId":"get_ssh_ca_public_key_api_ssh_ca_public_key_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHCAPublicKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ssh/certificates":{"post":{"tags":["SSH Certificates"],"summary":"Issue Ssh Certificate","description":"Issue short-lived SSH certificate via resource-service.","operationId":"issue_ssh_certificate_api_ssh_certificates_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHCertificateIssueRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHCertificateIssueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/ssh/certificates":{"post":{"tags":["SSH Certificates"],"summary":"Issue Sandbox Ssh Certificate","description":"Issue short-lived SSH certificate for a sandbox.\n\nAllocation ownership is resolved from sandbox_id server-side to avoid exposing\nallocation IDs in browser payloads.","operationId":"issue_sandbox_ssh_certificate_api_sandboxes__sandbox_id__ssh_certificates_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxSSHCertificateIssueRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SSHCertificateIssueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/sandboxes":{"get":{"tags":["Admin"],"summary":"List All Sandboxes","description":"List all sandboxes across the entire organization (Admin Only).\n\n## Who Should Use This\n- Internal: Organization administrators managing resources\n- Internal: Operations teams monitoring sandbox fleet\n- Internal: Support staff troubleshooting user issues\n\n## When To Use\n- Auditing all sandboxes in the organization\n- Finding idle or orphaned sandboxes for cleanup\n- Investigating resource usage patterns\n- Supporting users who can't find their sandboxes\n\n## How To Use\n1. Optionally filter by status: \"running\", \"stopped\", \"pending\"\n2. Set limit for pagination (default 100)\n3. Returns all sandboxes visible to the organization\n\n## Example Workflow\n```\n# List all running sandboxes\nGET /api/admin/sandboxes?status=running&limit=50\n→ Returns: {\"sandboxes\": [...], \"total\": 45}\n\n# Find a specific user's sandboxes, then stop them\nGET /api/admin/sandboxes?status=running\n# Filter results by user_id, then call DELETE endpoints\n```\n\n## Related Endpoints\n- GET /api/sandboxes - List your own sandboxes (user-level)\n- POST /api/admin/sandboxes/stop-idle - Bulk stop idle sandboxes\n- GET /api/admin/users - List users with usage stats\n- DELETE /api/sandboxes/{id} - Delete specific sandbox\n\n## Permissions Required\n- `sandbox.admin.sandboxes` - Required. Only admins can view all org sandboxes.\n\n## Technical Notes\nReturns sandboxes from all users in the organization. Use filters to narrow results.","operationId":"list_all_sandboxes_api_admin_sandboxes_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"include_soft_deleted","in":"query","required":false,"schema":{"type":"boolean","description":"Include status=deleted/terminated rows (admin Trash view)","default":false,"title":"Include Soft Deleted"},"description":"Include status=deleted/terminated rows (admin Trash view)"},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SandboxListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/sandboxes/{sandbox_id}/restore":{"post":{"tags":["Admin"],"summary":"Admin Restore Sandbox","description":"Restore a soft-deleted sandbox by flipping its status from `deleted`\nback to `stopped` so the user can Start it again from their dashboard.\n\n## Who Should Use This\n- Internal: support staff recovering accidentally-deleted user sandboxes.\n\n## When To Use\n- User clicked Delete instead of Stop and wants their data back.\n- Recovering test data after a bulk-delete sweep that went too far.\n\n## How To Use\n1. Find the sandbox via `GET /api/admin/sandboxes?include_soft_deleted=true`.\n2. POST to this endpoint with the sandbox_id.\n3. Sandbox returns to status=stopped; user can now Start it from /dashboard.\n\n## Behaviour\n- Idempotent: restoring an already-non-deleted sandbox is a no-op.\n- Does NOT recreate the underlying compute (allocation may have been\n  torn down). The first user-initiated Start after restore will spin\n  up fresh infrastructure if needed; persistent disk state is preserved\n  to the extent the deployment engine supports it.\n- Records an audit row (`action_type=admin_restored`).\n\n## Permissions Required\n- `sandbox.admin.sandboxes` - Same as the rest of the admin surface.\n\n## Related Endpoints\n- GET /api/admin/sandboxes?include_soft_deleted=true - find soft-deleted rows\n- POST /api/sandboxes/{id}/start - bring restored sandbox back to running","operationId":"admin_restore_sandbox_api_admin_sandboxes__sandbox_id__restore_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users":{"get":{"tags":["Admin"],"summary":"List Users With Costs","description":"List all users in the organization with their current costs and usage stats.\n\n## Who Should Use This\n- Internal: Finance teams reviewing billing data\n- Internal: Admins identifying high-usage users\n- Internal: Operations teams planning capacity\n\n## When To Use\n- Monthly billing reconciliation\n- Identifying users approaching cost limits\n- Auditing organization membership and usage\n- Planning resource allocation and budgets\n\n## How To Use\n1. Call this endpoint to get all users with cost summaries\n2. Response includes user IDs, current costs, sandbox counts\n3. Use for reporting or identifying users needing attention\n\n## Example Workflow\n```\nGET /api/admin/users\n→ Returns: {\"users\": [{\"user_id\": \"...\", \"current_cost\": 45.50, \"sandbox_count\": 3}, ...], \"total\": 25}\n\n# Find high-cost users and set limits\n# For each user with current_cost > threshold:\nPOST /api/costs/limits/user/{user_id} {\"monthly_limit\": 100}\n```\n\n## Related Endpoints\n- GET /api/costs/user/{user_id} - Detailed cost report for specific user\n- POST /api/costs/limits/user/{user_id} - Set cost limits\n- GET /api/admin/sandboxes - View all sandboxes\n\n## Permissions Required\n- `sandbox.admin.costs` - Required. Only admins can view all org users.\n\n## Technical Notes\nReturns all active users with aggregated cost data. Costs are calculated in real-time.","operationId":"list_users_with_costs_api_admin_users_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/sandboxes/stop-idle":{"post":{"tags":["Admin"],"summary":"Stop Idle Sandboxes","description":"Bulk stop all sandboxes that have been idle for more than specified hours.\n\n## Who Should Use This\n- Internal: Automated cost-saving jobs\n- Internal: Operations teams performing cleanup\n- Internal: Admins reducing resource usage\n\n## When To Use\n- Daily/weekly automated cleanup jobs\n- Before billing period end to reduce costs\n- When cloud resource limits are approached\n- Manual cleanup of forgotten sandboxes\n\n## How To Use\n1. Set idle_hours threshold (default 24 hours)\n2. All sandboxes with no activity beyond that threshold are stopped\n3. Sandboxes are stopped (not deleted) - users can restart them\n\n## Example Workflow\n```\n# Stop all sandboxes idle for more than 12 hours\nPOST /api/admin/sandboxes/stop-idle?idle_hours=12\n→ Returns: {\"message\": \"Stopped 8 idle sandboxes\", \"stopped_count\": 8, \"stopped_ids\": [...]}\n\n# Recommended: Run this via scheduled job\n# cron: 0 4 * * * curl -X POST .../api/admin/sandboxes/stop-idle?idle_hours=24\n```\n\n## Related Endpoints\n- GET /api/admin/sandboxes - View all sandboxes to identify idle ones\n- POST /api/sandboxes/{id}/stop - Stop individual sandbox\n- DELETE /api/sandboxes/{id} - Permanently delete sandbox\n\n## Permissions Required\n- `sandbox.admin.sandboxes` - Required. Only admins can bulk-stop sandboxes.\n\n## Technical Notes\nSandboxes are stopped (scaled to 0) not deleted. Data persists and users can restart.","operationId":"stop_idle_sandboxes_api_admin_sandboxes_stop_idle_post","parameters":[{"name":"idle_hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Idle Hours"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StopIdleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/contact":{"post":{"tags":["Public"],"summary":"Submit Contact Form","description":"Public contact form. No authentication required. Stored in DynamoDB for operator triage.","operationId":"submit_contact_form_api_contact_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/suspend":{"post":{"tags":["Admin"],"summary":"Suspend User","description":"Suspend a user: stop all their running sandboxes and containers.","operationId":"suspend_user_api_admin_users__user_id__suspend_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","maxLength":36,"pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$","title":"User Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuspendUserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuspendUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/users/{user_id}/unsuspend":{"post":{"tags":["Admin"],"summary":"Unsuspend User","description":"Unsuspend a user. Does not automatically restart their sandboxes.","operationId":"unsuspend_user_api_admin_users__user_id__unsuspend_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","maxLength":36,"pattern":"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$","title":"User Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuspendUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/audit-log":{"get":{"tags":["Admin"],"summary":"Get Audit Log","description":"List recent actions across the organization for audit purposes.\n\nRate-limited (10/min) and fan-out-bounded server-side. The underlying\n`db.list_org_audit_log` is a stopgap that scans up to 50 org sandboxes\n× 5 actions; container actions are not yet included. Tracked for proper\nfix in tickets/20260510_audit_log_org_keyed_gsi/.","operationId":"get_audit_log_api_admin_audit_log_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/organizations":{"get":{"tags":["Admin"],"summary":"List Organizations","description":"List organizations. Enriches auth service org data with sandbox counts and costs.","operationId":"list_organizations_api_admin_organizations_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health":{"get":{"tags":["Health"],"summary":"Health Check","description":"Check if the Sandbox Platform service is healthy and operational.\n\n## Who Should Use This\n- External: Load balancers and health check systems\n- External: Monitoring systems (Datadog, Prometheus, etc.)\n- Internal: Deployment pipelines verifying service readiness\n\n## When To Use\n- Continuous health monitoring\n- Pre-request checks to verify service availability\n- Deployment verification after rollouts\n- Debugging connectivity issues\n\n## How To Use\n1. Call this endpoint with no authentication required\n2. A 200 response with status=\"healthy\" indicates service is operational\n3. Any other response indicates issues\n\n## Example Workflow\n```\nGET /api/health\n→ Returns: {\"status\": \"healthy\", \"service\": \"sandbox-platform\", \"timestamp\": \"2026-02-01T...\"}\n\n# In monitoring:\nif response.status != \"healthy\":\n    alert(\"Sandbox Platform service degraded!\")\n```\n\n## Related Endpoints\n- No related endpoints - this is a standalone health check\n\n## Permissions Required\n- None - This endpoint is unauthenticated for health monitoring purposes.\n\n## Technical Notes\nReturns basic health status. Does not test downstream dependencies for fast response.","operationId":"health_check_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/api/sandboxes/{sandbox_id}/metrics":{"get":{"tags":["Sandboxes"],"summary":"Get Sandbox Metrics","description":"Get real-time resource metrics for a running sandbox.\n\n## Who Should Use This\n- External API users monitoring their sandbox resource usage\n- AI agents optimizing resource allocation\n- Internal dashboards displaying live metrics\n\n## When To Use\n- Monitoring sandbox health and performance\n- Detecting high CPU/memory usage before issues occur\n- Tracking session costs in real-time\n- Deciding when to scale up or terminate resources\n\n## How To Use\n1. Provide the sandbox_id of a running sandbox\n2. Receive current CPU, memory, disk, and network metrics\n3. Also includes uptime_hours and session_cost for billing visibility\n\n## Example Workflow\n```\nGET /api/sandboxes/sandbox_abc123/metrics\n→ Returns: {\n    \"cpu_utilization\": 45.2,\n    \"memory_utilization\": 68.5,\n    \"disk_utilization\": 12.3,\n    \"network_in\": 1024000,\n    \"network_out\": 512000,\n    \"uptime_hours\": 2.5,\n    \"session_cost\": 0.12\n  }\n\n# Use metrics to decide actions:\nif cpu_utilization > 90:\n    # Consider scaling up or optimizing workload\n```\n\n## Related Endpoints\n- GET /api/sandboxes/{id} - Get sandbox details\n- POST /api/sandboxes/{id}/execute - Run commands (may affect metrics)\n- GET /api/costs/user/current - View overall costs\n\n## Permissions Required\n- `sandbox.read.sandboxes` - View metrics for your own sandboxes\n- `sandbox.admin.sandboxes` - View metrics for any sandbox in org\n\n## Technical Notes\nMetrics are collected via manager agent on the instance. Falls back to Resource Service if manager unavailable.","operationId":"get_sandbox_metrics_api_sandboxes__sandbox_id__metrics_get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/files":{"post":{"tags":["Sandboxes"],"summary":"Save Sandbox File","description":"Save a file to the sandbox's /workspace directory.\n\n## Who Should Use This\n- AI agents writing code or config files to sandboxes\n- External API users deploying files programmatically\n- Internal automation systems preparing sandbox environments\n\n## When To Use\n- Deploying source code to a sandbox before execution\n- Writing configuration files for applications\n- Saving generated content from AI agents\n- Preparing test fixtures or data files\n\n## How To Use\n1. Sandbox must be in \"running\" status\n2. Provide filename (relative to /workspace) and content\n3. File is created or overwritten in /workspace/{filename}\n\n## Example Workflow\n```\n# Write a Python script to the sandbox\nPOST /api/sandboxes/sandbox_abc123/files\n{\"filename\": \"main.py\", \"content\": \"print('Hello, World!')\"}\n→ Returns: {\"message\": \"File saved\", \"filename\": \"main.py\"}\n\n# Then execute it\nPOST /api/sandboxes/sandbox_abc123/execute\n{\"command\": \"python main.py\"}\n→ Returns: {\"stdout\": \"Hello, World!\", ...}\n```\n\n## Related Endpoints\n- POST /api/sandboxes/{id}/execute - Execute commands (run your saved files)\n- POST /api/sandboxes/{id}/upload - Upload binary files (multipart form)\n- GET /api/sandboxes/{id}/download - Download entire workspace\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Write files to your own sandboxes\n- `sandbox.admin.sandboxes` - Write files to any sandbox in org\n\n## Technical Notes\nFiles are saved under /workspace using a shell-safe base64 transfer path.","operationId":"save_sandbox_file_api_sandboxes__sandbox_id__files_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveFileRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/upload":{"post":{"tags":["Sandboxes"],"summary":"Upload To Sandbox","description":"Upload a binary file to the sandbox using multipart form data.\n\n## Who Should Use This\n- External API users uploading binary files (images, archives, etc.)\n- AI agents deploying pre-built artifacts to sandboxes\n- Internal systems provisioning sandboxes with datasets\n\n## When To Use\n- Uploading binary files that can't be sent as text (images, zips, etc.)\n- Deploying large files more efficiently than inline content\n- Provisioning sandboxes with pre-built artifacts\n\n## How To Use\n1. Sandbox must be in \"running\" status\n2. Send multipart form data with file\n3. File is uploaded to /workspace directory\n\n## Example Workflow\n```\n# Using curl with multipart form data:\ncurl -X POST .../api/sandboxes/sandbox_abc123/upload \\\n  -H \"Authorization: Bearer ...\" \\\n  -F \"file=@mydata.zip\" \\\n  -F \"filename=artifacts/mydata.zip\"\n→ Returns: {\"message\": \"File uploaded: artifacts/mydata.zip\", ...}\n```\n\n## Related Endpoints\n- POST /api/sandboxes/{id}/files - Save text files (simpler for text content)\n- GET /api/sandboxes/{id}/download - Download entire workspace\n- POST /api/sandboxes/{id}/execute - Execute commands on uploaded files\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Upload to your own sandboxes\n- `sandbox.admin.sandboxes` - Upload to any sandbox in org\n\n## Technical Notes\nAccepts multipart uploads and writes binary content to /workspace via safe base64 transfer.","operationId":"upload_to_sandbox_api_sandboxes__sandbox_id__upload_post","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_to_sandbox_api_sandboxes__sandbox_id__upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sandboxes/{sandbox_id}/download":{"get":{"tags":["Sandboxes"],"summary":"Download Sandbox Workspace","description":"Download the entire sandbox /workspace directory as a compressed tarball.\n\n## Who Should Use This\n- AI agents extracting work products from sandboxes\n- External API users backing up sandbox contents\n- Internal systems archiving completed work\n\n## When To Use\n- Retrieving all files created/modified in a sandbox session\n- Backing up sandbox state before termination\n- Extracting generated artifacts (code, data, reports)\n- Archiving completed AI agent work products\n\n## How To Use\n1. Sandbox must be in \"running\" status\n2. Call endpoint to receive base64-encoded tarball\n3. Decode and extract the tarball locally\n\n## Example Workflow\n```\nGET /api/sandboxes/sandbox_abc123/download\n→ Returns: {\n    \"filename\": \"workspace_sandbox_abc123.tar.gz\",\n    \"content_base64\": \"H4sIAAAAAAAAA...\",\n    \"message\": \"Workspace packaged for download\"\n  }\n\n# Decode and extract locally:\necho $content_base64 | base64 -d > workspace.tar.gz\ntar -xzf workspace.tar.gz\n```\n\n## Related Endpoints\n- POST /api/sandboxes/{id}/files - Save individual files\n- POST /api/sandboxes/{id}/execute - Execute commands to prepare files\n- DELETE /api/sandboxes/{id} - Delete sandbox (loses workspace)\n\n## Permissions Required\n- `sandbox.read.sandboxes` - Download from your own sandboxes\n- `sandbox.admin.sandboxes` - Download from any sandbox in org\n\n## Technical Notes\nCreates tarball of /workspace directory. Content is base64-encoded for JSON transport.","operationId":"download_sandbox_workspace_api_sandboxes__sandbox_id__download_get","parameters":[{"name":"sandbox_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^sandbox_[a-f0-9]{8,32}$","title":"Sandbox Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadWorkspaceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/browsers":{"post":{"tags":["Containers"],"summary":"Create Browser","description":"Create a new browser container for web automation and testing.\n\n## Who Should Use This\n- AI agents needing web browser access (browsing, scraping, testing)\n- External API users running browser automation tasks\n- Internal testing systems for end-to-end web testing\n\n## When To Use\n- Running AI agents that need to interact with websites\n- Automating web-based workflows (form filling, data extraction)\n- Testing web applications in isolated browser environments\n- Capturing screenshots or recordings of web pages\n\n## How To Use\n1. Specify browser_type: \"firefox\", \"chrome\", or \"chromium\"\n2. Optionally set cpu, memory, and environment variables\n3. Receive container_id and poll /status until ready\n4. Use access_url to connect to the browser via VNC/web interface\n\n## Example Workflow\n```\n# Create a Firefox browser container\nPOST /api/browsers\n{\"browser_type\": \"firefox\"}\n→ Returns: {\"container_id\": \"browser_abc123\", \"state\": \"waiting_callback\", ...}\n\n# Poll for status until ready\nGET /api/containers/browser_abc123/status\n→ Returns: {\"state\": \"assigned\", \"access_url\": \"https://browser_abc123.browsers.sandbox.example.com?token=...\", ...}\n\n# Use the access_url to connect via VNC in browser\n# Or use Selenium/Playwright pointing to the container IP\n```\n\n## Related Endpoints\n- GET /api/containers/{id}/status - Poll container status until ready\n- GET /api/containers - List your containers\n- DELETE /api/containers/{id} - Delete browser container\n- POST /api/containers/{id}/access-url - Generate new access URL\n\n## Permissions Required\n- `sandbox.create.sandboxes` - Create browser containers\n\n## Technical Notes\nUses async provisioning with warm pools for faster startup. Containers run on Fargate.","operationId":"create_browser_api_browsers_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrowserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BrowserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/desktops":{"post":{"tags":["Containers"],"summary":"Create Desktop","description":"Create a new full Linux desktop container accessible via web browser —\nor, with `computer_name`/`blueprint_id`, a **persistent Computer**.\n\n## Who Should Use This\n- AI agents needing GUI application access\n- External API users running desktop applications remotely\n- Internal teams needing isolated desktop environments\n\n## When To Use\n- Running AI agents that need GUI applications (IDEs, office apps)\n- Testing desktop applications in isolated environments\n- Providing users with temporary desktop environments\n- Running applications that require display output\n- Creating a named, owned, off/on-capable **Computer** (see below)\n\n## How To Use\n1. Specify desktop_type: \"ubuntu\", \"debian\", \"alpine\"\n2. Optionally set cpu, memory, and environment variables\n3. Receive container_id and poll /status until ready\n4. Use access_url to connect to desktop via web browser (noVNC)\n\n## Persistent Computers (tag \"My Computers\" — the durable variant)\nSetting `computer_name` (and/or `blueprint_id`) creates a **Computer**: a durable,\nowned desktop that keeps its identity across stop/resume, is listed by\nGET /api/computers even while OFF, and owns a persistent home at `/config`.\n- `blueprint_id` — instantiate from a Blueprint (GET /api/blueprints); its apps\n  install on first boot (`apps_install_status` on the Computer).\n- Resources are REQUESTS: `cpu`/`memory`/disk are clamped to the caller's tier at\n  create time — `resource_clamped`/`resource_note` in the response say when.\n- `computer_id` — recreate a deleted Computer onto its surviving home (or use\n  POST /api/computers/{id}/recreate).\n- Use `desktop_type:\"ubuntu-xfce-agent\"` for the agent-ready image (in-VM manager);\n  then GET /api/containers/{id}/agent-bootstrap to drive it. Whether files actually\n  survive Turn off HERE: GET /api/capabilities. Full agent journey:\n  docs/AGENT_GUIDE_MY_COMPUTER.md + the \"My Computers\" section of /llms.txt.\n\n## Example Workflow\n```\n# Create an Ubuntu desktop container\nPOST /api/desktops\n{\"desktop_type\": \"ubuntu\"}\n→ Returns: {\"container_id\": \"desktop_abc123\", \"state\": \"waiting_callback\", ...}\n\n# Poll for status until ready\nGET /api/containers/desktop_abc123/status\n→ Returns: {\"state\": \"assigned\", \"access_url\": \"https://desktop_abc123.desktops.sandbox.example.com?token=...\", ...}\n\n# Open access_url in browser for full desktop experience\n```\n\n## Related Endpoints\n- GET /api/containers/{id}/status - Poll container status until ready\n- GET /api/containers - List your containers\n- DELETE /api/containers/{id} - Delete desktop container\n- POST /api/containers/{id}/access-url - Generate new access URL\n- POST /api/browsers - For browser-only needs (lighter weight)\n\n## Permissions Required\n- `sandbox.create.sandboxes` - Create desktop containers\n\n## Technical Notes\nUses async provisioning with warm pools for faster startup. Runs full XFCE/LXDE desktop.","operationId":"create_desktop_api_desktops_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DesktopRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DesktopResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ephemeral":{"post":{"tags":["Containers"],"summary":"Create Ephemeral","description":"Create a lightweight ephemeral compute container for short-lived tasks.\n\n## Who Should Use This\n- AI coding agents needing quick compute environments\n- External API users running short-lived scripts or workers\n- Internal systems for batch processing and automation\n\n## When To Use\n- Running AI coding agents that need isolated compute\n- Executing short-lived scripts or batch jobs\n- Running API workers or microservices\n- Testing code in isolated environments quickly\n\n## How To Use\n1. Specify ephemeral_type: \"coding_agent\", \"script_runner\", \"api_worker\", \"custom\"\n2. For \"custom\", also provide custom_image\n3. Optionally set command, cpu, memory, environment, max_runtime_minutes\n4. Poll /status until ready, then use exec_url for command execution\n\n## Example Workflow\n```\n# Create a coding agent container\nPOST /api/ephemeral\n{\"ephemeral_type\": \"coding_agent\", \"name\": \"my-agent\"}\n→ Returns: {\"container_id\": \"ephemeral_abc123\", \"state\": \"waiting_callback\", ...}\n\n# Poll for status until ready\nGET /api/containers/ephemeral_abc123/status\n→ Returns: {\"state\": \"assigned\", \"exec_url\": \"...\", \"logs_url\": \"...\", ...}\n\n# Execute commands via exec_url or use the /execute endpoint\n```\n\n## Related Endpoints\n- GET /api/containers/{id}/status - Poll container status until ready\n- GET /api/containers - List your containers\n- DELETE /api/containers/{id} - Delete ephemeral container\n- POST /api/sandboxes - For persistent Linux environments (EC2-based)\n- POST /api/browsers - For browser-specific tasks\n\n## Permissions Required\n- `sandbox.create.sandboxes` - Create ephemeral containers\n\n## Technical Notes\nEphemeral containers are optimized for quick startup and short-lived tasks.\nThey auto-terminate after max_runtime_minutes (if set) or idle_timeout_minutes.","operationId":"create_ephemeral_api_ephemeral_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EphemeralResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers":{"get":{"tags":["Containers"],"summary":"List Containers","description":"List all your browser, desktop, and ephemeral containers.\n\n## Who Should Use This\n- AI agents tracking their active containers\n- External API users managing their container fleet\n- Internal dashboards displaying container inventory\n\n## When To Use\n- Displaying user's active containers in a dashboard\n- Finding containers to connect to or terminate\n- Auditing active resources before cleanup\n- Monitoring container states after creation\n\n## How To Use\n1. Optionally filter by container_type: \"browser\", \"desktop\", \"ephemeral\"\n2. Optionally filter by status: \"running\", \"pending\", \"stopped\"\n3. Set limit for pagination (default 100)\n\n## Example Workflow\n```\n# List all running browser containers\nGET /api/containers?container_type=browser&status=running\n→ Returns: {\"containers\": [...], \"total\": 3}\n\n# Find a container and get its access URL\n# Then connect or delete as needed\nGET /api/containers/browser_abc123\nDELETE /api/containers/browser_abc123\n```\n\n## Related Endpoints\n- GET /api/containers/{id} - Get specific container details\n- GET /api/containers/{id}/status - Poll container status\n- DELETE /api/containers/{id} - Delete a container\n- GET /api/sandboxes - List sandboxes (EC2-based, separate from containers)\n\n## Permissions Required\n- `sandbox.read.sandboxes` - List your own containers\n\n## Technical Notes\nReturns browser, desktop, and ephemeral containers. For EC2-based sandboxes, use /api/sandboxes.","operationId":"list_containers_api_containers_get","parameters":[{"name":"container_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Container Type"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContainerListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/capabilities":{"get":{"tags":["My Computers"],"summary":"Live capability flags (persistence/export) — check before promising either","description":"Live capability flags for the My Computers surface (road-to-100 U2/U4; PMM N1).\n\nThe UI must never promise persistence, export, or file survival it cannot\ndeliver — and must stop denying them once they are lit. This is the single\nsource of truth both directions. Derivation is fail-closed: a flag without\nits config (bucket / filesystem id) is NOT a capability.\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"get_capabilities_api_capabilities_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformCapabilities"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blueprints":{"post":{"tags":["My Computers"],"summary":"Author a Blueprint (curated Computer config)","description":"Author a Blueprint — a curated Computer config (base image, tools/apps, default\nresources). Ticket 20260625 (P3.1); tenancy ticket 20260710 (T1/T3); scope authoring\nroad-to-100 A2. The blueprint is TENANT-SCOPED; its `org_id` is server-assigned from\nthe caller — a supplied `org_id` (or `max_cpu`/`max_memory`/`disk_gb`, or any unknown\nfield) is a 422 (BlueprintCreateRequest is extra=\"forbid\").\n\n`owner_scope` names WHO the blueprint is for, and the route enforces who may author it:\n- `user` (default) — just you; any writer.\n- `org` — everyone in your org; requires org admin (`sandbox.admin.sandboxes`/`sandbox.admin`).\n- `platform` — curated for every tenant; requires `sandbox.cross_tenant` (platform staff).\nAn insufficient permission is a 403 — this is the caller's OWN create request, so no\nexistence non-disclosure applies.\n\n`default_cpu`/`default_memory` are REQUESTS; the tier grants the ceiling at instantiate\ntime. `apps` REFERENCES one-click-claw AppListing ids; it does not define a new install\nschema (E-E9).\n\n## Permissions Required\n- `sandbox.write.sandboxes` (+ org admin for `org` scope, cross-tenant for `platform`)","operationId":"create_blueprint_api_blueprints_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlueprintCreateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlueprintSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["My Computers"],"summary":"List Blueprints visible to you (platform ∪ your org ∪ your own)","description":"List the Blueprints visible to you: platform-curated ∪ your org's ∪ your own.\n\nA Blueprint is the declarative config a Computer is created from. See\nDESIGN_20260710_my_computer.md §2.1/§3.1. Ticket 20260625 (P3.1); tenancy ticket\n20260710 (T2). Two bounded GSI1 queries (SCOPE#PLATFORM + ORG#{caller_org}), no scan and\nno global bucket. Empty until blueprints are authored.\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"list_blueprints_api_blueprints_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlueprintListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blueprints/{blueprint_id}":{"get":{"tags":["My Computers"],"summary":"Get one Blueprint (foreign/unknown ids are the same 404)","description":"Get one Blueprint by id. Ticket 20260625 (P3.1).\n\nOwnership enforced (audit A-1): only system blueprints or your own are\nreadable; anything else is a 404 (existence is not confirmed).\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"get_blueprint_api_blueprints__blueprint_id__get","parameters":[{"name":"blueprint_id","in":"path","required":true,"schema":{"type":"string","title":"Blueprint Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlueprintSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["My Computers"],"summary":"Delete a Blueprint you authored (foreign/unknown ids are the same 404)","description":"Delete one Blueprint by id. Ticket 20260812_sandbox_features_evidence_and_fixes (G1)\n— the create existed but no delete did (405), a dead-end.\n\nOwnership: the blueprint must be visible to you (own/org/system) or it is a 404\n(existence not confirmed); then only the AUTHOR may delete. Platform-curated\nblueprints are never user-deletable. Idempotent.\n\n## Permissions Required\n- `sandbox.write.sandboxes`","operationId":"delete_blueprint_api_blueprints__blueprint_id__delete","parameters":[{"name":"blueprint_id","in":"path","required":true,"schema":{"type":"string","title":"Blueprint Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers":{"get":{"tags":["My Computers"],"summary":"List your persistent Computers (including turned-off ones)","description":"List your persistent Computers (named, owned desktops), including OFF ones.\n\nA Computer is a durable, owned desktop (Blueprint -> Computer -> State). See\nDESIGN_20260710_my_computer.md §2.2/§3.2. Distinct from ephemeral containers\n(GET /api/containers): only records flagged `is_computer` appear here.\n\nTicket 20260625 (P4.1): real GSI1 USER# query filtered to is_computer (no scan),\nincl. OFF (STOPPED) computers. Empty when you have none.\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"list_computers_api_computers_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComputerListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}":{"patch":{"tags":["My Computers"],"summary":"Rename a Computer","description":"Rename a persistent Computer — ticket 20260625 (DESIGN §3.2 rename, was PROPOSED).\n\nRename only; no other mutation. Only records that ARE Computers (is_computer) can be\nrenamed — an ephemeral container 404s here (it has no name to own). Ownership enforced\nvia the canonical Phase-2 check (owner / same-org admin / cross-tenant); a FOREIGN\ncomputer is a 404 — never a 403 — so existence is not disclosed (TS-09, parity with\nevery other /api/computers route).\n\n## Permissions Required\n- `sandbox.write.sandboxes`","operationId":"rename_computer_api_computers__container_id__patch","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComputerRenameRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComputerSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/export":{"post":{"tags":["My Computers"],"summary":"Export the Computer's /config home as a presigned download","description":"Export a Computer's persistent home (\"take your home with you\") — filesystem\nticket 20260703 task P3.2, served by the fork-C snapshot layer (build leg 2).\n\nRUNNING Computer → a fresh tar.zst of /config (excludes caches/trash) is written by\nthe in-container manager to s3://{bucket}/{org_id}/{computer_id}/exports/… and a\npresigned download URL is returned. STOPPED Computer → the latest stop-snapshot is\npresigned directly (identical format — the snapshot IS the export). DARK behind\nPOOL_HOME_SNAPSHOT_ENABLED (503 when off); keys derive from the stored record's\norg/computer ids only, so no cross-tenant object is constructable.\n\n## Permissions Required\n- `sandbox.write.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"export_computer_home_api_computers__container_id__export_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComputerExportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}":{"get":{"tags":["Containers"],"summary":"Get Container","description":"Get detailed information about a specific container.\n\n## Who Should Use This\n- AI agents checking container status and URLs\n- External API users retrieving container details\n- Internal systems monitoring container health\n\n## When To Use\n- Retrieving access_url after container is ready\n- Checking container status and metadata\n- Getting session token for authentication\n- Verifying container ownership before operations\n\n## How To Use\n1. Provide the container_id\n2. Receive full container details including access_url if ready\n3. Use access_url to connect to the container\n\n## Example Workflow\n```\nGET /api/containers/browser_abc123\n→ Returns: {\n    \"container_id\": \"browser_abc123\",\n    \"container_type\": \"browser\",\n    \"browser_type\": \"firefox\",\n    \"status\": \"running\",\n    \"state\": \"assigned\",\n    \"access_url\": \"https://browser_abc123.browsers.sandbox.example.com?token=...\",\n    ...\n  }\n```\n\n## Related Endpoints\n- GET /api/containers - List all your containers\n- GET /api/containers/{id}/status - Lightweight status polling\n- DELETE /api/containers/{id} - Delete container\n- POST /api/containers/{id}/access-url - Generate fresh access URL\n\n## Permissions Required\n- `sandbox.read.sandboxes` - View your own containers\n\n## Technical Notes\nReturns full container details including URLs and session tokens for ready containers.","operationId":"get_container_api_containers__container_id__get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContainerSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Containers"],"summary":"Delete Container","description":"Permanently delete a container (browser, desktop, or ephemeral).\n\n## Who Should Use This\n- AI agents cleaning up after completing tasks\n- External API users removing unneeded containers\n- Internal systems performing automated cleanup\n\n## When To Use\n- After completing work in a container\n- Cleaning up failed or stuck containers\n- Removing containers to reduce costs\n- Implementing automatic cleanup policies\n\n## How To Use\n1. Provide the container_id to delete\n2. Container is immediately terminated and removed\n3. All data in the container is permanently lost\n\n## Example Workflow\n```\n# Delete a container after work is complete\nDELETE /api/containers/browser_abc123\n→ Returns: {\"message\": \"Container deleted\", \"container_id\": \"browser_abc123\"}\n\n# Good practice: Download any needed data before deletion\nGET /api/containers/browser_abc123  # (if applicable, get files first)\nDELETE /api/containers/browser_abc123\n```\n\n## Related Endpoints\n- GET /api/containers - List containers to find ones to delete\n- GET /api/containers/{id} - Check container before deletion\n- POST /api/admin/sandboxes/stop-idle - Admin bulk cleanup\n\n## Permissions Required\n- `sandbox.delete.sandboxes` - Delete your own containers\n- `sandbox.admin.sandboxes` - Delete any container in org\n\n## Technical Notes\nDeletion is permanent and irreversible. Container data is not backed up.","operationId":"delete_container_api_containers__container_id__delete","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/stop":{"post":{"tags":["Containers"],"summary":"Stop Container","description":"Stop a running container, retaining its storage/config so it can be resumed.\n\nStop TERMINATES the running compute (frees billing) and unregisters proxy routes, but\nKEEPS the container record so Resume can relaunch it. The Fargate task is genuinely\nstopped (not service-scaled — desktops are standalone tasks). Resume recreates a fresh\ndeployment. PERSISTENCE: with POOL_HOME_SNAPSHOT_ENABLED (fork-C interim, filesystem\nticket 20260703) a persistent Computer's /config is snapshotted to S3 BEFORE the task\nis terminated and restored on resume; a snapshot failure ABORTS the stop (retryable)\nrather than silently discarding state. With the flag off (default) — and for ordinary\nnon-Computer desktops always — state is still NOT preserved until EFS persistence\nlands and Resume yields a clean desktop. Only ASSIGNED containers may be stopped.\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Stop your own containers","operationId":"stop_container_api_containers__container_id__stop_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/resume":{"post":{"tags":["Containers"],"summary":"Resume Container","description":"Resume a previously stopped container by re-launching its Fargate task.\n\nTransitions STOPPED → WAITING_CALLBACK; readiness is then driven by the always-on\nFargate IP poller (or the health-callback sidecar), exactly like initial provisioning.\nOnly STOPPED containers may be resumed.\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Resume your own containers","operationId":"resume_container_api_containers__container_id__resume_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/release":{"post":{"tags":["Containers"],"summary":"Release Container","description":"Release a container back to the warm pool instead of deleting it.\n\nThe Fargate task keeps running — no image re-pull, no cold start.\nThe container becomes available for the next user to claim instantly.\n\nState flow: ASSIGNED → RELEASING (cleanup) → READY (back in pool)\n\nSteps:\n1. ASSIGNED → RELEASING (audit: cleanup started)\n2. Unregister proxy route (old user's subdomain stops working)\n3. Exec reset script inside container (kill processes, restore snapshot)\n4. RELEASING → READY (audit: cleanup complete, container back in pool)","operationId":"release_container_api_containers__container_id__release_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/access-url":{"post":{"tags":["Containers"],"summary":"Generate Access Url","description":"Generate a new access URL with a fresh session token.\n\n## Who Should Use This\n- AI agents sharing container access with other systems\n- External API users creating shareable URLs\n- Internal systems generating time-limited access tokens\n\n## When To Use\n- Refreshing expired session tokens\n- Creating one-time shareable URLs for collaboration\n- Generating time-limited access for security\n- Resetting access after sharing with others\n\n## How To Use\n1. Container must be in \"running\" status\n2. Optionally specify ttl_seconds for custom expiration\n3. Receive new access_url with fresh session token\n\n## Example Workflow\n```\n# Generate a new access URL with 1-hour expiration\nPOST /api/containers/browser_abc123/access-url\n{\"ttl_seconds\": 3600}\n→ Returns: {\n    \"access_url\": \"https://browser_abc123.browsers.sandbox.example.com?token=new_token_...\",\n    \"session_token\": \"new_token_...\",\n    \"expires_at\": \"2026-02-01T15:00:00Z\",\n    \"ttl_seconds\": 3600\n  }\n\n# Share the access_url with a colleague or external system\n```\n\n## Related Endpoints\n- GET /api/containers/{id} - Get current container URLs\n- POST /api/containers/{id}/activity - Keep session alive\n- GET /api/containers/{id}/status - Check container status\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Generate URLs for your own containers\n\n## Technical Notes\nPrevious session tokens remain valid until their own expiration. Generates new token only.","operationId":"generate_access_url_api_containers__container_id__access_url_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessUrlRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessUrlResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/activity":{"post":{"tags":["Containers"],"summary":"Update Container Activity","description":"Send a heartbeat to keep the container active and prevent idle timeout.\n\n## Who Should Use This\n- Client-side JavaScript in container web UIs\n- AI agents with long-running sessions\n- External API users with long-lived connections\n\n## When To Use\n- While actively using a container to prevent idle timeout\n- During long-running operations that may appear idle\n- As a periodic heartbeat from client applications\n\n## How To Use\n1. Call this endpoint periodically (e.g., every 5 minutes)\n2. Container's idle timeout clock resets on each call\n3. Container won't be terminated while heartbeats continue\n\n## Example Workflow\n```\n# Client-side JavaScript sends heartbeat every 5 minutes\nsetInterval(() => {\n    fetch('/api/containers/browser_abc123/activity', {\n        method: 'POST',\n        headers: {'Authorization': 'Bearer ...'}\n    });\n}, 300000);  // 5 minutes\n\n# Server response\n→ Returns: {\"message\": \"Activity updated\", \"container_id\": \"browser_abc123\"}\n```\n\n## Related Endpoints\n- GET /api/containers/{id}/status - Check container status\n- POST /api/containers/{id}/access-url - Refresh access tokens\n- DELETE /api/containers/{id} - Delete container\n\n## Permissions Required\n- `sandbox.write.sandboxes` - Send heartbeats for your own containers\n\n## Technical Notes\nUpdates last_activity timestamp and refreshes proxy TTL. Called automatically by web UIs.","operationId":"update_container_activity_api_containers__container_id__activity_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/status":{"get":{"tags":["Containers"],"summary":"Get Container Status","description":"Get container status for polling until ready.\n\n## Who Should Use This\n- AI agents polling after container creation\n- External API users waiting for containers to be ready\n- Internal dashboards displaying container states\n\n## When To Use\n- Immediately after creating a container (POST /api/browsers, etc.)\n- Poll repeatedly until state becomes \"assigned\"\n- To check if a container is still running\n\n## How To Use\n1. After creating a container, poll this endpoint every 2-5 seconds\n2. Wait until state is \"assigned\" (container ready for use)\n3. Retrieve access_url from response to connect\n\n## Example Workflow\n```\n# After creating a container:\nPOST /api/browsers {\"browser_type\": \"firefox\"}\n→ Returns: {\"container_id\": \"browser_abc123\", \"state\": \"waiting_callback\", ...}\n\n# Poll for status:\nGET /api/containers/browser_abc123/status\n→ Returns: {\"state\": \"waiting_callback\", \"message\": \"Container is starting...\", ...}\n\n# Keep polling...\nGET /api/containers/browser_abc123/status\n→ Returns: {\"state\": \"assigned\", \"access_url\": \"https://...\", \"message\": \"Container is assigned and ready for use.\"}\n\n# Now use the access_url!\n```\n\n## State Meanings\n- `provisioning` - Container is being created\n- `waiting_callback` - Waiting for container health check\n- `ready` - Container is ready (pool containers only)\n- `assigned` - Container is ready for use (your final target state)\n- `failed` - Container failed to start (check message for reason)\n- `terminating` / `terminated` - Container is being/has been deleted\n\n## Related Endpoints\n- POST /api/browsers - Create browser container\n- POST /api/desktops - Create desktop container\n- POST /api/ephemeral - Create ephemeral container\n- GET /api/containers/{id} - Get full container details\n\n## Permissions Required\n- `sandbox.read.sandboxes` - Poll status of your own containers\n\n## Technical Notes\nLightweight status endpoint optimized for frequent polling. Returns access_url when ready.","operationId":"get_container_status_api_containers__container_id__status_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContainerStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/agent-bootstrap":{"get":{"tags":["Containers"],"summary":"Get Container Agent Bootstrap","description":"Get the AI-agent bootstrap bundle for a manager-enabled desktop.\n\nThe API/agent equivalent of the HTML \"Copy AI agent bootstrap\" button: returns the\nmanager-go (:1337) URL + bearer token plus ready-to-run install/connect commands so an\nagent (or a human) can drive the desktop with the computer-agent CLI.\n\n## When To Use\n- You created a desktop with the manager feature enabled and want to attach an agent.\n- Poll GET /api/containers/{id}/status for the manager_url/token (manager_status ==\n  \"registered\"); then poll GET /api/containers/{id}/manager-status until healthy==true\n  (a real :1337 probe) before driving the desktop.\n\n## Returns\n`{manager_url, manager_token, install_cmd, connect_cmd, llms_txt_url, bootstrap}`.\n\n## Permissions Required\n- `sandbox.read.sandboxes` - Read container details (owner or admin).\n\n## Technical Notes\nReturns 404 when the container is not a manager-enabled desktop, or when the manager\nroute/token has not been provisioned yet.","operationId":"get_container_agent_bootstrap_api_containers__container_id__agent_bootstrap_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/manager-status":{"get":{"tags":["Containers"],"summary":"Get Container Manager Status","description":"Probe the in-container manager-go (:1337) for REAL readiness (desktops).\n\n`GET /status` reports `manager_status=\"registered\"` (the proxy route exists) — that does\nNOT mean the manager is up (it boot-installs best-effort). THIS endpoint actually calls the\nmanager's /health with the bearer token. Agents should poll this until `healthy==true`\nbefore driving the desktop. Mirrors the sandbox /manager-status pattern.\n\n## Returns\n`{healthy: bool, status: \"healthy\"|\"unreachable\"|\"unauthorized\"|\"pending\"|\"disabled\", manager_url}`.\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"get_container_manager_status_api_containers__container_id__manager_status_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/containers/{container_id}/boot-logs":{"get":{"tags":["Containers"],"summary":"Get Container Boot Logs","description":"Boot / auto-install visibility for a desktop — ticket 20260625 (P4.2, MC-10 PARTIAL).\n\nReports the auto-install OUTCOME, derived from observed truth (audit A-4): a live\n\"installing\" is re-checked against the real manager :1337/health probe (→ \"ready\")\nand against the bounded install window (→ \"failed\"); a stale \"installing\" is never\nreported past that window. Distinct from `/manager-status` (reachability right now).\n\nSECURITY — raw log lines are WITHHELD (ticket 20260710 T4 / TS-04, decision\nD-2026-07-10-C). The boot-log tail is in-container [computer-manager] install output\nand can contain the injected MANAGER_API_TOKEN; the manager-{id} proxy route runs with\nforward-auth DISABLED, so a leaked token is RCE on that user's computer. No\nlog-redaction processor exists in app/ yet (owned by\ntickets/20260518_log_secret_redaction_processor/). Until it lands this endpoint returns\n`install_status` ONLY. See `_boot_log_redaction_available()`.\n\n## Returns\n`{container_id, install_status, source, note}` (no raw log lines until redaction ships).\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"get_container_boot_logs_api_containers__container_id__boot_logs_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/pool-status":{"get":{"tags":["Containers"],"summary":"Get Pool Status","description":"Get warm pool status showing ready container counts per type.\n\nReturns pool availability so the UI can show \"Instant (from pool)\"\nvs \"Cold start (~2-5 min)\" badges in the create dialog.","operationId":"get_pool_status_api_pool_status_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/storage":{"get":{"tags":["My Computers"],"summary":"One Computer's storage gauge (size vs quota, home status)","description":"Storage accounting for one Computer's persistent home (filesystem ticket\nP2.2 surface). 404 for a Computer with no home yet (homes are provisioned\nlazily on the first persistent attach) and — parity — for a foreign home.\n\n## Permissions Required\n- `sandbox.read.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"get_computer_storage_api_computers__container_id__storage_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeStorageStatus"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/sync/s3":{"get":{"tags":["My Computers"],"summary":"Read this Computer's customer-S3 sync config (never returns credentials)","description":"The customer's registered S3 sync destination for this Computer's home. The\nsecret access key is stored OFF-BAND and is NEVER included — only a\n`has_credentials` boolean signals whether one is set. Empty (`configured:false`)\nwhen nothing has been registered.\n\n## Permissions Required\n- `sandbox.read.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"get_computer_sync_s3_api_computers__container_id__sync_s3_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3SyncConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["My Computers"],"summary":"Register/update a customer S3 bucket + credentials (creds stored off-band)","description":"Register or update the customer-owned S3 destination. The credentials, when\nsupplied, are written to a SEPARATE off-band record (SK=SYNC_S3_SECRET) that no\nread endpoint returns; the client-visible config carries only bucket/region/\nprefix/sse and a `has_credentials` flag. Omitting the keys on an edit keeps the\nstored credential unchanged. The sync RUN path is owner/infra-gated and not\ninvoked here — this only persists config.\n\n## Permissions Required\n- `sandbox.write.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"put_computer_sync_s3_api_computers__container_id__sync_s3_put","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3SyncConfigRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3SyncConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["My Computers"],"summary":"Remove this Computer's S3 sync config and scrub its off-band credential","description":"Remove the customer-S3 sync config for this Computer. The off-band credential\nrecord is overwritten to scrub the stored secret, and the config record is\ntombstoned (`configured:false`) so a subsequent GET reads as not-configured.\n\n## Permissions Required\n- `sandbox.write.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"delete_computer_sync_s3_api_computers__container_id__sync_s3_delete","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/storage/usage":{"get":{"tags":["My Computers"],"summary":"Your org's home-storage gauge + the homes you may see","description":"The caller's persistent-home storage usage: the org's `sandbox.home_storage_gb`\ngauge (current/limit — the attach gate's inputs) plus the homes the caller may\nsee — their OWN homes; org admins see the whole org (GSI walks only, H2/H3;\ndeny-by-default on an unresolvable org).\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"get_storage_usage_api_storage_usage_get","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgStorageUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/files":{"get":{"tags":["My Computers"],"summary":"Browse a Computer's files (list a directory)","description":"List one directory of a Computer's manager workspace (the Drives/Files browser).\n\n`path` is optional and workspace-relative (default = workspace root). The path\nis validated app-side (rejects `..`/absolute/backslash/control) AND re-checked\nby the manager's `ResolveWithin` — depth in defence.\n\n## Permissions Required\n- `sandbox.read.sandboxes` — owner / same-org admin / cross-tenant (foreign→404)","operationId":"list_computer_files_api_computers__container_id__files_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"path","in":"query","required":false,"schema":{"type":"string","default":"","title":"Path"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["My Computers"],"summary":"Upload a file to a Computer","description":"Upload a file into a Computer's manager workspace (multipart passthrough).\n\nTarget path = `path` or `filename` or the uploaded file's own name, validated\nworkspace-relative. Honors `SANDBOX_UPLOAD_MAX_BYTES` app-side; the manager\ncaps multipart at 64 MiB and returns a sha256 checksum which is surfaced.\n\n## Permissions Required\n- `sandbox.write.sandboxes` — owner / same-org admin (foreign→404)","operationId":"upload_computer_file_api_computers__container_id__files_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_computer_file_api_computers__container_id__files_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["My Computers"],"summary":"Delete one file from a Computer","description":"Delete one file from a Computer's manager workspace.\n\nNOTE: this requires a manager that serves `DELETE /files/delete` — a go-manager\nchange (Phase 3) that needs a manager RELEASE to work live. Until the fleet is\non such a manager, the route is gated by `COMPUTER_FILE_DELETE_ENABLED`\n(default off) and returns 501 so it degrades cleanly rather than misreporting\na missing op as a missing file.\n\n## Permissions Required\n- `sandbox.write.sandboxes` — delete is a write (owner / same-org admin, foreign→404)","operationId":"delete_computer_file_api_computers__container_id__files_delete","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"path","in":"query","required":true,"schema":{"type":"string","title":"Path"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileDeleteResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/files/download":{"get":{"tags":["My Computers"],"summary":"Download one file from a Computer","description":"Stream one file back from a Computer's manager workspace.\n\n`path` is required and workspace-relative. The manager's `allow_any_path`\nescape hatch is never forwarded — the tenant browser stays workspace-confined.\n\n## Permissions Required\n- `sandbox.read.sandboxes`","operationId":"download_computer_file_api_computers__container_id__files_download_get","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"path","in":"query","required":true,"schema":{"type":"string","title":"Path"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/home-accounting/run":{"post":{"tags":["My Computers"],"summary":"Run Home Accounting","description":"Run one home-accounting pass for an org NOW (measure → cache → billing\naccrual → GC ladder → purge retries → gauge reconcile). The periodic loop\nships DARK (HOME_ACCOUNTING_ENABLED=false); this route is the exercisable\npath and the P3.3 clock-shifted-ladder verify hook.\n\n## Permissions Required\n- `sandbox.admin.sandboxes` — own org only (cross-tenant staff may pass another org)","operationId":"run_home_accounting_api_admin_home_accounting_run_post","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeAccountingRunRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeAccountingRunResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/home":{"delete":{"tags":["My Computers"],"summary":"Right-to-delete: purge a Computer's home (files, snapshots, exports) for good","description":"Right-to-delete for a Computer's persistent home (filesystem ticket P3.4;\nGDPR Art. 17 hygiene, 20260501 §9.1): purge the S3 {org}/{computer}/ prefix,\nthe EFS access point, the storage-gauge share, and the home record — every\nstep audited; an incomplete purge stays `purging` and is retried by the\naccounting pass until done (≤24 h SLA alarmed via HOME_PURGE_MAX_HOURS).\n\nThe home may outlive its Computer (detached soul, ticket OQ-3), so this\nchecks the HOME record's owner/org, not the container's. Foreign → 404.\n\n## Permissions Required\n- `sandbox.delete.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"delete_computer_home_api_computers__container_id__home_delete","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/computers/{container_id}/recreate":{"post":{"tags":["My Computers"],"summary":"Recreate a deleted Computer onto its surviving home","description":"Recreate a TERMINATED Computer onto its surviving home (filesystem ticket\n20260703 P1.4 — the \"detached soul\" comes back, OQ-3). Terminate deletes the\ncontainer row but the HOME row + S3 stop-snapshot survive; this brings the\nmachine back with the SAME computer_id, its recorded desktop type and name,\na re-attached home + writer lock, and (when the snapshot flag is lit) its\nlast stop-snapshot restored into /config.\n\nThin wrapper over POST /api/desktops with computer_id set — identical quota,\nbilling, tier-clamp and manager behavior. Refusals: unknown/foreign home →\n404 (parity); home mid-purge → 409; a live container row → 409 (a STOPPED\nComputer must be resumed, not recreated).\n\n## Permissions Required\n- `sandbox.create.sandboxes` — owner / same-org admin / cross-tenant (Phase-2 checked)","operationId":"recreate_computer_api_computers__container_id__recreate_post","parameters":[{"name":"container_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"pattern":"^(?:browser|desktop|ephemeral)_[a-f0-9]{8,32}$","title":"Container Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DesktopResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quota/capabilities":{"get":{"summary":" Capabilities","operationId":"_capabilities_quota_capabilities_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/quota/health":{"get":{"summary":" Health","operationId":"_health_quota_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AccessUrlRequest":{"properties":{"ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":300.0},{"type":"null"}],"title":"Ttl Seconds","description":"Token TTL in seconds (5 min to 24 hours)"}},"type":"object","title":"AccessUrlRequest","description":"Request to generate new access URL with fresh token","examples":[{"ttl_seconds":7200}]},"AccessUrlResponse":{"properties":{"access_url":{"type":"string","title":"Access Url","description":"Full access URL with new token"},"session_token":{"type":"string","title":"Session Token","description":"New session token"},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Token expiration time"},"ttl_seconds":{"type":"integer","title":"Ttl Seconds","description":"Token TTL in seconds"}},"type":"object","required":["access_url","session_token","expires_at","ttl_seconds"],"title":"AccessUrlResponse","description":"Response with new access URL","examples":[{"access_url":"https://brw-a1b2c3d4.containers.example.com?token=sess_newtoken123","expires_at":"2026-01-31T14:30:00Z","session_token":"sess_newtoken123","ttl_seconds":7200}]},"AlertSeverity":{"type":"string","enum":["info","warning","critical","exceeded"],"title":"AlertSeverity","description":"Quota usage alert levels."},"AnonymousCheckoutResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Stripe checkout session id"},"url":{"type":"string","title":"Url","description":"Stripe Checkout URL the browser should be redirected to"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At","description":"Session expiry (ISO 8601)"},"status":{"type":"string","title":"Status","description":"open, complete, expired"},"verification_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verification Token","description":"Anti-fraud verification token"},"access_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Access Token","description":"JWT for the newly-created account; absent if the account already existed"},"org_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id","description":"Org id of the newly-created account; absent if the account already existed"},"new_account":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"New Account","description":"True when a new account was provisioned in this call"},"account_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Error","description":"Customer-safe message when account provisioning failed (checkout still proceeded)"}},"additionalProperties":true,"type":"object","required":["id","url","status"],"title":"AnonymousCheckoutResponse","description":"Response for `POST /api/payments/checkout/anonymous/{plan_id}`.\n\nExtends a Stripe checkout session payload with the freshly-provisioned\naccount's `org_id` and `access_token` (when account creation succeeded).\nClients redirect the browser to `url` and may use the access token to\nlog the new user in once they return."},"AppInstallRequest":{"properties":{"sandbox_id":{"type":"string","maxLength":128,"minLength":1,"title":"Sandbox Id"},"inputs":{"type":"object","title":"Inputs"}},"additionalProperties":false,"type":"object","required":["sandbox_id"],"title":"AppInstallRequest","description":"POST /api/apps/{listing_id}/install body.\n\n`sandbox_id` names an EXISTING running sandbox the caller owns (P1 scope).\n`inputs` is the install form — the user's answers to the manifest's `inputs`;\nsecret values live here in flight only and are NEVER persisted (DESIGN §5.1).\nUnknown inputs are rejected by the renderer (no silent extra variables)."},"AppRetryRequest":{"properties":{"inputs":{"type":"object","title":"Inputs"}},"additionalProperties":false,"type":"object","title":"AppRetryRequest","description":"POST /api/apps/installs/{install_id}/retry body (OPTIONAL — DF-003).\n\n`inputs` re-supplies CORRECTED secret (or non-secret) values for a retry of a\nFAILED install — e.g. a wrong API key that caused the failure. Values live here\nin flight only and are NEVER persisted; only the re-supplied secret files are\nre-delivered to the box. An empty body (or omitted `inputs`) is a bare retry."},"AuditLogEntry":{"properties":{"timestamp":{"type":"string","title":"Timestamp","default":""},"actor_id":{"type":"string","title":"Actor Id","default":""},"actor_email":{"type":"string","title":"Actor Email","default":""},"action":{"type":"string","title":"Action","default":""},"resource_type":{"type":"string","title":"Resource Type","default":""},"resource_id":{"type":"string","title":"Resource Id","default":""},"details":{"type":"string","title":"Details","default":""},"severity":{"type":"string","title":"Severity","default":"info"}},"type":"object","title":"AuditLogEntry","description":"A single audit log entry"},"AuditLogResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/AuditLogEntry"},"type":"array","title":"Events"},"total":{"type":"integer","title":"Total","default":0}},"type":"object","title":"AuditLogResponse","description":"Response containing audit log entries"},"BillingBalanceResponse":{"properties":{"balance":{"type":"string","title":"Balance","description":"Current balance","default":"0.00"},"available_balance":{"type":"string","title":"Available Balance","description":"Available after reservations","default":"0.00"},"currency":{"type":"string","title":"Currency","description":"Currency code","default":"usd"}},"additionalProperties":true,"type":"object","title":"BillingBalanceResponse"},"BillingTransactionEntry":{"properties":{"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Transaction id"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type","description":"balance, credit, debit, reserve, commit, refund"},"timestamp":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timestamp"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"debit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Debit"},"credit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credit"},"balance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Balance"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Metadata"},"transaction_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transaction Id"},"amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amount"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"additionalProperties":true,"type":"object","title":"BillingTransactionEntry","description":"Billing transaction entry. Field naming matches billing's actual\n/billing/{org}/transactions response (id + debit/credit, not the\nearlier transaction_id + amount). All fields optional so future\nschema additions in billing flow through via extra='allow'."},"BillingTransactionsResponse":{"properties":{"transactions":{"items":{"$ref":"#/components/schemas/BillingTransactionEntry"},"type":"array","title":"Transactions"},"summary":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Summary","description":"Period summary (opening/closing balance, debits, credits)"},"count":{"type":"integer","title":"Count","default":0},"has_more":{"type":"boolean","title":"Has More","default":false}},"additionalProperties":true,"type":"object","title":"BillingTransactionsResponse","description":"Billing's actual /billing/{org}/transactions shape:\n   {transactions: [...], summary: {opening_balance, total_debits, ...}}\nNO count or has_more in the actual response — derive count from len()\nand surface summary as a structured dict."},"BillingUsageRecord":{"properties":{"record_id":{"type":"string","title":"Record Id","description":"Usage record ID","default":""},"resource_type":{"type":"string","title":"Resource Type","description":"compute, storage, etc.","default":""},"cost":{"type":"string","title":"Cost","description":"Cost for this record","default":"0.00"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"additionalProperties":true,"type":"object","title":"BillingUsageRecord"},"BillingUsageRecordsResponse":{"properties":{"records":{"items":{"$ref":"#/components/schemas/BillingUsageRecord"},"type":"array","title":"Records"},"count":{"type":"integer","title":"Count","default":0},"has_more":{"type":"boolean","title":"Has More","default":false}},"additionalProperties":true,"type":"object","title":"BillingUsageRecordsResponse"},"BillingUsageSummaryResponse":{"properties":{"org_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id"},"start_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date"},"period":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Period"},"summary":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Summary"},"group_by":{"anyOf":[{},{"type":"null"}],"title":"Group By"},"total_cost":{"type":"string","title":"Total Cost","description":"Total cost this period","default":"0.00"},"period_start":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Period Start"},"period_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Period End"}},"additionalProperties":true,"type":"object","title":"BillingUsageSummaryResponse","description":"Billing's actual /billing/usage/{org}/summary shape:\n   {org_id, start_date, end_date, period, summary: {total_cost, ...},\n    group_by}\nSurface the most-used fields at top-level too via model_post_init so\nlegacy callers reading `.total_cost` directly keep working."},"BlueprintCreateRequest":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"},"base_desktop_type":{"type":"string","title":"Base Desktop Type","default":"ubuntu-xfce-agent"},"description":{"anyOf":[{"type":"string","maxLength":280},{"type":"null"}],"title":"Description"},"apps":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Apps"},"default_cpu":{"type":"string","title":"Default Cpu","default":"2048"},"default_memory":{"type":"string","title":"Default Memory","default":"4096"},"owner_scope":{"type":"string","enum":["user","org","platform"],"title":"Owner Scope","default":"user"}},"additionalProperties":false,"type":"object","required":["name"],"title":"BlueprintCreateRequest","description":"POST /api/blueprints body. Per-user authoring; the technical knobs live here, hidden from\nthe end user who only picks a Blueprint + a name.\n\nLIMITS ARE GRANTS, NOT CONFIG (ticket 20260710, decision D-2026-07-10-B). `max_cpu`,\n`max_memory`, and `disk_gb` are DELIBERATELY absent — they are policy the caller may not\nauthor. With `extra=\"forbid\"`, sending any of them (or `org_id`, or any unknown field) is a\n422 — NOT a silent ignore (the exact `extra=\"ignore\"` trap the seam ticket found upstream).\n`default_cpu`/`default_memory` remain as *requests*; the caller's tier decides the grant at\ninstantiate time. `org_id` is server-assigned from the caller, never from this body (TS-01)."},"BlueprintListResponse":{"properties":{"blueprints":{"items":{"$ref":"#/components/schemas/BlueprintSummary"},"type":"array","title":"Blueprints"},"total":{"type":"integer","title":"Total","default":0}},"type":"object","title":"BlueprintListResponse"},"BlueprintSummary":{"properties":{"blueprint_id":{"type":"string","title":"Blueprint Id","description":"Blueprint id (bp-<hex>)"},"name":{"type":"string","title":"Name","description":"User-facing name, e.g. 'Ubuntu Agent Desktop'"},"base_desktop_type":{"type":"string","title":"Base Desktop Type","description":"Desktop image key this blueprint instantiates"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"owner_scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Scope","description":"platform (curated, everyone) | org (this org) | user (the caller's own)"},"apps":{"items":{"type":"string"},"type":"array","title":"Apps","description":"one-click-claw AppListing ids installed on first boot (A3; claw manifest schema)"},"default_cpu":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Cpu","description":"Requested vCPU units (tier-clamped at instantiate)"},"default_memory":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Memory","description":"Requested memory MB (tier-clamped at instantiate)"},"version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version","description":"Blueprint version (Computers pin the version they were created from)"}},"type":"object","required":["blueprint_id","name","base_desktop_type"],"title":"BlueprintSummary","description":"A curated Computer config (base image + tools + defaults). DESIGN §2.1.\n\nowner_scope tells the catalog UI WHO a row is for (platform-curated / this\norg / the caller's own) — the backend has already scoped the rows, so this\nfield is presentation, never authorization. Resource fields are REQUESTS\n(clamped to the caller's tier at instantiate time, D-2026-07-10-B).","examples":[{"apps":["claw-personal-assistant"],"base_desktop_type":"ubuntu-xfce-agent","blueprint_id":"bp-1f2e3d4c5b6a7988","default_cpu":"2048","default_memory":"4096","description":"Agent-ready Ubuntu with the research toolchain preinstalled","name":"Research Agent Desktop","owner_scope":"org","version":1}]},"Body_complete_checkout_api_payments_checkout_complete_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"new_account":{"type":"boolean","title":"New Account","default":false}},"type":"object","required":["session_id"],"title":"Body_complete_checkout_api_payments_checkout_complete_post"},"Body_create_anonymous_checkout_api_payments_checkout_anonymous__plan_id__post":{"properties":{"email":{"type":"string","title":"Email"},"session_token":{"type":"string","title":"Session Token"},"fingerprint":{"type":"string","title":"Fingerprint"}},"type":"object","required":["email","session_token","fingerprint"],"title":"Body_create_anonymous_checkout_api_payments_checkout_anonymous__plan_id__post"},"Body_create_topup_api_payments_topup_post":{"properties":{"amount":{"type":"number","maximum":10000.0,"exclusiveMinimum":0.0,"title":"Amount"}},"type":"object","required":["amount"],"title":"Body_create_topup_api_payments_topup_post"},"Body_upload_computer_file_api_computers__container_id__files_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"File to upload"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename","description":"Target path under the workspace"},"path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Path","description":"Alias for filename (target path)"},"overwrite":{"type":"boolean","title":"Overwrite","description":"Replace an existing file at the target","default":true}},"type":"object","required":["file"],"title":"Body_upload_computer_file_api_computers__container_id__files_post"},"Body_upload_to_sandbox_api_sandboxes__sandbox_id__upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"File to upload"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename","description":"Optional target path under /workspace"}},"type":"object","required":["file"],"title":"Body_upload_to_sandbox_api_sandboxes__sandbox_id__upload_post"},"BrowserRequest":{"properties":{"browser_type":{"allOf":[{"$ref":"#/components/schemas/BrowserType"}],"description":"Type of browser","default":"firefox"},"ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":300.0},{"type":"null"}],"title":"Ttl Seconds","description":"Session token TTL"},"idle_timeout_minutes":{"type":"integer","maximum":1440.0,"minimum":5.0,"title":"Idle Timeout Minutes","description":"Auto-stop after idle","default":30},"cpu":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpu","description":"CPU units (e.g., '1024')"},"memory":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Memory","description":"Memory in MB (e.g., '2048')"},"environment":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Environment","description":"Environment variables"},"metadata":{"type":"object","title":"Metadata","description":"Additional metadata"},"enable_debug_port":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Debug Port","description":"Enable debug port for AI agent access. Defaults to True (all types support it). Set False to disable."},"homepage_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Homepage Url","description":"Default homepage URL. Sets CHROME_CLI or FIREFOX_CLI startup URL."}},"type":"object","title":"BrowserRequest","description":"Request to create a browser container","examples":[{"browser_type":"firefox","environment":{"DISPLAY_RESOLUTION":"1920x1080"},"idle_timeout_minutes":60,"metadata":{"project":"price-monitor","purpose":"web-scraping"}},{"browser_type":"chrome","enable_debug_port":true,"homepage_url":"https://example.com","metadata":{"agent":"browser-use","purpose":"ai-agent"}}]},"BrowserResponse":{"properties":{"container_id":{"type":"string","title":"Container Id","description":"Unique container identifier"},"browser_type":{"type":"string","title":"Browser Type","description":"Browser type (firefox, chrome, etc.)"},"status":{"type":"string","title":"Status","description":"Container status"},"state":{"type":"string","title":"State","description":"State machine state","default":"provisioning"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url","description":"Public URL for browser access"},"access_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Access Url","description":"Full access URL with session token"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token","description":"Session authentication token"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration time"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"from_pool":{"type":"boolean","title":"From Pool","description":"Whether claimed from warm pool","default":false},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Status message"},"cdp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cdp Url","description":"Chrome DevTools Protocol URL for AI agent connection"},"debug_port":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Debug Port","description":"CDP debug port number if enabled"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode","description":"Container mode: interactive, agent, headless, interactive+agent"}},"type":"object","required":["container_id","browser_type","status","created_at"],"title":"BrowserResponse","description":"Response after creating browser container","examples":[{"access_url":"https://brw-a1b2c3d4.containers.example.com?token=sess_abc123","browser_type":"firefox","cdp_url":"wss://debug-brw-a1b2c3d4.browser.ab0t.com","container_id":"brw_a1b2c3d4","created_at":"2026-01-31T10:30:00Z","debug_port":9222,"expires_at":"2026-01-31T12:30:00Z","from_pool":false,"message":"Browser container is ready","mode":"interactive+agent","public_url":"https://brw-a1b2c3d4.containers.example.com","session_token":"sess_abc123","state":"assigned","status":"running"},{"access_url":"https://brw-e5f6g7h8.browser.ab0t.com?token=sess_def456","browser_type":"chrome","cdp_url":"wss://debug-brw-e5f6g7h8.browser.ab0t.com","container_id":"brw_e5f6g7h8","created_at":"2026-01-31T10:30:00Z","debug_port":9222,"expires_at":"2026-01-31T14:30:00Z","from_pool":true,"message":"Container assigned from warm pool. Ready to use!","mode":"interactive+agent","public_url":"https://brw-e5f6g7h8.browser.ab0t.com","session_token":"sess_def456","state":"assigned","status":"running"}]},"BrowserType":{"type":"string","enum":["firefox","chrome","chromium-headless","selenium-chrome","headless-shell","lightpanda"],"title":"BrowserType","description":"Supported browser container types"},"CancelSubscriptionResponse":{"properties":{"subscription_id":{"type":"string","title":"Subscription Id"},"status":{"type":"string","title":"Status"},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End"},"canceled_at":{"type":"string","title":"Canceled At"},"message":{"type":"string","title":"Message"}},"additionalProperties":true,"type":"object","required":["subscription_id","status","cancel_at_period_end","canceled_at","message"],"title":"CancelSubscriptionResponse"},"CheckoutCompleteResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Session status: complete, paid, open, expired"},"session_id":{"type":"string","title":"Session Id","description":"Stripe checkout session id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Customer email captured at checkout"},"plan_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan Id","description":"Plan id the customer purchased"},"tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tier","description":"Resolved tier id (None if plan→tier mapping was not found)"},"tier_synced":{"type":"boolean","title":"Tier Synced","description":"True iff the tier was successfully PUT to the billing service in this call","default":false},"tier_pending":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Tier Pending","description":"True when tier resolved but billing sync failed; webhook will retry"},"retry":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Retry","description":"True when the session is not yet paid and the client should retry shortly"},"redirect":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect","description":"Suggested in-app URL for the client to navigate to next"}},"additionalProperties":true,"type":"object","required":["status","session_id"],"title":"CheckoutCompleteResponse","description":"Response for `POST /api/payments/checkout/complete`.\n\nReturned after the browser comes back from Stripe with a session id.\nReports the final session status, the org/plan/email decoded from the\nsession metadata, and whether tier sync to billing succeeded."},"CheckoutInitResponse":{"properties":{"session_token":{"type":"string","title":"Session Token","description":"Anti-fraud session token"},"expires_at":{"type":"string","title":"Expires At","description":"Token expiration (ISO 8601)"},"fingerprint":{"type":"string","title":"Fingerprint","description":"Browser fingerprint hash"}},"additionalProperties":true,"type":"object","required":["session_token","expires_at","fingerprint"],"title":"CheckoutInitResponse"},"CheckoutSessionResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Stripe session ID"},"url":{"type":"string","title":"Url","description":"Redirect URL for Stripe Checkout"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"status":{"type":"string","title":"Status","description":"open, complete, expired"},"verification_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Verification Token"}},"additionalProperties":true,"type":"object","required":["id","url","status"],"title":"CheckoutSessionResponse"},"CogSaveRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"description":{"type":"string","maxLength":500,"title":"Description","default":""},"owner_scope":{"type":"string","enum":["user","org"],"title":"Owner Scope","default":"user"},"task":{"type":"string","maxLength":8000,"title":"Task","default":""},"harness":{"type":"string","maxLength":64,"title":"Harness","default":"claude-code"},"template_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Template Id"},"definition":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Definition"},"input_schema":{"items":{"type":"object"},"type":"array","maxItems":50,"title":"Input Schema"},"grants":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Grants"},"trigger":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Trigger"},"output":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Output"},"autonomy":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Autonomy"},"target_sandbox_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Target Sandbox Id"},"run_in_docker":{"type":"boolean","title":"Run In Docker","default":false},"docker_image":{"type":"string","maxLength":300,"title":"Docker Image","default":""}},"additionalProperties":false,"type":"object","required":["name"],"title":"CogSaveRequest","description":"POST/PUT /api/cogs body. `user_id`/`org_id` are server-assigned from the caller\n— NEVER read from the body (extra=forbid → an unknown field is a 422). The webhook\ntoken is server-minted (never accepted from the body); grants/trigger/output/\nautonomy are normalized + safe-defaulted server-side."},"ComputerExportResponse":{"properties":{"message":{"type":"string","title":"Message"},"download_url":{"type":"string","title":"Download Url"},"expires_in_seconds":{"type":"integer","title":"Expires In Seconds"}},"type":"object","required":["message","download_url","expires_in_seconds"],"title":"ComputerExportResponse","description":"POST /api/computers/{id}/export — filesystem ticket 20260703 P3.2."},"ComputerListResponse":{"properties":{"computers":{"items":{"$ref":"#/components/schemas/ComputerSummary"},"type":"array","title":"Computers"},"total":{"type":"integer","title":"Total","default":0}},"type":"object","title":"ComputerListResponse"},"ComputerRenameRequest":{"properties":{"computer_name":{"type":"string","maxLength":80,"minLength":1,"title":"Computer Name"}},"type":"object","required":["computer_name"],"title":"ComputerRenameRequest","description":"PATCH /api/computers/{id} body — rename only (DESIGN §3.2)."},"ComputerSummary":{"properties":{"container_id":{"type":"string","title":"Container Id"},"computer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Computer Name"},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blueprint Id"},"blueprint_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blueprint Name"},"desktop_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Desktop Type"},"state":{"type":"string","title":"State"},"install_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Install Status"},"manager_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Status"},"cpu":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpu"},"memory":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Memory"},"saved_ago":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Saved Ago"},"apps_install_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Apps Install Status","description":"Blueprint-apps boot install rollup (A3): pending | installing | ready | failed | unavailable | unknown. None = no apps requested."}},"type":"object","required":["container_id","state"],"title":"ComputerSummary","description":"A persistent, owned desktop (Blueprint -> Computer -> State). DESIGN §2.2."},"ContactRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"email":{"type":"string","maxLength":320,"minLength":3,"pattern":"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$","title":"Email"},"subject":{"type":"string","pattern":"^(support|sales|billing|feedback|partnership|other)$","title":"Subject"},"message":{"type":"string","maxLength":5000,"minLength":10,"title":"Message"}},"type":"object","required":["name","email","subject","message"],"title":"ContactRequest","description":"Public contact form submission (no auth required)"},"ContactResponse":{"properties":{"contact_id":{"type":"string","title":"Contact Id","description":"Unique contact submission ID"},"status":{"type":"string","title":"Status","description":"Submission status","default":"received"}},"type":"object","required":["contact_id"],"title":"ContactResponse","description":"Response after submitting contact form"},"ContainerListResponse":{"properties":{"containers":{"items":{"$ref":"#/components/schemas/ContainerSummary"},"type":"array","title":"Containers","description":"List of containers"},"total":{"type":"integer","title":"Total","description":"Total count"}},"type":"object","required":["containers","total"],"title":"ContainerListResponse","description":"Response containing list of containers","examples":[{"containers":[{"browser_type":"firefox","container_id":"brw_a1b2c3d4","container_type":"browser","state":"assigned"},{"container_id":"dsk_e5f6g7h8","container_type":"desktop","desktop_type":"alpine-xfce","state":"assigned"}],"total":2}]},"ContainerStatusResponse":{"properties":{"container_id":{"type":"string","title":"Container Id","description":"Container identifier"},"status":{"type":"string","title":"Status","description":"Container status (pending, running, stopped, etc.)","default":"pending"},"state":{"type":"string","title":"State","description":"Current state (provisioning, assigned, etc.)"},"container_type":{"type":"string","title":"Container Type","description":"Container type (browser, desktop, ephemeral)"},"browser_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Browser Type","description":"Browser type if applicable"},"desktop_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Desktop Type","description":"Desktop type if applicable"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url","description":"Public access URL"},"access_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Access Url","description":"Full access URL with token"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token","description":"Session token"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At","description":"Creation time"},"from_pool":{"type":"boolean","title":"From Pool","description":"Whether from warm pool","default":false},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Status message"},"cdp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cdp Url","description":"Chrome DevTools Protocol URL for AI agent connection"},"debug_port":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Debug Port","description":"CDP debug port number if enabled"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode","description":"Container mode: interactive, agent, headless, interactive+agent"},"manager_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Url","description":"manager-go (:1337) API URL for agent control (desktops)"},"manager_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Token","description":"Bearer token for the manager-go API"},"manager_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Status","description":"Manager readiness: pending|ready"},"install_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Install Status","description":"Auto-install outcome: installing|ready|failed (persistent Computers; ticket 20260625 P4.2)"}},"type":"object","required":["container_id","state","container_type"],"title":"ContainerStatusResponse","description":"Response for container status polling endpoint","examples":[{"access_url":"https://brw-a1b2c3d4.containers.example.com?token=sess_abc123","browser_type":"firefox","cdp_url":"wss://debug-brw-a1b2c3d4.browser.ab0t.com","container_id":"brw_a1b2c3d4","container_type":"browser","created_at":"2026-01-31T10:30:00Z","debug_port":9222,"expires_at":"2026-01-31T12:30:00Z","from_pool":false,"message":"Container is ready for use","mode":"interactive+agent","public_url":"https://brw-a1b2c3d4.containers.example.com","session_token":"sess_abc123","state":"assigned"}]},"ContainerSummary":{"properties":{"container_id":{"type":"string","title":"Container Id","description":"Unique container identifier"},"container_type":{"type":"string","title":"Container Type","description":"Type: browser, desktop, or ephemeral"},"browser_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Browser Type","description":"Browser type if browser container"},"desktop_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Desktop Type","description":"Desktop type if desktop container"},"ephemeral_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ephemeral Type","description":"Ephemeral type if ephemeral container"},"status":{"type":"string","title":"Status","description":"Container status (pending, running, stopped, etc.)","default":"pending"},"state":{"type":"string","title":"State","description":"Container state machine state"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url","description":"Public access URL"},"access_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Access Url","description":"Full access URL with session token"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token","description":"Session token for authentication"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration time"},"last_activity":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity","description":"Last activity timestamp"},"from_pool":{"type":"boolean","title":"From Pool","description":"Whether container came from warm pool","default":false},"cdp_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cdp Url","description":"Debug URL for AI agent connection"},"debug_port":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Debug Port","description":"Debug port number"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode","description":"Container mode: interactive, agent, headless, interactive+agent"}},"type":"object","required":["container_id","container_type","state","created_at"],"title":"ContainerSummary","description":"Summary of a container for list views","examples":[{"browser_type":"firefox","cdp_url":"wss://debug-brw-a1b2c3d4.browser.ab0t.com","container_id":"brw_a1b2c3d4","container_type":"browser","created_at":"2026-01-31T10:30:00Z","debug_port":9222,"mode":"interactive+agent","public_url":"https://brw-a1b2c3d4.containers.example.com","state":"assigned"}]},"CostBreakdownItem":{"properties":{"sandbox_id":{"type":"string","title":"Sandbox Id","description":"Sandbox identifier"},"name":{"type":"string","title":"Name","description":"Sandbox name"},"cost":{"type":"string","title":"Cost","description":"Cost for period"},"hours":{"type":"integer","title":"Hours","description":"Compute hours used"}},"additionalProperties":true,"type":"object","required":["sandbox_id","name","cost","hours"],"title":"CostBreakdownItem","description":"Single sandbox cost entry in a cost report"},"CostReport":{"properties":{"entity_id":{"type":"string","title":"Entity Id","description":"User ID or organization ID"},"entity_type":{"type":"string","title":"Entity Type","description":"Entity type: 'user' or 'organization'"},"period":{"type":"string","title":"Period","description":"Report period: 'daily', 'weekly', 'monthly'"},"total_cost":{"type":"string","title":"Total Cost","description":"Total cost for the period"},"active_sandboxes":{"type":"integer","title":"Active Sandboxes","description":"Number of currently active sandboxes"},"stopped_sandboxes":{"type":"integer","title":"Stopped Sandboxes","description":"Number of stopped sandboxes"},"breakdown":{"items":{"$ref":"#/components/schemas/CostBreakdownItem"},"type":"array","title":"Breakdown","description":"Cost breakdown by sandbox"}},"type":"object","required":["entity_id","entity_type","period","total_cost","active_sandboxes","stopped_sandboxes","breakdown"],"title":"CostReport","description":"Cost report for user or organization","examples":[{"active_sandboxes":2,"breakdown":[{"cost":"32.50","hours":780,"name":"ml-training","sandbox_id":"sandbox_a1b2c3d4"},{"cost":"13.17","hours":316,"name":"dev-env","sandbox_id":"sandbox_e5f6g7h8"}],"entity_id":"user_abc123","entity_type":"user","period":"monthly","stopped_sandboxes":3,"total_cost":"45.67"}]},"DesktopRequest":{"properties":{"desktop_type":{"allOf":[{"$ref":"#/components/schemas/DesktopType"}],"description":"Type of desktop","default":"alpine-xfce"},"ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":300.0},{"type":"null"}],"title":"Ttl Seconds","description":"Session token TTL"},"idle_timeout_minutes":{"type":"integer","maximum":1440.0,"minimum":5.0,"title":"Idle Timeout Minutes","description":"Auto-stop after idle","default":30},"cpu":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpu","description":"CPU units (e.g., '2048')"},"memory":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Memory","description":"Memory in MB (e.g., '4096')"},"environment":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Environment","description":"Environment variables"},"metadata":{"type":"object","title":"Metadata","description":"Additional metadata"},"blueprint_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Blueprint Id","description":"Create as a persistent Computer from this Blueprint"},"computer_name":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Computer Name","description":"Name for the persistent Computer (implies is_computer)"},"computer_id":{"anyOf":[{"type":"string","maxLength":64,"pattern":"^desktop_[a-f0-9]{8,32}$"},{"type":"null"}],"title":"Computer Id","description":"Recreate an existing Computer onto its surviving home (implies is_computer)"},"apps":{"items":{"type":"string"},"type":"array","maxItems":20,"title":"Apps","description":"AppListing ids to install on first boot (references the claw manifest; no forked schema)"}},"type":"object","title":"DesktopRequest","description":"Request to create a desktop container","examples":[{"cpu":"2048","desktop_type":"ubuntu-xfce","idle_timeout_minutes":120,"memory":"4096","metadata":{"purpose":"development","user":"developer@example.com"}}]},"DesktopResponse":{"properties":{"container_id":{"type":"string","title":"Container Id","description":"Unique container identifier"},"desktop_type":{"type":"string","title":"Desktop Type","description":"Desktop type (alpine-xfce, ubuntu-xfce, etc.)"},"status":{"type":"string","title":"Status","description":"Container status"},"state":{"type":"string","title":"State","description":"State machine state","default":"provisioning"},"public_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Url","description":"Public URL for desktop access"},"access_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Access Url","description":"Full access URL with session token"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token","description":"Session authentication token"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration time"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"from_pool":{"type":"boolean","title":"From Pool","description":"Whether claimed from warm pool","default":false},"cpu":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpu","description":"Granted vCPU units (tier-clamped for a Computer)"},"memory":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Memory","description":"Granted memory MB (tier-clamped for a Computer)"},"disk_gb":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Disk Gb","description":"Granted persistent /config size in GB (tier-clamped for a Computer)"},"resource_clamped":{"type":"boolean","title":"Resource Clamped","description":"True if the requested cpu/memory/disk exceeded the tier and were clamped","default":false},"resource_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Note","description":"Human-readable summary of what was clamped, e.g. 'disk_gb 500->40'"},"apps_requested":{"items":{"type":"string"},"type":"array","title":"Apps Requested","description":"AppListing ids pinned to install on first boot"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Status message"}},"type":"object","required":["container_id","desktop_type","status","created_at"],"title":"DesktopResponse","description":"Response after creating desktop container","examples":[{"access_url":"https://dsk-e5f6g7h8.containers.example.com?token=sess_xyz789","container_id":"dsk_e5f6g7h8","created_at":"2026-01-31T10:30:00Z","desktop_type":"ubuntu-xfce","expires_at":"2026-01-31T14:30:00Z","from_pool":true,"message":"Desktop ready - claimed from warm pool","public_url":"https://dsk-e5f6g7h8.containers.example.com","session_token":"sess_xyz789","state":"assigned","status":"running"}]},"DesktopType":{"type":"string","enum":["alpine-xfce","alpine-kde","ubuntu-xfce","ubuntu-xfce-agent","windows"],"title":"DesktopType","description":"Supported desktop container types"},"DownloadWorkspaceResponse":{"properties":{"filename":{"type":"string","title":"Filename","description":"Suggested filename for the download"},"content_base64":{"type":"string","title":"Content Base64","description":"Base64-encoded tarball of workspace"},"message":{"type":"string","title":"Message","description":"Status message"}},"type":"object","required":["filename","content_base64","message"],"title":"DownloadWorkspaceResponse","description":"Response for workspace download endpoint","examples":[{"content_base64":"H4sIAAAAAAAAA+3OMQrCQBCG4eyc4haCCCEWVp7AK9i...","filename":"workspace_sandbox_a1b2c3d4.tar.gz","message":"Workspace packaged for download"}]},"EphemeralRequest":{"properties":{"ephemeral_type":{"allOf":[{"$ref":"#/components/schemas/EphemeralType"}],"description":"Container type/image preset","default":"ubuntu"},"custom_image":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Image","description":"Custom Docker image (only if ephemeral_type=custom)"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Optional name for the container"},"ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":300.0},{"type":"null"}],"title":"Ttl Seconds","description":"Session token TTL"},"idle_timeout_minutes":{"type":"integer","maximum":1440.0,"minimum":5.0,"title":"Idle Timeout Minutes","description":"Auto-stop after idle","default":30},"max_runtime_minutes":{"anyOf":[{"type":"integer","maximum":1440.0,"minimum":5.0},{"type":"null"}],"title":"Max Runtime Minutes","description":"Max runtime before auto-terminate","default":60},"cpu":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpu","description":"CPU units (256, 512, 1024, 2048, 4096)"},"memory":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Memory","description":"Memory in MB"},"storage_gb":{"anyOf":[{"type":"integer","maximum":200.0,"minimum":21.0},{"type":"null"}],"title":"Storage Gb","description":"Optional sized scratch disk in GiB (21-200) for the container's ephemeral storage. Omit for the platform default (~20 GiB). Ephemeral scratch only — not persisted across stop."},"command":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Command","description":"Command to run on startup"},"environment":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Environment","description":"Environment variables"},"volumes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Volumes","description":"Volume mounts"},"metadata":{"type":"object","title":"Metadata","description":"Additional metadata"}},"type":"object","title":"EphemeralRequest","description":"Request to create an ephemeral compute container.\n\nEphemeral containers are lightweight Fargate containers for:\n- Coding agents\n- Script execution\n- API workers\n- General compute tasks\n\nThey support warm pooling for instant provisioning of common types.","examples":[{"command":"python /app/process.py","cpu":"1024","environment":{"DATA_SOURCE":"s3://bucket/data","PYTHONUNBUFFERED":"1"},"ephemeral_type":"python","idle_timeout_minutes":15,"max_runtime_minutes":120,"memory":"2048","metadata":{"job_id":"job_12345","priority":"high"},"name":"data-processor"}]},"EphemeralResponse":{"properties":{"container_id":{"type":"string","title":"Container Id","description":"Unique container identifier"},"ephemeral_type":{"type":"string","title":"Ephemeral Type","description":"Container type"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name","description":"Container name"},"status":{"type":"string","title":"Status","description":"Container status"},"state":{"type":"string","title":"State","description":"State machine state","default":"provisioning"},"public_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Public Ip","description":"Container public IP"},"exec_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exec Url","description":"URL for executing commands"},"logs_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logs Url","description":"URL for streaming logs"},"session_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Token","description":"Session authentication token"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"Token expiration time"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"from_pool":{"type":"boolean","title":"From Pool","description":"Whether claimed from warm pool","default":false},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Status message"}},"type":"object","required":["container_id","ephemeral_type","status","created_at"],"title":"EphemeralResponse","description":"Response after creating ephemeral container","examples":[{"container_id":"eph_a1b2c3d4","created_at":"2026-01-31T10:30:00Z","ephemeral_type":"python","exec_url":"https://eph-a1b2c3d4.containers.example.com/exec","expires_at":"2026-01-31T12:30:00Z","from_pool":true,"logs_url":"https://eph-a1b2c3d4.containers.example.com/logs","message":"Container assigned from warm pool","name":"data-processor","public_ip":"10.0.1.50","session_token":"sess_xyz789","state":"assigned","status":"running"}]},"EphemeralType":{"type":"string","enum":["ubuntu","alpine","debian","python","node","go","rust","agent-python","agent-node","custom"],"title":"EphemeralType","description":"Supported ephemeral container types.\nThese are lightweight, fast-starting containers for general compute."},"ExecuteCommandRequest":{"properties":{"command":{"type":"string","title":"Command","description":"Command to execute in the sandbox"}},"type":"object","required":["command"],"title":"ExecuteCommandRequest","description":"Request to execute a command in a sandbox","examples":[{"command":"python train.py --epochs 10 --batch-size 32"}]},"ExecuteCommandResponse":{"properties":{"stdout":{"type":"string","title":"Stdout","description":"Standard output from command","default":""},"stderr":{"type":"string","title":"Stderr","description":"Standard error from command","default":""},"exit_code":{"type":"integer","title":"Exit Code","description":"Command exit code","default":0},"execution_time_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Execution Time Ms","description":"Execution time in milliseconds"},"executed_via":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Executed Via","description":"Path that served the command: manager | resource_service_ssm"},"manager_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Error","description":"Primary manager error when SSM fallback was used"}},"type":"object","title":"ExecuteCommandResponse","description":"Response from executing a command.\n\nexecuted_via: \"manager\" | \"resource_service_ssm\" — which path served the\n  command (frontend renders a Manager API / SSM Fallback badge).\nmanager_error: primary (manager) error string when SSM fallback was used.","examples":[{"execution_time_ms":4523,"exit_code":0,"stderr":"","stdout":"Training started...\nEpoch 1/10: loss=0.523\nEpoch 2/10: loss=0.412\n"}]},"FileDeleteResult":{"properties":{"message":{"type":"string","title":"Message","default":"File deleted"},"path":{"type":"string","title":"Path","description":"Workspace-relative path deleted"}},"type":"object","required":["path"],"title":"FileDeleteResult","description":"DELETE /api/computers/{id}/files — delete ack."},"FileEntry":{"properties":{"name":{"type":"string","title":"Name","description":"Entry name (no path components)"},"path":{"type":"string","title":"Path","description":"Workspace-relative path to this entry"},"type":{"type":"string","title":"Type","description":"\"file\" or \"dir\""},"size":{"type":"integer","title":"Size","description":"Size in bytes (0 for directories)","default":0},"modified":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modified","description":"ISO-8601 mtime (UTC)"},"permissions":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Permissions","description":"Unix mode string, e.g. -rw-r--r--"}},"type":"object","required":["name","path","type"],"title":"FileEntry","description":"One directory entry, normalized to the workspace-relative browser shape."},"FileListResponse":{"properties":{"path":{"type":"string","title":"Path","description":"Workspace-relative path that was listed","default":""},"entries":{"items":{"$ref":"#/components/schemas/FileEntry"},"type":"array","title":"Entries"},"count":{"type":"integer","title":"Count","default":0}},"type":"object","title":"FileListResponse","description":"GET /api/computers/{id}/files — a directory listing."},"FileUploadResult":{"properties":{"message":{"type":"string","title":"Message","default":"File saved"},"filename":{"type":"string","title":"Filename","description":"Workspace-relative saved path"},"size":{"type":"integer","title":"Size","description":"Bytes written","default":0},"checksum":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Checksum","description":"sha256:<hex> from the manager"}},"type":"object","required":["filename"],"title":"FileUploadResult","description":"POST /api/computers/{id}/files — upload ack (surfaces the manager checksum)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Service health status"},"service":{"type":"string","title":"Service","description":"Service name"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp","description":"Current timestamp"}},"type":"object","required":["status","service","timestamp"],"title":"HealthResponse","description":"Health check response","examples":[{"service":"sandbox-platform","status":"healthy","timestamp":"2026-01-31T10:30:00Z"}]},"HomeAccountingRunRequest":{"properties":{"org_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Org Id","description":"Org to account (defaults to the caller's org)"}},"type":"object","title":"HomeAccountingRunRequest"},"HomeAccountingRunResponse":{"properties":{"message":{"type":"string","title":"Message"},"summary":{"type":"object","title":"Summary"}},"type":"object","required":["message","summary"],"title":"HomeAccountingRunResponse"},"HomeStorageStatus":{"properties":{"computer_id":{"type":"string","title":"Computer Id"},"quota_gb":{"type":"integer","title":"Quota Gb"},"size_bytes":{"type":"integer","title":"Size Bytes"},"size_gb":{"type":"number","title":"Size Gb"},"size_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size Source"},"size_measured_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size Measured At"},"status":{"type":"string","title":"Status"},"gc_stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gc Stage"},"org_over_quota_read_only":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Org Over Quota Read Only","description":"True when the org storage gauge is over its tier limit — the NEXT attach mounts read-only (quota gates attach, never a running write). None = gauge unavailable."},"storage_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Storage State","description":"Degradation-ladder rung for the org gauge (SP-7): 'ok' | 'warn' (>=80%) | 'grace' (>=90%, last warning) | 'readonly' (over-quota — next attach mounts read-only). Warn/grace never block writes, reads, or export. None = gauge unavailable."},"pct_used":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pct Used","description":"Org gauge as a fraction of the tier limit (0.0-…; >1.0 when over-quota). None for unlimited tiers or an unavailable gauge."}},"type":"object","required":["computer_id","quota_gb","size_bytes","size_gb","status"],"title":"HomeStorageStatus","description":"One computer's persistent-home storage accounting view."},"InvoiceItem":{"properties":{"invoice_id":{"type":"string","title":"Invoice Id"},"invoice_number":{"type":"string","title":"Invoice Number"},"status":{"type":"string","title":"Status"},"subtotal":{"type":"string","title":"Subtotal"},"amount_due":{"type":"string","title":"Amount Due"},"amount_paid":{"type":"string","title":"Amount Paid"},"total_amount":{"type":"string","title":"Total Amount"},"currency":{"type":"string","title":"Currency"},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Url"}},"additionalProperties":true,"type":"object","required":["invoice_id","invoice_number","status","subtotal","amount_due","amount_paid","total_amount","currency"],"title":"InvoiceItem"},"InvoicesResponse":{"properties":{"invoices":{"items":{"$ref":"#/components/schemas/InvoiceItem"},"type":"array","title":"Invoices"},"count":{"type":"integer","title":"Count","default":0},"has_more":{"type":"boolean","title":"Has More","default":false}},"additionalProperties":true,"type":"object","title":"InvoicesResponse"},"ManagerStatusResponse":{"properties":{"healthy":{"type":"boolean","title":"Healthy","description":"Whether the manager is responding"},"status":{"type":"string","title":"Status","description":"Status: healthy, unreachable, auth_failed, timeout, not_configured, not_ready"},"message":{"type":"string","title":"Message","description":"Human-readable explanation"},"details":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Details","description":"Version, uptime, etc. (unstructured from manager agent)"},"last_fallback_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Fallback Reason","description":"Why the last command used SSM fallback"},"status_updated":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Updated","description":"Set if sandbox status was updated"},"manager_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manager Type","description":"Manager runtime this sandbox runs: 'python' | 'go' (migration 20260607)"}},"additionalProperties":true,"type":"object","required":["healthy","status","message"],"title":"ManagerStatusResponse","description":"Response from manager health check"},"MessageResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Response message"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Status if applicable"},"sandbox_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sandbox Id","description":"Sandbox ID if applicable"},"container_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Container Id","description":"Container ID if applicable"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Reason/details if applicable"}},"type":"object","required":["message"],"title":"MessageResponse","description":"Generic message response with optional context fields","examples":[{"message":"Operation completed successfully","sandbox_id":"sandbox_a1b2c3d4","status":"stopped"}]},"MetricsResponse":{"properties":{"cpu_utilization":{"type":"number","title":"Cpu Utilization","description":"CPU utilization percentage"},"memory_utilization":{"type":"number","title":"Memory Utilization","description":"Memory utilization percentage"},"disk_utilization":{"type":"number","title":"Disk Utilization","description":"Disk utilization percentage","default":0},"network_in":{"type":"number","title":"Network In","description":"Network bytes received","default":0},"network_out":{"type":"number","title":"Network Out","description":"Network bytes sent","default":0},"uptime_hours":{"type":"number","title":"Uptime Hours","description":"Sandbox uptime in hours"},"session_cost":{"type":"number","title":"Session Cost","description":"Current session cost in USD"}},"type":"object","required":["cpu_utilization","memory_utilization","uptime_hours","session_cost"],"title":"MetricsResponse","description":"Sandbox metrics response","examples":[{"cpu_utilization":45.2,"disk_utilization":23.1,"memory_utilization":62.8,"network_in":1048576,"network_out":524288,"session_cost":0.104,"uptime_hours":2.5}]},"OrgListResponse":{"properties":{"organizations":{"items":{"$ref":"#/components/schemas/OrgSummary"},"type":"array","title":"Organizations"},"total":{"type":"integer","title":"Total","default":0}},"type":"object","title":"OrgListResponse","description":"Response containing list of organizations"},"OrgStorageUsageResponse":{"properties":{"org_id":{"type":"string","title":"Org Id"},"current_gb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Gb"},"limit_gb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Limit Gb"},"over_quota":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Over Quota"},"storage_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Storage State","description":"Degradation-ladder rung for the org gauge (SP-7): 'ok' | 'warn' (>=80%) | 'grace' (>=90%) | 'readonly' (over-quota). Warn/grace never block writes, reads, or export. None = gauge unavailable."},"pct_used":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pct Used","description":"Org gauge as a fraction of the tier limit (0.0-…; >1.0 when over-quota). None for unlimited tiers or an unavailable gauge."},"homes":{"items":{"$ref":"#/components/schemas/HomeStorageStatus"},"type":"array","title":"Homes"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["org_id","homes","total"],"title":"OrgStorageUsageResponse","description":"The caller's storage picture: org gauge + the homes they may see."},"OrgSummary":{"properties":{"org_id":{"type":"string","title":"Org Id","default":""},"name":{"type":"string","title":"Name","default":""},"slug":{"type":"string","title":"Slug","default":""},"member_count":{"type":"integer","title":"Member Count","default":0},"sandbox_count":{"type":"integer","title":"Sandbox Count","default":0},"monthly_cost":{"type":"number","title":"Monthly Cost","default":0.0},"created_at":{"type":"string","title":"Created At","default":""}},"type":"object","title":"OrgSummary","description":"Summary of an organization"},"PaymentMethodDeleteResponse":{"properties":{"status":{"type":"string","title":"Status"},"deleted":{"type":"boolean","title":"Deleted"}},"additionalProperties":true,"type":"object","required":["status","deleted"],"title":"PaymentMethodDeleteResponse"},"PaymentMethodItem":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type","default":"card"},"last4":{"type":"string","title":"Last4","default":""},"brand":{"type":"string","title":"Brand","default":""},"exp_month":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp Month"},"exp_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp Year"},"is_default":{"type":"boolean","title":"Is Default","default":false},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"additionalProperties":true,"type":"object","required":["id"],"title":"PaymentMethodItem"},"PaymentMethodSetDefaultResponse":{"properties":{"id":{"type":"string","title":"Id"},"is_default":{"type":"boolean","title":"Is Default"},"message":{"type":"string","title":"Message"}},"additionalProperties":true,"type":"object","required":["id","is_default","message"],"title":"PaymentMethodSetDefaultResponse"},"PaymentMethodsResponse":{"properties":{"payment_methods":{"items":{"$ref":"#/components/schemas/PaymentMethodItem"},"type":"array","title":"Payment Methods"}},"additionalProperties":true,"type":"object","title":"PaymentMethodsResponse"},"PlanFeature":{"properties":{"feature_key":{"type":"string","title":"Feature Key","default":""},"type":{"type":"string","title":"Type","default":"boolean"},"display_name":{"type":"string","title":"Display Name","default":""},"value":{"anyOf":[{"type":"boolean"},{"type":"integer"},{"type":"number"},{"type":"string"},{"type":"null"}],"title":"Value","default":true}},"additionalProperties":true,"type":"object","title":"PlanFeature"},"PlanItem":{"properties":{"plan_id":{"type":"string","title":"Plan Id"},"name":{"type":"string","title":"Name","default":""},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"features":{"items":{"$ref":"#/components/schemas/PlanFeature"},"type":"array","title":"Features"},"prices":{"items":{"$ref":"#/components/schemas/PlanPrice"},"type":"array","title":"Prices"},"default_price":{"anyOf":[{"$ref":"#/components/schemas/PlanPrice"},{"type":"null"}]},"trial_period_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Trial Period Days"}},"additionalProperties":true,"type":"object","required":["plan_id"],"title":"PlanItem"},"PlanPrice":{"properties":{"price_id":{"type":"string","title":"Price Id"},"amount":{"type":"number","title":"Amount","default":0},"currency":{"type":"string","title":"Currency","default":"usd"},"type":{"type":"string","title":"Type","default":"recurring"},"interval":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Interval"},"interval_count":{"type":"integer","title":"Interval Count","default":1},"display_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Text"}},"additionalProperties":true,"type":"object","required":["price_id"],"title":"PlanPrice"},"PlansResponse":{"properties":{"plans":{"items":{"$ref":"#/components/schemas/PlanItem"},"type":"array","title":"Plans"},"count":{"type":"integer","title":"Count","default":0}},"additionalProperties":true,"type":"object","title":"PlansResponse"},"PlatformCapabilities":{"properties":{"persistence_enabled":{"type":"boolean","title":"Persistence Enabled","description":"True when files on a Computer actually survive Turn off here (fork-C stop-snapshot lit, or the EFS live-mount lit)"},"export_available":{"type":"boolean","title":"Export Available","description":"True when POST /api/computers/{id}/export can actually serve a download in this environment (snapshot layer lit)"}},"type":"object","required":["persistence_enabled","export_available"],"title":"PlatformCapabilities","description":"GET /api/capabilities — the LIVE capability flags the UI derives its promises\nfrom (road-to-100 U2/U4; PMM audit 20260711 N1). HONESTY CONTRACT: a capability\nis True ONLY when the feature is actually servable in THIS environment (flag AND\nits required config both present — the same fail-closed derivation the serving\npaths use). The page's persistence banner, delete-dialog copy and create-funnel\npitch are all driven by this response, never hardcoded."},"PoolInfo":{"properties":{"type":{"type":"string","title":"Type","description":"Container type: browser, desktop, ephemeral"},"subtype":{"type":"string","title":"Subtype","description":"Specific type within category"},"ready":{"type":"integer","title":"Ready","description":"Number of ready containers"},"target":{"type":"integer","title":"Target","description":"Target pool size"}},"type":"object","required":["type","subtype","ready","target"],"title":"PoolInfo","description":"Status of a single warm pool"},"PoolStatusResponse":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Whether warm pooling is enabled"},"pools":{"items":{"$ref":"#/components/schemas/PoolInfo"},"type":"array","title":"Pools","description":"Per-type pool status"}},"type":"object","required":["enabled"],"title":"PoolStatusResponse","description":"Warm pool status response"},"PortalSessionResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Stripe Customer Portal URL"},"id":{"type":"string","title":"Id","description":"Portal session ID"}},"additionalProperties":true,"type":"object","required":["url","id"],"title":"PortalSessionResponse"},"PricingProductRates":{"properties":{"display_name":{"type":"string","title":"Display Name","description":"Human-readable product name"},"description":{"type":"string","title":"Description","description":"Short marketing description","default":""},"display":{"type":"string","title":"Display","description":"Headline price string for the pricing widget","default":""},"variants":{"additionalProperties":{"$ref":"#/components/schemas/PricingVariantRate"},"type":"object","title":"Variants","description":"Variant name → variant rate"}},"additionalProperties":true,"type":"object","required":["display_name"],"title":"PricingProductRates","description":"Pricing for one product, keyed by variant name."},"PricingRatesResponse":{"properties":{"rates":{"additionalProperties":{"$ref":"#/components/schemas/PricingProductRates"},"type":"object","title":"Rates","description":"Product id → pricing payload"},"currency":{"type":"string","title":"Currency","description":"ISO currency code","default":"USD"},"billing_model":{"type":"string","title":"Billing Model","description":"How usage is billed: per_minute, per_hour, etc.","default":"per_minute"},"refund_on_stop":{"type":"boolean","title":"Refund On Stop","description":"True if unused minutes are refunded when the user stops a sandbox early","default":true},"surge":{"allOf":[{"$ref":"#/components/schemas/PricingSurgeConfig"}],"description":"Active surge-pricing multipliers, if any"}},"additionalProperties":true,"type":"object","title":"PricingRatesResponse","description":"Public response for `GET /api/pricing/rates`.\n\nCustomer-facing prices only — internal cost figures are never surfaced\nhere. Values come from quota-config.json (single source of truth)."},"PricingSurgeConfig":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Whether surge pricing is currently active","default":false},"multiplier":{"type":"number","title":"Multiplier","description":"Multiplier applied to base rates when surge is active","default":1.0},"description":{"type":"string","title":"Description","description":"Operator-facing reason surge is in effect","default":""}},"additionalProperties":true,"type":"object","title":"PricingSurgeConfig","description":"Surge-pricing configuration. Empty/disabled by default."},"PricingVariantRate":{"properties":{"price_per_hour":{"type":"string","title":"Price Per Hour","description":"Customer-facing hourly rate, USD"},"allocation_price":{"type":"string","title":"Allocation Price","description":"Reservation/allocation fee, USD","default":"0"},"compute":{"type":"string","title":"Compute","description":"Compute class: cpu, gpu-l4, gpu-a100, etc.","default":"unknown"}},"additionalProperties":true,"type":"object","required":["price_per_hour"],"title":"PricingVariantRate","description":"One billable variant for a product (e.g. CPU vs GPU sandbox)."},"QuotaDecision":{"type":"string","enum":["allow","allow_warning","deny","unlimited","shadow_allow","unknown_tier"],"title":"QuotaDecision","description":"Outcome of a quota check."},"QuotaRecalculateResponse":{"properties":{"org_id":{"type":"string","title":"Org Id","description":"Org whose counters were reconciled"},"resources":{"additionalProperties":{"$ref":"#/components/schemas/QuotaResourceReconcileEntry"},"type":"object","title":"Resources","description":"resource_key → before/after recompute result"},"reconciled_by":{"type":"string","title":"Reconciled By","description":"user_id of the caller who triggered the recalculation"},"scope":{"type":"string","title":"Scope","description":"'self' (caller's own org) | 'admin' (any org, gated on sandbox.admin)"}},"additionalProperties":true,"type":"object","required":["org_id","reconciled_by","scope"],"title":"QuotaRecalculateResponse","description":"Response for `POST /api/quotas/recalculate` — the 'Recalculate usage'\nbutton. Structured before->after per resource, plus who ran it and the\nscope. The operation is idempotent and only ever sets counters to derived\ntruth."},"QuotaResourceReconcileEntry":{"properties":{"counter_type":{"type":"string","title":"Counter Type","description":"gauge | accumulator | rate"},"before":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Before","description":"Cached counter value before the recompute"},"after":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"After","description":"Value the counter was set to (the derived truth)"},"changed":{"type":"boolean","title":"Changed","description":"True if the counter was actually repaired","default":false},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","description":"'provider' (gauge live count) | 'ledger' (accumulator re-sum) | null"},"status":{"type":"string","title":"Status","description":"repaired | in_sync | skipped_rate | no_truth_source | no_observation | truth_unavailable","default":""}},"additionalProperties":true,"type":"object","required":["counter_type"],"title":"QuotaResourceReconcileEntry","description":"One resource's before->after recompute result from `reconcile_org`\n(ticket 20260810_billing_resilience_and_erroneous_tier_downgrade P4.1).\n\nMirrors the library's `QuotaEngine.reconcile_org` per-resource dict. The\ncounter is only ever force-set to the derived truth (a live gauge count or\na durable period-ledger re-sum), never an arbitrary value."},"QuotaResult":{"properties":{"decision":{"$ref":"#/components/schemas/QuotaDecision"},"resource_key":{"type":"string","title":"Resource Key"},"current":{"type":"number","title":"Current","description":"Current usage before this request"},"requested":{"type":"number","title":"Requested","description":"Amount requested"},"limit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Limit","description":"Effective limit (None = unlimited)"},"tier_id":{"type":"string","title":"Tier Id"},"tier_display":{"type":"string","title":"Tier Display","description":"Human-readable tier name"},"has_override":{"type":"boolean","title":"Has Override","default":false},"severity":{"allOf":[{"$ref":"#/components/schemas/AlertSeverity"}],"default":"info"},"message":{"type":"string","title":"Message","description":"Human-readable explanation"},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url"},"retry_after":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Retry After","description":"Seconds to wait before retrying (for RATE limits)"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"User checked (if per-user)"},"user_current":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"User Current","description":"User's current usage"},"user_limit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"User Limit","description":"User's per-user limit"},"denied_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Denied Level","description":"Which level caused denial: 'org' or 'user' (None if allowed)"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Machine-readable reason code for enforcement outcomes (e.g. 'global_kill_switch', 'enforcement_disabled', 'shadow_would_deny', 'tier_not_in_config'). None for normal results."},"remaining":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Remaining","description":"How much headroom remains after this request would be applied.","readOnly":true},"utilization":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Utilization","description":"Current usage as fraction of limit (before this request).","readOnly":true}},"type":"object","required":["decision","resource_key","current","requested","limit","tier_id","tier_display","message","remaining","utilization"],"title":"QuotaResult","description":"Result of a single quota check.\n\nDesigned to be returned directly as a 429 response body when denied,\nor logged when allowed with warnings.\n\nUsage:\n    result = await engine.check(request)\n    if result.denied:\n        raise HTTPException(status_code=429, detail=result.to_api_error())\n    if result.warning:\n        logger.warning(\"quota_warning\", **result.model_dump())"},"QuotaTierInfo":{"properties":{"tier_id":{"type":"string","title":"Tier Id","description":"Stable tier identifier"},"display_name":{"type":"string","title":"Display Name","description":"Human-readable tier name (e.g. 'Pro')"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Marketing description"},"features":{"items":{"type":"string"},"type":"array","title":"Features","description":"Feature bullet list"},"limits":{"additionalProperties":{"$ref":"#/components/schemas/QuotaTierLimitInfo"},"type":"object","title":"Limits","description":"resource_key → limit info"},"upgrade_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Url","description":"Optional upgrade landing page URL"}},"additionalProperties":true,"type":"object","required":["tier_id","display_name"],"title":"QuotaTierInfo","description":"One tier definition exposed to the pricing/comparison page."},"QuotaTierLimitInfo":{"properties":{"limit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Limit","description":"Numeric limit (None means unlimited for this resource)"},"limit_display":{"type":"string","title":"Limit Display","description":"Human-readable limit (e.g. '10' or 'Unlimited')","default":""}},"additionalProperties":true,"type":"object","title":"QuotaTierLimitInfo","description":"One resource limit within a tier."},"QuotaTiersResponse":{"properties":{"tiers":{"items":{"$ref":"#/components/schemas/QuotaTierInfo"},"type":"array","title":"Tiers","description":"Tiers in display order"}},"additionalProperties":true,"type":"object","title":"QuotaTiersResponse","description":"Public response for `GET /api/quotas/tiers` — pricing-page input."},"QuotaUsageItem":{"properties":{"resource_key":{"type":"string","title":"Resource Key"},"display_name":{"type":"string","title":"Display Name"},"unit":{"type":"string","title":"Unit"},"current":{"type":"number","title":"Current"},"limit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Limit"},"utilization":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Utilization","description":"0.0–1.0+, None if unlimited"},"severity":{"$ref":"#/components/schemas/AlertSeverity"},"has_override":{"type":"boolean","title":"Has Override","default":false},"counter_type":{"type":"string","title":"Counter Type"}},"type":"object","required":["resource_key","display_name","unit","current","limit","utilization","severity","counter_type"],"title":"QuotaUsageItem","description":"Usage for a single resource — used in dashboard views."},"QuotaUsageResponse":{"properties":{"org_id":{"type":"string","title":"Org Id"},"tier_id":{"type":"string","title":"Tier Id"},"tier_display":{"type":"string","title":"Tier Display"},"resources":{"items":{"$ref":"#/components/schemas/QuotaUsageItem"},"type":"array","title":"Resources"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"warnings_count":{"type":"integer","title":"Warnings Count","readOnly":true},"exceeded_count":{"type":"integer","title":"Exceeded Count","readOnly":true}},"type":"object","required":["org_id","tier_id","tier_display","resources","warnings_count","exceeded_count"],"title":"QuotaUsageResponse","description":"Full usage report for an org — all resources with limits."},"S3SyncConfigRequest":{"properties":{"bucket":{"type":"string","maxLength":255,"minLength":1,"title":"Bucket"},"region":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Region"},"prefix":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Prefix"},"sse":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Sse"},"access_key_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Access Key Id"},"secret_access_key":{"anyOf":[{"type":"string","maxLength":256,"minLength":8},{"type":"null"}],"title":"Secret Access Key"}},"type":"object","required":["bucket"],"title":"S3SyncConfigRequest","description":"PUT /api/computers/{id}/sync/s3 body. `access_key_id`/`secret_access_key`\nare write-only — supplied to set/replace the off-band credential, never read\nback. Omit them on edit to keep the stored credential unchanged."},"S3SyncConfigResponse":{"properties":{"configured":{"type":"boolean","title":"Configured","description":"True once a bucket has been registered"},"bucket":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bucket"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix"},"sse":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sse"},"has_credentials":{"type":"boolean","title":"Has Credentials","description":"True when a secret access key is stored off-band. The key itself is NEVER returned.","default":false},"last_run_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Run At"},"last_run_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Run Status"}},"type":"object","required":["configured"],"title":"S3SyncConfigResponse","description":"GET/PUT /api/computers/{id}/sync/s3 — the CLIENT-VISIBLE view of a customer's\nS3 sync config. Credentials are WRITE-ONLY: they are never a field here. The\nonly signal that keys are set is `has_credentials` (SP-3 honesty contract)."},"SSHAccessInfo":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Whether SSH access is enabled"},"command":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Command","description":"SSH command to connect"},"host":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Host","description":"SSH hostname or IP"},"key_fingerprints":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Key Fingerprints","description":"Authorized key fingerprints"}},"additionalProperties":true,"type":"object","required":["enabled"],"title":"SSHAccessInfo","description":"SSH access information for a sandbox"},"SSHCAPublicKeyResponse":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Id"},"algorithm":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Algorithm"},"ca_public_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ca Public Key"},"fingerprint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fingerprint"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["enabled"],"title":"SSHCAPublicKeyResponse","description":"SSH CA public key metadata."},"SSHCertificateIssueRequest":{"properties":{"allocation_id":{"type":"string","maxLength":255,"minLength":1,"title":"Allocation Id"},"instance_id":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Instance Id"},"public_key":{"type":"string","maxLength":10000,"minLength":20,"title":"Public Key"},"requested_username":{"anyOf":[{"type":"string","maxLength":32,"minLength":1},{"type":"null"}],"title":"Requested Username"},"ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":60.0},{"type":"null"}],"title":"Ttl Seconds"}},"type":"object","required":["allocation_id","public_key"],"title":"SSHCertificateIssueRequest","description":"Request to issue a short-lived SSH certificate."},"SSHCertificateIssueResponse":{"properties":{"certificate":{"type":"string","title":"Certificate"},"valid_before":{"type":"string","title":"Valid Before"},"principals":{"items":{"type":"string"},"type":"array","title":"Principals"},"key_id":{"type":"string","title":"Key Id"},"audit_id":{"type":"string","title":"Audit Id"},"ssh_command_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ssh Command Hint"}},"type":"object","required":["certificate","valid_before","principals","key_id","audit_id"],"title":"SSHCertificateIssueResponse","description":"Issued SSH certificate payload."},"SSHKeyGenerateRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Key name"},"key_type":{"type":"string","title":"Key Type","description":"Key type: 'ed25519' (recommended) or 'rsa'","default":"ed25519"}},"type":"object","required":["name"],"title":"SSHKeyGenerateRequest","description":"Request to generate a new SSH keypair"},"SSHKeyGenerateResponse":{"properties":{"key_id":{"type":"string","title":"Key Id","description":"Key ID"},"name":{"type":"string","title":"Name","description":"Key name"},"public_key":{"type":"string","title":"Public Key","description":"Public key content"},"private_key_pem":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Private Key Pem","description":"Private key PEM (sensitive — save immediately)"},"private_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Private Key","description":"Alias of private_key_pem for older clients"},"fingerprint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fingerprint","description":"Key fingerprint"},"key_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Type","description":"Key type (ed25519/rsa)"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"Creation timestamp"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"Active flag","default":true},"warning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warning","description":"Operator warning text from upstream"}},"additionalProperties":true,"type":"object","required":["key_id","name","public_key"],"title":"SSHKeyGenerateResponse","description":"Response after generating an SSH keypair (proxied from resource service).\n\nUpstream returns the PEM as `private_key_pem`; we accept either field\nname and surface both for caller convenience. The dashboard's\n`pendingPemData` reads `private_key_pem`."},"SSHKeyResponse":{"properties":{"key_id":{"type":"string","title":"Key Id"},"name":{"type":"string","title":"Name"},"fingerprint":{"type":"string","title":"Fingerprint"},"key_type":{"type":"string","title":"Key Type"},"created_at":{"type":"string","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"is_active":{"type":"boolean","title":"Is Active","default":true},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},"type":"object","required":["key_id","name","fingerprint","key_type","created_at"],"title":"SSHKeyResponse","description":"SSH key summary"},"SSHKeyUpdateRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"}},"type":"object","required":["name"],"title":"SSHKeyUpdateRequest","description":"Request to update an SSH key name"},"SSHKeyUploadRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Key name"},"public_key":{"type":"string","minLength":20,"title":"Public Key","description":"SSH public key (ssh-rsa, ssh-ed25519, etc.)"}},"type":"object","required":["name","public_key"],"title":"SSHKeyUploadRequest","description":"Request to upload an SSH public key"},"SandboxListResponse":{"properties":{"sandboxes":{"items":{"$ref":"#/components/schemas/SandboxSummary"},"type":"array","title":"Sandboxes","description":"List of sandboxes"},"total":{"type":"integer","title":"Total","description":"Total count"}},"type":"object","required":["sandboxes","total"],"title":"SandboxListResponse","description":"Response containing list of sandboxes","examples":[{"sandboxes":[{"instance_tier":"ab0t.medium","instance_type":"t3.medium","name":"ml-training","sandbox_id":"sandbox_a1b2c3d4","status":"running"},{"instance_tier":"ab0t.micro","instance_type":"t3.micro","name":"dev-env","sandbox_id":"sandbox_e5f6g7h8","status":"stopped"}],"total":2}]},"SandboxRequest":{"properties":{"name":{"type":"string","title":"Name","description":"Sandbox name"},"instance_type":{"type":"string","title":"Instance Type","description":"Sandbox instance selection. Accepts branded tiers like ab0t.small or canonical runtime types like t3.small.","default":"ab0t.micro"},"docker_image":{"type":"string","title":"Docker Image","description":"Docker image to run","default":"ubuntu:latest"},"environment":{"additionalProperties":{"type":"string"},"type":"object","title":"Environment","description":"Environment variables"},"metadata":{"type":"object","title":"Metadata","description":"Additional metadata"},"auto_stop_minutes":{"anyOf":[{"type":"integer","maximum":480.0,"minimum":5.0},{"type":"null"}],"title":"Auto Stop Minutes","description":"Auto-stop after this many minutes idle. Null = never auto-stop (manual)."}},"type":"object","required":["name"],"title":"SandboxRequest","description":"Request to create a sandbox","examples":[{"auto_stop_minutes":30,"docker_image":"python:3.11-slim","environment":{"MODEL_NAME":"gpt-classifier","PYTHONUNBUFFERED":"1"},"instance_type":"ab0t.medium","metadata":{"project":"nlp-pipeline","team":"data-science"},"name":"ml-training-sandbox"},{"docker_image":"ubuntu:latest","environment":{},"instance_type":"t3.medium","metadata":{"project":"sdk-example"},"name":"agent-sandbox"}]},"SandboxResponse":{"properties":{"sandbox_id":{"type":"string","title":"Sandbox Id","description":"Unique sandbox identifier"},"name":{"type":"string","title":"Name","description":"Human-readable sandbox name (echoed from request)"},"instance_type":{"type":"string","title":"Instance Type","description":"Canonical provider/runtime instance type used for execution"},"instance_tier":{"type":"string","title":"Instance Tier","description":"Branded sandbox tier used for product display"},"allocation_id":{"type":"string","title":"Allocation Id","description":"Resource allocation ID"},"status":{"type":"string","title":"Status","description":"Current status (pending, running, stopped)"},"instance_ip":{"type":"string","title":"Instance Ip","description":"EC2 instance IP address"},"api_endpoint":{"type":"string","title":"Api Endpoint","description":"Manager API endpoint URL"},"connection_token":{"type":"string","title":"Connection Token","description":"Token for authenticating with manager"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"hourly_cost":{"type":"string","title":"Hourly Cost","description":"Hourly cost in USD"},"message":{"type":"string","title":"Message","description":"Status message"},"auto_stop_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Auto Stop Minutes","description":"Idle minutes before the sandbox is auto-stopped. Null = manual stop only."},"manager_type":{"type":"string","title":"Manager Type","description":"Manager runtime the sandbox runs: 'python' | 'go'","default":"python"}},"type":"object","required":["sandbox_id","name","instance_type","instance_tier","allocation_id","status","instance_ip","api_endpoint","connection_token","created_at","hourly_cost","message"],"title":"SandboxResponse","description":"Response after creating sandbox","examples":[{"allocation_id":"ec2_x9y8z7w6","api_endpoint":"http://172.31.40.119:8000","auto_stop_minutes":30,"connection_token":"mgr_tok_abc123xyz","created_at":"2026-01-31T10:30:00Z","hourly_cost":"0.0416","instance_ip":"172.31.40.119","instance_tier":"ab0t.medium","instance_type":"t3.medium","message":"Sandbox created successfully","name":"ml-training-sandbox","sandbox_id":"sandbox_a1b2c3d4","status":"running"}]},"SandboxSSHCertificateIssueRequest":{"properties":{"instance_id":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Instance Id"},"public_key":{"type":"string","maxLength":10000,"minLength":20,"title":"Public Key"},"requested_username":{"anyOf":[{"type":"string","maxLength":32,"minLength":1},{"type":"null"}],"title":"Requested Username"},"ttl_seconds":{"anyOf":[{"type":"integer","maximum":86400.0,"minimum":60.0},{"type":"null"}],"title":"Ttl Seconds"}},"type":"object","required":["public_key"],"title":"SandboxSSHCertificateIssueRequest","description":"Sandbox-scoped cert request (allocation resolved server-side)."},"SandboxSummary":{"properties":{"sandbox_id":{"type":"string","title":"Sandbox Id","description":"Unique sandbox identifier"},"name":{"type":"string","title":"Name","description":"Sandbox name"},"status":{"type":"string","title":"Status","description":"Current status"},"instance_type":{"type":"string","title":"Instance Type","description":"Canonical provider/runtime instance type"},"instance_tier":{"type":"string","title":"Instance Tier","description":"Branded sandbox tier for product display"},"instance_ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instance Ip","description":"Instance IP if running"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp"},"hourly_cost":{"type":"string","title":"Hourly Cost","description":"Hourly cost in USD"},"current_cost":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Cost","description":"Accumulated cost"},"scaling_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scaling Type","description":"Scaling type: manual or auto"},"idle_timeout_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Idle Timeout Minutes","description":"Minutes before auto-stop when idle (null if manual)"},"ssh_access":{"anyOf":[{"$ref":"#/components/schemas/SSHAccessInfo"},{"type":"null"}],"description":"SSH access info"},"manager_type":{"type":"string","title":"Manager Type","description":"Manager runtime the sandbox was provisioned with: 'python' | 'go'","default":"python"},"counts_against_quota":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Counts Against Quota","description":"True iff this row currently holds a quota slot (status in pending/provisioning/running). Terminal states (stopped/error/deleted/terminated) do NOT count."},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active","description":"True iff the sandbox is in an active (non-terminal) state. The default view shows only active rows; the advanced view (?include=all) also surfaces terminal ones."}},"type":"object","required":["sandbox_id","name","status","instance_type","instance_tier","created_at","hourly_cost"],"title":"SandboxSummary","description":"Summary of a sandbox for list views","examples":[{"counts_against_quota":true,"created_at":"2026-01-31T10:30:00Z","current_cost":"0.52","hourly_cost":"0.0416","instance_ip":"172.31.40.119","instance_tier":"ab0t.medium","instance_type":"t3.medium","is_active":true,"name":"ml-training","sandbox_id":"sandbox_a1b2c3d4","scaling_type":"manual","status":"running"}]},"SaveFileRequest":{"properties":{"filename":{"type":"string","maxLength":512,"minLength":1,"title":"Filename","description":"Relative path/filename under /workspace"},"content":{"type":"string","maxLength":1000000,"title":"Content","description":"UTF-8 file content (max 1 MB)"}},"type":"object","required":["filename","content"],"title":"SaveFileRequest","description":"Request to save a file to a sandbox","examples":[{"content":"{\"model\": \"gpt-4\", \"temperature\": 0.7}","filename":"config.json"}]},"SaveFileResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"filename":{"type":"string","title":"Filename","description":"Saved filename"}},"type":"object","required":["message","filename"],"title":"SaveFileResponse","description":"Response after saving a file","examples":[{"filename":"config.json","message":"File saved"}]},"ScheduleCreateRequest":{"properties":{"template_id":{"type":"string","maxLength":64,"minLength":1,"title":"Template Id"},"name":{"type":"string","maxLength":120,"title":"Name","default":""},"description":{"type":"string","maxLength":500,"title":"Description","default":""},"sandbox_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Sandbox Id"},"sandbox_ids":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Sandbox Ids"},"inputs":{"type":"object","title":"Inputs"},"cron":{"type":"string","maxLength":120,"minLength":1,"title":"Cron"},"timezone":{"type":"string","maxLength":64,"title":"Timezone","default":"UTC"},"enabled":{"type":"boolean","title":"Enabled","default":true}},"additionalProperties":false,"type":"object","required":["template_id","cron"],"title":"ScheduleCreateRequest","description":"POST /api/cogs/schedules body. `user_id`/`org_id` are server-assigned from\nthe caller — never the body (extra=forbid → an unknown field is a 422)."},"SetCostLimitRequest":{"properties":{"monthly_limit":{"type":"number","exclusiveMinimum":0.0,"title":"Monthly Limit","description":"Monthly cost limit in dollars"}},"type":"object","required":["monthly_limit"],"title":"SetCostLimitRequest","description":"Request to set a user's cost limit","examples":[{"monthly_limit":50.0}]},"SetCostLimitResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"user_id":{"type":"string","title":"User Id","description":"User ID"},"limit":{"type":"number","title":"Limit","description":"New monthly limit"}},"type":"object","required":["message","user_id","limit"],"title":"SetCostLimitResponse","description":"Response after setting cost limit","examples":[{"limit":50.0,"message":"Cost limit updated","user_id":"user_abc123"}]},"StopIdleResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Success message"},"stopped_count":{"type":"integer","title":"Stopped Count","description":"Number of sandboxes stopped"},"stopped_ids":{"items":{"type":"string"},"type":"array","title":"Stopped Ids","description":"IDs of stopped sandboxes"}},"type":"object","required":["message","stopped_count","stopped_ids"],"title":"StopIdleResponse","description":"Response after stopping idle sandboxes","examples":[{"message":"Stopped 3 idle sandboxes","stopped_count":3,"stopped_ids":["sandbox_a1b2c3d4","sandbox_e5f6g7h8","sandbox_i9j0k1l2"]}]},"SubscriptionItem":{"properties":{"subscription_id":{"type":"string","title":"Subscription Id","description":"Unique subscription ID"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id","description":"Alias"},"org_id":{"type":"string","title":"Org Id","description":"Organization ID"},"plan_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan Id"},"price_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Id"},"status":{"type":"string","title":"Status","description":"active, canceled, past_due, etc."},"amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount"},"customer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Email"},"current_period_start":{"type":"string","title":"Current Period Start"},"current_period_end":{"type":"string","title":"Current Period End"},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End","default":false},"canceled_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canceled At"},"ended_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ended At"},"trial_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trial End"},"next_billing_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Billing Date"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"}},"additionalProperties":true,"type":"object","required":["subscription_id","org_id","status","current_period_start","current_period_end","created_at"],"title":"SubscriptionItem"},"SubscriptionsResponse":{"properties":{"subscriptions":{"items":{"$ref":"#/components/schemas/SubscriptionItem"},"type":"array","title":"Subscriptions"},"total":{"type":"integer","title":"Total","default":0},"has_more":{"type":"boolean","title":"Has More","default":false}},"additionalProperties":true,"type":"object","title":"SubscriptionsResponse"},"SupportTicketCreateResponse":{"properties":{"ticket_id":{"type":"string","title":"Ticket Id","description":"Unique ticket identifier"},"status":{"type":"string","title":"Status","description":"Ticket status"},"created_at":{"type":"string","title":"Created At","description":"Creation timestamp"}},"type":"object","required":["ticket_id","status","created_at"],"title":"SupportTicketCreateResponse","description":"Response after creating a support ticket"},"SupportTicketListResponse":{"properties":{"tickets":{"items":{"$ref":"#/components/schemas/SupportTicketSummary"},"type":"array","title":"Tickets","description":"List of tickets"}},"type":"object","required":["tickets"],"title":"SupportTicketListResponse","description":"Response containing list of support tickets"},"SupportTicketRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"email":{"type":"string","maxLength":320,"minLength":3,"pattern":"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$","title":"Email"},"category":{"type":"string","pattern":"^(technical|billing|account|feature|other)$","title":"Category"},"message":{"type":"string","maxLength":5000,"minLength":10,"title":"Message"}},"type":"object","required":["name","email","category","message"],"title":"SupportTicketRequest","description":"Request to create a support ticket"},"SupportTicketSummary":{"properties":{"ticket_id":{"type":"string","title":"Ticket Id","description":"Unique ticket identifier"},"category":{"type":"string","title":"Category","description":"Ticket category"},"status":{"type":"string","title":"Status","description":"Ticket status"},"created_at":{"type":"string","title":"Created At","description":"Creation timestamp"},"message":{"type":"string","title":"Message","description":"Truncated message preview"}},"type":"object","required":["ticket_id","category","status","created_at","message"],"title":"SupportTicketSummary","description":"Summary of a support ticket for list views"},"SuspendUserRequest":{"properties":{"reason":{"type":"string","maxLength":500,"title":"Reason","default":"Suspended by platform admin"}},"type":"object","title":"SuspendUserRequest","description":"Request to suspend a user"},"SuspendUserResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"action":{"type":"string","title":"Action"},"stopped_sandboxes":{"type":"integer","title":"Stopped Sandboxes","default":0},"stopped_containers":{"type":"integer","title":"Stopped Containers","default":0}},"type":"object","required":["user_id","action"],"title":"SuspendUserResponse","description":"Response after suspending/unsuspending a user"},"TeamDeployRequest":{"properties":{"sandbox_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Sandbox Id"},"member_inputs":{"additionalProperties":{"type":"object"},"type":"object","title":"Member Inputs"}},"additionalProperties":false,"type":"object","title":"TeamDeployRequest","description":"POST /api/teams/{id}/deploy body. `sandbox_id` names the box the team stands up on\n(all members + the coordd authority share ONE box → one coordd space). `member_inputs`\ncarries the out-of-band buyer keys per member name (MODEL_API_KEY, TELEGRAM_BOT_TOKEN)\nfor a LIVE deploy — in flight only, never persisted. Both optional: without them (or\nwith the live gate off) the deploy records intent honestly."},"TeamMember":{"properties":{"name":{"type":"string","maxLength":31,"minLength":2,"pattern":"^[a-z][a-z0-9-]{1,30}$","title":"Name","description":"The member's handle — also the derived claws AGENT_NAME (lowercase, digits, dashes)"},"role":{"type":"string","enum":["manager","worker"],"title":"Role","default":"worker"},"template":{"type":"string","maxLength":80,"title":"Template","description":"The claws profile/persona template for this member","default":"solo/personal-assistant"},"channel":{"type":"string","maxLength":40,"title":"Channel","default":"telegram"},"model_provider":{"type":"string","maxLength":40,"title":"Model Provider","default":"anthropic"},"install_ref":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Install Ref","description":"The claw install id this member became at deploy (blank until deployed)"},"install_status":{"type":"string","maxLength":40,"title":"Install Status","default":""}},"additionalProperties":false,"type":"object","required":["name"],"title":"TeamMember","description":"One member of a claws team — a claw with a role. `name` + `role` are the two\nload-bearing fields; the rest default to the shipped claw's own defaults. Carries\nNO secret (the model key + channel token are delivered out-of-band at deploy, never\nstored). `install_ref`/`install_status` are STAMPED at deploy (the claw install this\nmember became); blank until then."},"TeamSaveRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"description":{"type":"string","maxLength":500,"title":"Description","default":""},"owner_scope":{"type":"string","enum":["user","org"],"title":"Owner Scope","default":"user"},"coordination":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Coordination"},"members":{"items":{"$ref":"#/components/schemas/TeamMember"},"type":"array","maxItems":25,"title":"Members"}},"additionalProperties":false,"type":"object","required":["name"],"title":"TeamSaveRequest","description":"POST/PUT /api/teams body. `user_id`/`org_id` are server-assigned from the caller\n— NEVER read from the body (extra=forbid → an unknown field is a 422). `deployment`\nis server-owned (recorded by /deploy), never accepted here."},"UserCostSummary":{"properties":{"user_id":{"type":"string","title":"User Id","description":"Unique user identifier"},"email":{"type":"string","title":"Email","description":"User email address"},"name":{"type":"string","title":"Name","description":"User display name"},"org_id":{"type":"string","title":"Org Id","description":"Organization ID"},"is_admin":{"type":"boolean","title":"Is Admin","description":"Whether user has admin privileges","default":false},"cost_limit":{"type":"string","title":"Cost Limit","description":"Monthly cost limit in USD"},"current_cost":{"type":"string","title":"Current Cost","description":"Current month cost in USD"},"active_sandboxes":{"type":"integer","title":"Active Sandboxes","description":"Number of running sandboxes"},"total_sandboxes":{"type":"integer","title":"Total Sandboxes","description":"Total sandbox count"},"total_cost":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Total Cost","description":"Alias for current_cost"},"sandbox_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sandbox Count","description":"Alias for total_sandboxes"},"usage_percentage":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Usage Percentage","description":"Cost usage percentage of limit"}},"type":"object","required":["user_id","email","name","org_id","cost_limit","current_cost","active_sandboxes","total_sandboxes"],"title":"UserCostSummary","description":"User with cost information for admin views","examples":[{"active_sandboxes":2,"cost_limit":"100.00","current_cost":"45.67","email":"developer@example.com","is_admin":false,"name":"John Developer","org_id":"org_xyz789","sandbox_count":5,"total_cost":"45.67","total_sandboxes":5,"usage_percentage":45.67,"user_id":"user_abc123"}]},"UserListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/UserCostSummary"},"type":"array","title":"Users","description":"List of users with cost info"},"total":{"type":"integer","title":"Total","description":"Total user count"}},"type":"object","required":["users","total"],"title":"UserListResponse","description":"Response containing list of users with cost information","examples":[{"total":2,"users":[{"active_sandboxes":2,"cost_limit":"100.00","current_cost":"45.67","email":"developer@example.com","is_admin":false,"name":"John Developer","org_id":"org_xyz789","total_sandboxes":5,"user_id":"user_abc123"},{"active_sandboxes":4,"cost_limit":"500.00","current_cost":"123.45","email":"admin@example.com","is_admin":true,"name":"Jane Admin","org_id":"org_xyz789","total_sandboxes":12,"user_id":"user_def456"}]}]},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WelcomeOfferStatusResponse":{"properties":{"offer_key":{"type":"string","title":"Offer Key","description":"Stable key for the welcome-offer campaign"},"acknowledged":{"type":"boolean","title":"Acknowledged","description":"Whether the user has permanently claimed the offer"},"should_show":{"type":"boolean","title":"Should Show","description":"Server-computed: whether to show the wizard now","default":false},"amount":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Amount","description":"Display amount of the free credit (e.g. '$10')"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Why should_show is true/false: claimed|snoozed|paid|unverified|no_grant_configured|tier_unknown|eligible"}},"type":"object","required":["offer_key","acknowledged"],"title":"WelcomeOfferStatusResponse","description":"Durable claim state + server-computed visibility for the dashboard welcome offer.\n\n`acknowledged` is retained for back-compat (true iff the offer was permanently claimed).\n`should_show` is the new server-authoritative gate the frontend should trust."},"WorkflowCancelResponse":{"properties":{"execution_id":{"type":"string","title":"Execution Id"},"status":{"type":"string","title":"Status","default":"cancelled"}},"type":"object","required":["execution_id"],"title":"WorkflowCancelResponse","description":"POST /workflow/executions/{id}/cancel."},"WorkflowExecuteRequest":{"properties":{"workflow":{"type":"object","title":"Workflow","description":"Raw manager WorkflowDefinition: {name, steps:[{type, name, ...}]}"},"variables":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Variables","description":"Non-secret ${VAR} substitutions forwarded to the manager"},"async_execution":{"type":"boolean","title":"Async Execution","description":"If true (default) the manager returns immediately; poll executions/{id}","default":true},"timeout":{"anyOf":[{"type":"integer","maximum":7200.0,"minimum":1.0},{"type":"null"}],"title":"Timeout","description":"Per-workflow timeout in seconds (manager-enforced)"},"scrub_values":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":64},{"type":"null"}],"title":"Scrub Values","description":"Exact secret strings to redact from proxied logs/status (values only)"}},"type":"object","required":["workflow"],"title":"WorkflowExecuteRequest","description":"Dispatch a workflow definition to a sandbox's manager /workflow/execute.\n\n`workflow` is the raw manager `Definition` ({name, steps:[...]}) and is\nforwarded unchanged. `variables` are the manager's `${VAR}` substitutions.\n\n`scrub_values` names the EXACT secret strings the caller wants redacted from\nany proxied logs/status before the platform persists or echoes them\n(platform-side backstop for the P0 secrets-in-logs finding, control (c)).\nValues only — never keys — appear here, and they are used only to redact the\nRESPONSE; they are never logged and never returned to the client. This is a\nbackstop for KNOWN values, NOT a substitute for the manager-side redaction\n(L9) that step-env user secrets require.","examples":[{"async_execution":true,"variables":{"WHO":"world"},"workflow":{"name":"hello","steps":[{"name":"greet","script_config":{"content":"echo hello ${WHO}"},"type":"script"}]}}]},"WorkflowExecuteResponse":{"properties":{"execution_id":{"type":"string","title":"Execution Id","description":"Manager-assigned execution id"},"workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workflow Id"},"status":{"type":"string","title":"Status","description":"pending | running | completed | failed | cancelled"},"result":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result"}},"type":"object","required":["execution_id","status"],"title":"WorkflowExecuteResponse","description":"Synchronous ack from POST /workflow/execute (manager ExecuteResponse)."},"WorkflowExecutionStatusResponse":{"properties":{"execution_id":{"type":"string","title":"Execution Id"},"workflow_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workflow Id"},"status":{"type":"string","title":"Status"},"started_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At"},"current_step":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Step"},"steps_completed":{"type":"integer","title":"Steps Completed","default":0},"total_steps":{"type":"integer","title":"Total Steps","default":0},"result":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Result"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["execution_id","status"],"title":"WorkflowExecutionStatusResponse","description":"GET /workflow/executions/{id} (manager ExecutionStatus). Scrubbed."},"WorkflowHistoryResponse":{"properties":{"executions":{"items":{"type":"object"},"type":"array","title":"Executions"},"count":{"type":"integer","title":"Count","default":0}},"type":"object","title":"WorkflowHistoryResponse","description":"GET /workflow/history. Each execution entry is scrubbed."},"WorkflowLogsResponse":{"properties":{"execution_id":{"type":"string","title":"Execution Id"},"logs":{"items":{"type":"string"},"type":"array","title":"Logs"},"count":{"type":"integer","title":"Count","default":0}},"type":"object","required":["execution_id"],"title":"WorkflowLogsResponse","description":"GET /workflow/executions/{id}/logs. Log lines are scrubbed of known values."},"WorkflowTemplateRunRequest":{"properties":{"sandbox_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Sandbox Id"},"sandbox_ids":{"items":{"type":"string"},"type":"array","maxItems":50,"title":"Sandbox Ids"},"inputs":{"type":"object","title":"Inputs"},"async_execution":{"type":"boolean","title":"Async Execution","default":true},"timeout":{"anyOf":[{"type":"integer","maximum":7200.0,"minimum":1.0},{"type":"null"}],"title":"Timeout"}},"additionalProperties":false,"type":"object","title":"WorkflowTemplateRunRequest","description":"POST /api/workflows/templates/{id}/run body — the fleet fan-out.\n\nTargets are the UNION of `sandbox_id` (single convenience) and `sandbox_ids`\n(the fleet), deduped; at least one is required. The run is DECOUPLED from any\nsingle sandbox — the same saved workflow runs against one box or many. `inputs`\nare validated against the template's non-secret `input_schema` ONCE and applied\nto every target."},"WorkflowTemplateRunResponse":{"properties":{"template_id":{"type":"string","title":"Template Id"},"dispatched":{"type":"integer","title":"Dispatched","default":0},"failed":{"type":"integer","title":"Failed","default":0},"results":{"items":{"$ref":"#/components/schemas/WorkflowTemplateRunResult"},"type":"array","title":"Results"}},"type":"object","required":["template_id"],"title":"WorkflowTemplateRunResponse"},"WorkflowTemplateRunResult":{"properties":{"sandbox_id":{"type":"string","title":"Sandbox Id"},"ok":{"type":"boolean","title":"Ok"},"execution_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Execution Id"},"error_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Code"}},"type":"object","required":["sandbox_id","ok"],"title":"WorkflowTemplateRunResult","description":"One target's outcome in a fan-out run. Partial success is the design: an\nunreachable/foreign/stopped box records a STATIC error_code and never aborts\nthe whole batch."},"WorkflowTemplateSaveRequest":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"description":{"type":"string","maxLength":500,"title":"Description","default":""},"owner_scope":{"type":"string","enum":["user","org"],"title":"Owner Scope","default":"user"},"definition":{"type":"object","title":"Definition"},"input_schema":{"items":{"type":"object"},"type":"array","maxItems":50,"title":"Input Schema"}},"additionalProperties":false,"type":"object","required":["name","definition"],"title":"WorkflowTemplateSaveRequest","description":"POST/PUT /api/workflows/templates body. `user_id`/`org_id` are server-assigned\nfrom the caller — NEVER read from the body (with extra=\"forbid\", sending them or\nany unknown field is a 422, not a silent ignore)."}}}}