# wanno — agent operating contract > wanno is a public-beta platform for deploying and operating applications in isolated VMs. > This file is the machine-readable contract; the sections below are normative for agents. ## Start here - [Documentation](https://wanno.dev/docs): human-readable guides for every surface. - [Product and dashboard](https://wanno.dev): sign in, deployments, projects, billing. - [REST API reference](https://wanno.dev/docs/rest): the endpoint catalog, base URL `https://wanno.dev/api`. - [Authenticate](https://wanno.dev/docs/auth): how API keys, scopes and teams work. - [CLI quickstart](https://wanno.dev/docs/cli): install with `curl -fsSL https://wanno.dev/cli/install.sh | bash`. - [CLI commands](https://wanno.dev/docs/commands): every subcommand and flag. - [MCP quickstart](https://wanno.dev/docs/mcp): install with `curl -fsSL https://wanno.dev/mcp/install.sh | bash`. - [Operating contract](https://wanno.dev/docs/contract): the rules below, in prose. - [Capability map](https://wanno.dev/docs/map): the same map generated into this file. - [SDKs](https://wanno.dev/docs/sdk): Node and Python clients over the same REST contract. - [API keys](https://wanno.dev/settings/api-keys): create and revoke `pvk_...` keys. - [This contract](https://wanno.dev/llms.txt): canonical URL of this file. Authentication for every REST call: `Authorization: Bearer $WANNO_API_KEY`. ## Choose a control surface 1. Prefer MCP when structured tools are available. Discover the current tool list at runtime; do not assume a fixed count. 2. Use the CLI for terminal, local-folder, and CI workflows. 3. Use REST for direct integrations. REST callers must provide an explicit runtime specification. Do not assume registry packages are published. Use the first-party installers below. ## MCP Install the self-contained Node bundle and register it with supported clients: ```bash curl -fsSL https://wanno.dev/mcp/install.sh | bash ``` The installer places the server at `~/.wanno/wanno-mcp.mjs`. If automatic registration is unavailable, use: ```json { "mcpServers": { "wanno": { "command": "node", "args": ["/absolute/path/to/.wanno/wanno-mcp.mjs"] } } } ``` Ask the MCP server to `login`, or provide `WANNO_API_KEY`. Read the live MCP tool descriptions before acting. ## CLI ```bash curl -fsSL https://wanno.dev/cli/install.sh | bash wanno login wanno whoami wanno deploy ``` `wanno deploy` deploys the current folder; `wanno drop` is an alias. The CLI detects the stack, install command, start command, and public port before creation. Use `WANNO_API_URL` only to override the default API base. ## Non-negotiable agent rules 1. Always read the deployment URL from `vm.url`. Never construct a hostname. 2. Bind the public application server to `0.0.0.0`, not localhost. 3. For MCP or CLI deployments, detect the stack and commands first. Do not invent commands when detection is available. 4. For direct REST creation, send `stack`, `installCommand`, `startCommand`, and `exposedPort` explicitly. 5. Poll `GET /vms/:id` or `get_vm` until `running` or `error`. Creation is asynchronous. 6. On failure, read `status`, `errorMessage`, build events, and logs before changing anything or retrying. 7. Put secrets in environment variables. Never write them into source, commands, prompts, URLs, or logs. 8. Use the narrowest API-key scopes required by the operation. 9. Upload a complete, internally consistent file set in one operation; partial uploads can create transient import failures. 10. Verify code changes with a deterministic build command and require exit code 0. ## REST quickstart ```bash export WANNO_API_KEY="pvk_..." curl -sS https://wanno.dev/api/vms \ -H "Authorization: Bearer $WANNO_API_KEY" curl -sS -X POST https://wanno.dev/api/vms \ -H "Authorization: Bearer $WANNO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "repoUrl": "https://github.com/owner/repo", "stack": "node22", "installCommand": "npm install", "startCommand": "npm run dev -- --host 0.0.0.0 --port 3000", "exposedPort": 3000 }' ``` After creation, poll the returned VM id: ```bash curl -sS https://wanno.dev/api/vms/ \ -H "Authorization: Bearer $WANNO_API_KEY" ``` ## Core deployment lifecycle - List: `GET /vms` - Create from a repository or multipart zip: `POST /vms` - Inspect status and authoritative URL: `GET /vms/:id` - Stream build and application logs: `GET /vms/:id/logs` (SSE) - Execute inside a running VM: `POST /vms/:id/run` - Upload files to a running VM: `POST /vms/:id/files` - List environment keys: `GET /vms/:id/env`; secret values are never returned - Merge or replace environment values: `PUT /vms/:id/env` - Restart process: `POST /vms/:id/restart` - Rebuild from stored source/spec: `POST /vms/:id/redeploy` - Wake or recreate an eligible deployment: `POST /vms/:id/revive` - Delete: `DELETE /vms/:id` Local-folder/zip deployments are one-shot source uploads. Unless a durable source is attached, ship changes with another upload or deployment rather than assuming the original archive can be reconstructed. ## Deploy a project with all of its values The full recipe for a production-shaped deployment. Provision dependencies first, then create the deployment so injected values are present from the first boot: 1. Managed Postgres (when the app needs one): `POST /databases` with optional `{"name": "...", "ephemeral": true|false}`. The response reveals `connectionString` ONCE — capture it immediately. Pass `ephemeral: true` ONLY for test/preview environments (the database is deleted when the deployment it is attached to is destroyed; never-attached ephemerals are reaped after 24h). Default (persistent) never expires — use it for anything holding real data. Ask the user which lifecycle they want when unspecified. 2. Object storage (when the app stores files): `POST /storage/buckets` `{"name": "...", "public": false}`, then grant the deployment or project access with `POST /storage/buckets/:id/grants` — the grant injects `_URL`, `_BUCKET`, `_TOKEN` (default prefix `WANNO_STORAGE`) as env vars on every deploy of the grantee. 3. Create the deployment: `POST /vms` with `environmentVariables` (all app secrets/config), plus `databaseIntegrationId` to attach the database from step 1 (injected as `DATABASE_URL`). 4. Poll `GET /vms/:id` to `running`; read the public URL from `vm.url`. 5. Cron jobs: create a function on the deployment (`POST /vms/:id/functions`) and attach a schedule (`PUT /vms/:id/functions/:fnId/schedule`) — see Functions below. ## Managed databases (Postgres) - Provision: `POST /databases` `{name?, ephemeral?}` → reveals `connectionString` once; `GET /integrations` lists them redacted; `DELETE /integrations/:id` removes one. - Attach to a deployment at create time via `databaseIntegrationId` on `POST /vms` (injected as `DATABASE_URL`). - Lifecycle: persistent by default (NEVER expires, survives deployment deletion). `ephemeral: true` = deleted with the deployment it is attached to; intended for test/preview environments only. Never mark a database holding real data ephemeral. - Cost: $2 one-time per provisioned database, either lifecycle. ## Object storage (buckets) Scopes `storage:read`/`storage:write`. - Buckets: `GET|POST /storage/buckets`, `DELETE /storage/buckets/:id` (`?force=true` when non-empty), team usage at `GET /storage/usage`. - Objects: raw-body `PUT|GET|DELETE /storage/buckets/:id/objects/` (streaming; `Range` and `ETag` supported), listing at `GET /storage/buckets/:id/objects?prefix=&delimiter=/`. - URLs for apps and users: - Public bucket (`public: true` at create): anyone can read objects at `GET /api/storage/public//` — use for directly embeddable assets. - Presigned URL: `POST /storage/buckets/:id/presign` mints a time-limited signed URL for exactly one method + key (max 7 days) — use for private sharing and browser uploads. - App credentials: grants (`POST /storage/buckets/:id/grants`, target one project XOR one deployment) inject `_URL/_BUCKET/_TOKEN` env vars on deploy; bucket tokens (`POST /storage/buckets/:id/tokens`, `wst_` plaintext shown once) are standalone data-path credentials. ## Functions and cron jobs Scopes `functions:read`/`functions:write`. A function is one JS/TS file served from its own scale-to-zero VM; its public URL is on the function record — read it, never construct it. This is also the cron-job primitive. - CRUD: `GET|POST /vms/:vmId/functions`, `GET|PATCH|DELETE /vms/:vmId/functions/:fnId`, hot-swap code with `PUT .../code` `{code, language: "js"|"ts"}`, env with `PUT .../env` `{env, remove?}` (values write-only). - Auth: private by default — invoke tokens (`POST .../tokens`, `fnt_` plaintext shown once) sent as `Authorization: Bearer` or `X-Function-Token`; `public: true` opens the URL. Server-side test invoke: `POST .../invoke`. - Cron: `PUT /vms/:vmId/functions/:fnId/schedule` `{expression: "0 * * * *", timezone?, method?: "GET"|"POST", path?, enabled?}` (standard 5-field cron, timezone-aware; response includes next run times); `DELETE .../schedule` removes it; `GET .../cron/runs` lists recent runs; `POST .../cron/run-now` fires once immediately. The runner wakes automatically for scheduled runs — sleeping functions still run their jobs. ## Capability map Use MCP discovery or the REST routes for the exact schema. ### bot — AI bots, channels and the web widget - List the team's AI bots. — CLI `wanno bot list` · MCP `bot_list` · REST `GET /bots` - Show one bot's detail + channels. — CLI `wanno bot get bot_123` · MCP `bot_get` · REST `GET /bots/{id}` - Have the LLM draft/replace a bot's workflow graph from an objective. — CLI `wanno bot generate bot_123 "Answer questions about our pricing"` · MCP `bot_generate` · REST `POST /bots/generate` - Compile the bot's graph into the web-chat app and deploy it live. — CLI `wanno bot deploy bot_123` · MCP `bot_deploy` · REST `POST /bots/{id}/deploy` - Create a bot (with an objective, its graph is drafted immediately). — CLI `wanno bot create support --channels chat,whatsapp` · MCP `bot_create` · REST `POST /bots` — body `{"name":"support","prompt":"Answer questions about our pricing"}` - Show the bot's connected MCP server (bearer token never returned). — CLI `wanno bot mcp get bot_123` · MCP `bot_mcp_get` · REST `GET /bots/{id}/mcp` - Connect (or replace) the bot's single MCP server. — CLI `wanno bot mcp set bot_123 https://mcp.example.com --token secret` · MCP `bot_mcp_set` · REST `PUT /bots/{id}/mcp` - Disconnect the bot's MCP server. — CLI `wanno bot mcp unset bot_123` · MCP `bot_mcp_unset` · REST `DELETE /bots/{id}/mcp` - Show the bot's web chat widget config + embed snippet. — CLI `wanno bot widget get bot_123` · MCP `bot_widget_get` · REST `GET /bots/{id}/web-config` - Update the bot's web widget: greeting, quick actions, theme, origins. — CLI `wanno bot widget set bot_123 --greeting "Hi! Ask me anything" --suggest "Pricing?" --suggest "How do I deploy?"` · MCP `bot_widget_set` · REST `PUT /bots/{id}/web-config` - Delete a bot (and its deployed web-chat app). — CLI `wanno bot delete bot_123` · MCP `bot_delete` · REST `DELETE /bots/{id}` ### db — managed Postgres databases - Provision a managed Postgres for the team (connection string shown once). — CLI `wanno db create my-db --deployment abc123` · MCP `db_create` · REST `POST /databases` — body `{"name":"app-db","vmId":"","ephemeral":false}` - List the team's managed databases. — CLI `wanno db list` · MCP `db_list` · REST `GET /integrations` - Delete a managed database. Its data is gone for good. — CLI `wanno db delete db_123` · MCP `db_delete` · REST `DELETE /integrations/{id}` - (Re)attach a database to a deployment or Project, or detach it. — CLI `wanno db assign db_123 --deployment abc123` · MCP `db_assign` · REST `PATCH /integrations/{id}` ### domain — custom domains - List the team's custom domains. — CLI `wanno domain list` · MCP `domain_list` · REST `GET /domains` - Register a custom domain (returns the DNS records to set). — CLI `wanno domain add app.example.com` · MCP `domain_add` · REST `POST /domains` — body `{"fqdn":"app.example.com"}` - Verify DNS ownership of a domain. — CLI `wanno domain verify dom_123` · MCP `domain_verify` · REST `POST /domains/{id}/verify` - Attach a verified domain to a deployment or a Project. — CLI `wanno domain attach dom_123 abc123` · MCP `domain_attach` · REST `POST /domains/{id}/attach` — body `{"vmId":""}` - Detach a domain from its deployment/Project. — CLI `wanno domain detach dom_123` · MCP `domain_detach` · REST `DELETE /domains/{id}/attach` - Delete a domain. Traffic to it stops routing. — CLI `wanno domain delete dom_123` · MCP `domain_delete` · REST `DELETE /domains/{id}` ### experiment — experiments - List a Project's experiments. — CLI `wanno experiment list proj_123` · MCP `experiment_list` · REST `GET /projects/{id}/experiments` - Show one experiment. — CLI `wanno experiment get proj_123 exp_1` · MCP `experiment_get` · REST `GET /projects/{id}/experiments/{experimentId}` - Create an experiment from a JSON spec file. — CLI `wanno experiment create proj_123 ./spec.json` · MCP `experiment_create` · REST `POST /projects/{id}/experiments` - Per-variant results. — CLI `wanno experiment results proj_123 exp_1` · MCP `experiment_results` · REST `GET /projects/{id}/experiments/{experimentId}/results` - Health checks (sample ratio, exposure balance). — CLI `wanno experiment health proj_123 exp_1` · MCP `experiment_health` · REST `GET /projects/{id}/experiments/{experimentId}/health` - Raw exposure counts by variant. — CLI `wanno experiment exposures proj_123 exp_1` · MCP `experiment_exposures` · REST `GET /projects/{id}/experiments/{experimentId}/exposures` - Audit log of state changes. — CLI `wanno experiment audit proj_123 exp_1` · MCP `experiment_audit` · REST `GET /projects/{id}/experiments/{experimentId}/audit` - Export the experiment's full record. — CLI `wanno experiment export proj_123 exp_1` · MCP `experiment_export` · REST `GET /projects/{id}/experiments/{experimentId}/export` - Tail recent exposures/events, optionally filtered. — CLI `wanno experiment live-tail proj_123 exp_1` · MCP `experiment_live_tail` · REST `GET /projects/{id}/experiments/{experimentId}/live-tail` - Approve a draft experiment, optionally scheduling its start. — CLI `wanno experiment approve proj_123 exp_1` · MCP `experiment_approve` · REST `POST /projects/{id}/experiments/{experimentId}/approve` - Start an approved experiment now. — CLI `wanno experiment start proj_123 exp_1` · MCP `experiment_start` · REST `POST /projects/{id}/experiments/{experimentId}/start` - Pause a running experiment. — CLI `wanno experiment pause proj_123 exp_1` · MCP `experiment_pause` · REST `POST /projects/{id}/experiments/{experimentId}/pause` - Resume a paused experiment. — CLI `wanno experiment resume proj_123 exp_1` · MCP `experiment_resume` · REST `POST /projects/{id}/experiments/{experimentId}/resume` - Record the ship decision and close out the experiment. — CLI `wanno experiment complete proj_123 exp_1 ship_treatment "clear lift, shipping"` · MCP `experiment_complete` · REST `POST /projects/{id}/experiments/{experimentId}/complete` - Clone an experiment as a new draft. — CLI `wanno experiment clone proj_123 exp_1` · MCP `experiment_clone` · REST `POST /projects/{id}/experiments/{experimentId}/clone` - Archive a completed experiment. — CLI `wanno experiment archive proj_123 exp_1` · MCP `experiment_archive` · REST `POST /projects/{id}/experiments/{experimentId}/archive` - Cancel a scheduled/running experiment without a ship decision. — CLI `wanno experiment cancel proj_123 exp_1` · MCP `experiment_cancel` · REST `POST /projects/{id}/experiments/{experimentId}/cancel` - Delete a draft/scheduled experiment (never-ran only). — CLI `wanno experiment delete proj_123 exp_1` · MCP `experiment_delete` · REST `DELETE /projects/{id}/experiments/{experimentId}` ### flag — feature flags - List a Project's feature flags. — CLI `wanno flag list proj_123` · MCP `flag_list` · REST `GET /projects/{id}/flags` - Show one flag. — CLI `wanno flag get proj_123 new-checkout` · MCP `flag_get` · REST `GET /projects/{id}/flags/{key}` - Delete a flag (a flag with experiment history can't be deleted). — CLI `wanno flag delete proj_123 new-checkout` · MCP `flag_delete` · REST `DELETE /projects/{id}/flags/{key}` - Create a feature flag. — CLI `wanno flag create proj_123 new-checkout "New checkout"` · MCP `flag_create` · REST `POST /projects/{id}/flags` — body `{"key":"new-checkout","name":"New checkout","type":"boolean"}` - List a Project's flag environments. — CLI `wanno flag envs proj_123` · MCP `flag_envs` · REST `GET /projects/{id}/environments` ### fn — serverless functions and crons - List a deployment's serverless functions. — CLI `wanno fn list abc123` · MCP `fn_list` · REST `GET /vms/{id}/functions` - Create a function from a local JS/TS handler file. — CLI `wanno fn create abc123 hello ./handler.ts` · MCP `fn_create` · REST `POST /vms/{id}/functions` — body `{"name":"hello","code":"export default (req, res) => res.json({ ok: true })","language":"ts","public":false}` - Show one function's detail. — CLI `wanno fn get abc123 fn_1` · MCP `fn_get` · REST `GET /vms/{id}/functions/{fnId}` - Hot-swap a function's code from a local file. — CLI `wanno fn code abc123 fn_1 ./handler.ts` · MCP `fn_code` · REST `PUT /vms/{id}/functions/{fnId}/code` - Merge KEY=VALUE pairs into a function's env. — CLI `wanno fn env abc123 fn_1 FOO=bar` · MCP `fn_env` · REST `PUT /vms/{id}/functions/{fnId}/env` - Server-side test invoke via the function's real public path. — CLI `wanno fn invoke abc123 fn_1 --method POST --body {}` · MCP `fn_invoke` · REST `POST /vms/{id}/functions/{fnId}/invoke` - List a function's invoke tokens. — CLI `wanno fn tokens abc123 fn_1` · MCP `fn_tokens` · REST `GET /vms/{id}/functions/{fnId}/tokens` - Mint an invoke token for a function (plaintext shown once). — CLI `wanno fn token-create abc123 fn_1 ci` · MCP `fn_token_create` · REST `POST /vms/{id}/functions/{fnId}/tokens` - Set a cron schedule for the function. — CLI `wanno fn schedule abc123 fn_1 "*/5 * * * *"` · MCP `fn_schedule` · REST `PUT /vms/{id}/functions/{fnId}/schedule` — body `{"expression":"0 7 * * *","timezone":"America/Bogota","method":"POST","path":"/"}` - Revoke a function invoke token. — CLI `wanno fn token-delete abc123 fn_1 tok_1` · MCP `fn_token_delete` · REST `DELETE /vms/{id}/functions/{fnId}/tokens/{tokenId}` - Remove a function's cron schedule. — CLI `wanno fn unschedule abc123 fn_1` · MCP `fn_unschedule` · REST `DELETE /vms/{id}/functions/{fnId}/schedule` - List a function's cron run history. — CLI `wanno fn runs abc123 fn_1` · MCP `fn_runs` · REST `GET /vms/{id}/functions/{fnId}/cron/runs` - Trigger a function's cron run immediately. — CLI `wanno fn run-now abc123 fn_1` · MCP `fn_run_now` · REST `POST /vms/{id}/functions/{fnId}/cron/run-now` - Delete a function (tears down its runner VM). — CLI `wanno fn delete abc123 fn_1` · MCP `fn_delete` · REST `DELETE /vms/{id}/functions/{fnId}` - Update a function's name, tags, or public flag. — CLI `wanno fn update abc123 fn_1 --public true` · MCP `fn_update` · REST `PATCH /vms/{id}/functions/{fnId}` ### gen — AI Builder sessions - List the team's AI Builder sessions. — CLI `wanno gen list` · MCP `gen_list` · REST `GET /generate` - Show an AI Builder session's status, URL, stack and chat history. — CLI `wanno gen get gen_123` · MCP `gen_get` · REST `GET /generate/{id}` - Delete an AI Builder session (its VM, files and owned database). — CLI `wanno gen delete gen_123` · MCP `gen_delete` · REST `DELETE /generate/{id}` - AI-generate an app from a prompt (or resume a session), refined turn by turn. — CLI `wanno gen create --prompt "a todo app with auth"` · MCP `gen_create` · REST `POST /generate` ### github — GitHub workflow scaffolding - Render a GitHub Actions workflow (preview-per-PR, or --deploy for branch deploys). — CLI `wanno github scaffold` · MCP `github_scaffold` · REST `POST /github/scaffold` ### key — API keys - API keys — not available in this CLI release yet. — CLI `wanno key list` ### project — projects — stable slug, durable config, blue/green deploys - List the team's Projects (stable slug + deploy config). — CLI `wanno project list` · MCP `project_list` · REST `GET /projects` - Show one Project's detail (+ recent deployments on a TTY). — CLI `wanno project get my-app` · MCP `project_get` · REST `GET /projects/{id}` - Create a Project (deploy defaults optional — a deploy can override any of them). — CLI `wanno project create my-app --repo https://github.com/me/app --stack node20` · MCP `project_create` · REST `POST /projects` - Delete a Project (409s with a live deployment unless --force). — CLI `wanno project delete my-app --force` · MCP `project_delete` · REST `DELETE /projects/{id}` - History of deployments cut over into this Project. — CLI `wanno project deployments my-app` · MCP `project_deployments` · REST `GET /projects/{id}/deployments` - Deploy a fresh VM into the Project (git source; overrides win over stored defaults). — CLI `wanno project deploy my-app --wait` · MCP `project_deploy` · REST `POST /projects/{id}/deploy` - List the Project's env var KEY NAMES. — CLI `wanno project env get my-app` · MCP `project_env_get` · REST `GET /projects/{id}/env` - Merge KEY=VALUE pairs onto the Project's stored env. — CLI `wanno project env set my-app FOO=bar` · MCP `project_env_set` · REST `PUT /projects/{id}/env` - Show the Project's edge IP firewall config. — CLI `wanno project firewall get my-app` · MCP `project_firewall_get` · REST `GET /projects/{id}/firewall` - Replace the Project's edge IP firewall config (seeded into every future deploy). — CLI `wanno project firewall set my-app --default deny --allow 10.0.0.0/8` · MCP `project_firewall_set` · REST `PUT /projects/{id}/firewall` - Rename the Project (display name, not the slug). — CLI `wanno project rename my-app "My App"` · MCP `project_rename` · REST `PATCH /projects/{id}` - Change the Project's public slug URL. — CLI `wanno project rename-slug my-app my-new-app` · MCP `project_rename_slug` · REST `PATCH /projects/{id}/slug` - Adopt a standalone deployment as the Project's active deployment. — CLI `wanno project assign abc123 --project my-app` · MCP `project_assign` · REST `POST /projects/{id}/assign` - Detach a deployment from its Project (back to standalone). — CLI `wanno project unassign abc123` · MCP `project_unassign` · REST `POST /projects/{id}/unassign` ### prompt — prompt A/B experiments - List the team's prompt experiments. — CLI `wanno prompt list` · MCP `prompt_list` · REST `GET /prompts` - Experiment detail + per-variant results in one card. — CLI `wanno prompt get pex_123` · MCP `prompt_get` · REST `GET /prompts/{id}` - Create a prompt A/B experiment with one or more variants. — CLI `wanno prompt create --key greeting --variant a="Hi {{name}}" --variant b="Hey {{name}}!"` · MCP `prompt_create` · REST `POST /prompts` — body `{"key":"greeting","name":"Greeting","mode":"random","variants":[{"key":"a","template":"Hi {{name}}"},{"key":"b","template":"Hey {{name}}!"}]}` - Anchor a prompt experiment to a deployment and reveal its runtime credential. — CLI `wanno prompt attach pex_123 abc123` · MCP `prompt_attach` · REST `POST /prompts/{id}/attach` - List an experiment's served/completed traces. — CLI `wanno prompt traces pex_123 --status completed` · MCP `prompt_traces` · REST `GET /prompts/{id}/traces` - Rate one trace 👍/👎, or clear its rating. — CLI `wanno prompt rate pex_123 trc_1 up` · MCP `prompt_rate` · REST `POST /prompts/{id}/traces/{traceId}/rating` - Detach a prompt experiment from its deployment. — CLI `wanno prompt detach pex_123` · MCP `prompt_detach` · REST `POST /prompts/{id}/detach` - Delete one variant from a prompt experiment. — CLI `wanno prompt variant-delete pex_123 pvr_1` · MCP `prompt_variant_delete` · REST `DELETE /prompts/{id}/variants/{variantId}` - List the team's prompt runtime credentials (grants). — CLI `wanno prompt grants` · MCP `prompt_grants` · REST `GET /prompts/grants` - Rotate a prompt grant's credential (the old token stops working). — CLI `wanno prompt grant-rotate pgr_123` · MCP `prompt_grant_rotate` · REST `POST /prompts/grants/{grantId}/rotate` - Revoke a prompt grant (its credential stops working immediately). — CLI `wanno prompt grant-revoke pgr_123` · MCP `prompt_grant_revoke` · REST `DELETE /prompts/grants/{grantId}` ### store — object storage buckets and objects - List the team's object storage buckets. — CLI `wanno store list` · MCP `store_list` · REST `GET /storage/buckets` - Bytes used vs. the team's storage cap. — CLI `wanno store usage` · MCP `store_usage` · REST `GET /storage/usage` - Create a bucket. — CLI `wanno store create assets --public` · MCP `store_create` · REST `POST /storage/buckets` — body `{"name":"assets","public":false}` - Delete a bucket and all of its objects. — CLI `wanno store delete bkt_123 --force` · MCP `store_delete` · REST `DELETE /storage/buckets/{id}` - List objects in a bucket. — CLI `wanno store ls bkt_123 images/` · MCP `store_ls` · REST `GET /storage/buckets/{id}/objects` - Upload a local file as an object. — CLI `wanno store put bkt_123 ./logo.png` · MCP `store_put` · REST `PUT /storage/buckets/{id}/objects/{key}` - Download an object to a local file. — CLI `wanno store get bkt_123 logo.png --out ./logo.png` · MCP `store_get` · REST `GET /storage/buckets/{id}/objects/{key}` - Delete an object. — CLI `wanno store rm bkt_123 logo.png` · MCP `store_rm` · REST `DELETE /storage/buckets/{id}/objects/{key}` - Mint a presigned HMAC URL for one object. — CLI `wanno store presign bkt_123 logo.png --expires 3600` · MCP `store_presign` · REST `POST /storage/buckets/{id}/presign` - List a bucket's project/VM grants. — CLI `wanno store grants bkt_123` · MCP `store_grants` · REST `GET /storage/buckets/{id}/grants` - Grant bucket access to a Project or deployment (injects credentials as env vars). — CLI `wanno store grant bkt_123 --vm abc123` · MCP `store_grant` · REST `POST /storage/buckets/{id}/grants` - Revoke a bucket grant (its injected credential stops working). — CLI `wanno store revoke-grant bkt_123 grt_1` · MCP `store_revoke_grant` · REST `DELETE /storage/buckets/{id}/grants/{grantId}` - List a bucket's access tokens. — CLI `wanno store tokens bkt_123` · MCP `store_tokens` · REST `GET /storage/buckets/{id}/tokens` - Mint a bucket-scoped access token (plaintext shown once). — CLI `wanno store mint-token bkt_123 --name ci` · MCP `store_mint_token` · REST `POST /storage/buckets/{id}/tokens` - Revoke a bucket access token. — CLI `wanno store revoke-token bkt_123 tok_1` · MCP `store_revoke_token` · REST `DELETE /storage/buckets/{id}/tokens/{tokenId}` - Rename a bucket or toggle its public flag. — CLI `wanno store update bkt_123 --public false` · MCP `store_update` · REST `PATCH /storage/buckets/{id}` ### team — teams, members and invitations - Teams — not available in this CLI release yet. — CLI `wanno team list` ### vm — deployments — logs, shell, env, firewall, monitoring, slugs - List the team's deployments. — CLI `wanno vm list` · MCP `vm_list` · REST `GET /vms` - Show one deployment's detail. — CLI `wanno vm get abc123` · MCP `vm_get` · REST `GET /vms/{id}` - Stream a deployment's log feed. — CLI `wanno vm logs abc123` · MCP `vm_logs` · REST `GET /vms/{id}/logs` - Run a shell command inside the deployment as root. — CLI `wanno vm run abc123 ls /app` · MCP `vm_run` · REST `POST /vms/{id}/run` — body `{"command":"ls -la /app"}` - List a directory on the deployment's /app disk. — CLI `wanno vm files abc123` · MCP `vm_files` · REST `GET /vms/{id}/files/list` - Print a text file from the deployment's /app disk. — CLI `wanno vm cat abc123 package.json` · MCP `vm_cat` · REST `GET /vms/{id}/files/content` - Write local files into a running deployment without a rebuild. — CLI `wanno vm upload abc123 index.js server.js` · MCP `vm_upload` · REST `POST /vms/{id}/files` - Rebuild (or relaunch in dev mode) and restart the app. — CLI `wanno vm restart abc123` · MCP `vm_restart` · REST `POST /vms/{id}/restart` - Pull latest code and rebuild in place (same id, env, slug). — CLI `wanno vm redeploy abc123` · MCP `vm_redeploy` · REST `POST /vms/{id}/redeploy` - Rebuild a stopped/errored PERMANENT deployment from its stored source. — CLI `wanno vm revive abc123` · MCP `vm_revive` · REST `POST /vms/{id}/revive` - Put a permanent deployment to sleep now. — CLI `wanno vm pause abc123` · MCP `vm_pause` · REST `POST /vms/{id}/pause` - Permanently delete a deployment. — CLI `wanno vm destroy abc123` · MCP `vm_destroy` · REST `DELETE /vms/{id}` - List the deployment's env var KEY NAMES (values are write-only). — CLI `wanno vm env get abc123` · MCP `vm_env_get` · REST `GET /vms/{id}/env` - Merge KEY=VALUE pairs into the deployment's env and restart it. — CLI `wanno vm env set abc123 FOO=bar BAZ=qux` · MCP `vm_env_set` · REST `PUT /vms/{id}/env` — body `{"env":{"API_KEY":"secret-value"},"replace":false}` - Show the deployment's edge IP firewall config. — CLI `wanno vm firewall get abc123` · MCP `vm_firewall_get` · REST `GET /vms/{id}/firewall` - Replace the deployment's edge IP firewall config. — CLI `wanno vm firewall set abc123 --default deny --allow 10.0.0.0/8` · MCP `vm_firewall_set` · REST `PUT /vms/{id}/firewall` — body `{"defaultAction":"allow","ipRules":[{"action":"deny","cidr":"203.0.113.0/24","note":"block a range"}],"rateLimit":{"maxEvents":100,"window":"1m"}}` - Show the deployment's uptime monitoring config + open alerts. — CLI `wanno vm monitoring get abc123` · MCP `vm_monitoring_get` · REST `GET /vms/{id}/monitoring` - Turn monitoring on/off and configure alert emails + error-rate rule. — CLI `wanno vm monitoring set abc123 on --email me@example.com` · MCP `vm_monitoring_set` · REST `PUT /vms/{id}/monitoring` — body `{"enabled":true,"emails":["you@example.com"],"notifyOn":{"down":true,"recovery":true,"errorRate":{"threshold":0.05,"windowMinutes":10}}}` - Make an ephemeral deployment permanent at a stable slug URL. — CLI `wanno vm persist abc123 my-app` · MCP `vm_persist` · REST `POST /vms/{id}/persist` - Detach the stable slug — back to a plain ephemeral deployment. — CLI `wanno vm unpersist abc123` · MCP `vm_unpersist` · REST `DELETE /vms/{id}/persist` - Assign or rename a deployment's stable slug (guided: validates + checks availability). — CLI `wanno vm slug set abc123 my-app` · MCP `vm_slug_set` · REST `POST /vms/{id}/persist` - Check whether a slug is available. — CLI `wanno vm slug check my-app` · MCP `vm_slug_check` · REST `GET /vms/slug-available` - Toggle always-on (disable scale-to-zero) for a permanent deployment. — CLI `wanno vm always-on abc123 on` · MCP `vm_always_on` · REST `PUT /vms/{id}/always-on` - Traffic + live bandwidth + all-time totals in one view. — CLI `wanno vm metrics abc123 --range 7d` · MCP `vm_metrics` · REST `GET /vms/{id}/traffic` ### General - Inspect a public GitHub repo and print the detected stack/commands/required env vars, without deploying. — CLI `wanno detect-repo https://github.com/owner/repo` · MCP `detect_repo` · REST `POST /vms/detect-public` - Zip a local project folder and print the detected stack/commands/required env vars, without deploying. — CLI `wanno detect-folder ./app` · MCP `detect_folder` · REST `POST /vms/detect-zip` - Continue a multi-workload detection by selecting one returned candidate without uploading the source again. — CLI `wanno detect-select-workload sel_abc candidate_web` · MCP `detect_select_workload` · REST `POST /vms/detect-selection` - Deploy the current folder (or a git repo via --repo) as a new VM. — CLI `wanno deploy` · MCP `deploy` · REST `POST /vms` - Interactive browser device-login. Stores a scoped API key locally. — CLI `wanno login` · MCP `login` · REST `POST /auth/device/start` - Forget the stored credential. — CLI `wanno logout` · MCP `logout` - Show the authenticated account. — CLI `wanno whoami` · MCP `whoami` · REST `GET /accounts/current` - Account credit balance + live usage. — CLI `wanno billing` · MCP `billing` · REST `GET /user/billing` - Interactive home screen (opened automatically by a bare `wanno` on a terminal). — CLI `wanno home` · REST `GET /vms` ### REST-only surfaces (no CLI/MCP equivalent) #### Teams - `GET /teams` — List the account's teams (the tenancy grouping under an account). (scope: requireScope("projects:read")) — planned: CLI plan 3 (device-flow intents) - `POST /teams` — Create a team. (scope: requireScope("projects:write") + requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `PATCH /teams/{teamId}` — Rename a team. (scope: requireScope("projects:write") + requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `DELETE /teams/{teamId}` — Archive (delete) a team. (scope: requireScope("projects:write") + requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `GET /teams/{teamId}/metrics` — Per-team compute/usage metrics for the settings dashboard. (scope: requireScope("usage:read")) — planned: CLI plan 3 (device-flow intents) #### API keys - `GET /teams/{teamId}/api-keys` — List a team's API keys (never returns the secret). (scope: requireScope("keys:read") + requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `POST /teams/{teamId}/api-keys` — Mint a team-scoped API key (plaintext shown once). (scope: requireScope("keys:write") + requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `DELETE /teams/{teamId}/api-keys/{keyId}` — Revoke an API key. (scope: requireScope("keys:write") + requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `POST /keys/self/revoke` — Self-revoke the API key making this request (the mobile sign-out path). (scope: any API-key principal (no scope check — the bearer itself proves possession)) — planned: CLI plan 3 (device-flow intents) #### Members & invitations - `GET /accounts/members` — Account roster (members + pending invitations). Any member may view. (scope: any account member (session); no scope check) — planned: CLI plan 3 (device-flow intents) - `POST /accounts/invitations` — Invite someone to the account by email + role (rate-limited; only an owner may invite an owner). (scope: requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `DELETE /accounts/invitations/{invitationId}` — Revoke a pending invitation. (scope: requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `PATCH /accounts/members/{userId}` — Change a member's role (only an owner may grant/touch owner; the sole owner can't be demoted). (scope: requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `DELETE /accounts/members/{userId}` — Remove a member from the account. (scope: requireRole("admin")) — planned: CLI plan 3 (device-flow intents) - `POST /invitations/accept` — Accept an account invitation, or a per-deployment share invitation (dsh_ token), by its token. (scope: signed-in, non-anonymous session; no scope check) #### Drives - `GET /drives` — List the team's block-storage drives (attachable to a VM). (scope: requireScope("vms:read")) - `POST /drives` — Create a drive (size capped by the account's plan). (scope: requireScope("vms:write")) - `DELETE /drives/{id}` — Delete a drive (must not be attached to a running VM). (scope: requireScope("vms:write")) #### Templates - `POST /templates/directus` — One-click Directus (headless CMS): provisions a managed Postgres + a permanent always-on VM, returns one-time admin credentials. (scope: requireScope("vms:write")) #### Search - `GET /search` — Command-palette search across deployments, projects, domains, functions and bots (+ deployments shared with the caller). (scope: requireScope("vms:read")) #### Showcase - `GET /showcase` — Published gallery cards (curated builder apps). Public, degrades to an empty list on any failure. (scope: public — no auth) - `GET /showcase/{slug}` — One published showcase card by slug. (scope: public — no auth) - `GET /showcase/{slug}/thumbnail` — The showcase card's live screenshot. (scope: public — no auth) - `GET /showcase/{slug}/remix` — Full remix brief for a showcase item (original prompt + Style DNA) — feed straight into POST /generate as remixOf. (scope: requireScope("vms:read")) #### Feedback - `POST /feedback` — Submit in-app feedback (a rating and/or a message, plus the page). (scope: signed-in, non-anonymous session; no scope check) #### User & account - `GET /user/me` — The signed-in user + their resolved plan (paid state, limits). (scope: any authenticated principal; no scope check) - `GET /user/shared-vms` — Deployments shared with the caller via per-deployment grants, across accounts. (scope: session principal (API keys get an empty list); no scope check) - `DELETE /user/shared-vms/{vmId}` — Leave a shared deployment (remove the caller's own share grant). (scope: session principal only; no scope check) - `GET /user/overview` — Dashboard account rollups (stat cards + the live deployments grid's metric batch). (scope: requireScope("usage:read")) - `GET /user/history` — Historical resource usage. (scope: requireScope("usage:read")) - `GET /user/deductions` — Credit debit ledger (what spent the balance, and on what). (scope: requireScope("usage:read")) - `GET /user/referral` — Referral status/code for the account. (scope: requireScope("usage:read")) - `GET /user/compute-timeline` — Compute cost over time (for the billing chart). (scope: requireScope("usage:read")) - `POST /user/password` — Change the signed-in user's password (legacy — only accounts that still have a password hash; Google OAuth is the only sign-in path now). (scope: signed-in session; no scope check) #### Accounts - `GET /accounts` — Every account (workspace) the signed-in user belongs to, for the account switcher. (scope: any authenticated principal; no scope check) - `POST /accounts/checkout-session` — Start a Stripe Checkout session to buy credit (custom amount; the account is credited only once the webhook fires). (scope: requireScope("full_access") + requireRole("owner")) #### Databases - `GET /integrations/{id}/db/tables` — List a managed database's tables (backs the deployment/Settings Data-tab browser). (scope: requireScope("vms:read") + the integration must belong to the caller's team) - `GET /integrations/{id}/db/rows` — Browse a table's rows (paginated). (scope: requireScope("vms:read") + the integration must belong to the caller's team) - `DELETE /integrations/{id}/db/rows` — Delete rows from a table. (scope: requireScope("vms:write") + the integration must belong to the caller's team) - `GET /integrations/{id}/db/stats` — Database overview: table/record totals, size, and per-table column + row counts. (scope: requireScope("vms:read") + the integration must belong to the caller's team) #### Functions - `GET /vms/{id}/functions/{fnId}/usage` — A function's metered usage (invocations, compute). (scope: requireScope("functions:read")) - `GET /vms/{id}/functions/{fnId}/metrics` — A function's HTTP metrics (hits, error rate) over a time range. (scope: requireScope("functions:read")) - `GET /vms/{id}/functions/{fnId}/runtime` — A function runner's live load-average/memory stats (null when it's asleep/stopped). (scope: requireScope("functions:read")) #### System - `GET /system/status` — Control-plane + host cache stats (fleet health). (scope: requireScope("system:read")) #### Channel credentials - `GET /settings/channel-credentials` — List the team's saved bot channel credentials. (scope: requireScope("bots:read")) - `POST /settings/channel-credentials` — Save a channel credential (e.g. a WhatsApp Business token). (scope: requireScope("bots:write")) - `DELETE /settings/channel-credentials/{id}` — Delete a channel credential. (scope: requireScope("bots:write")) #### Usage - `GET /user/usage` — Account-scoped compute/bandwidth usage + the concurrency gauge. (scope: requireScope("usage:read")) #### GitHub - `GET /github/status` — GitHub App install + connection status for the team. (scope: requireScope("github:read")) - `GET /github/connections` — List the team's GitHub App installations/connections. (scope: requireScope("github:read")) - `GET /github/repos` — List repositories the GitHub App can access. (scope: requireScope("github:read")) - `GET /github/triggers` — List the team's preview-per-PR / push deploy triggers. (scope: requireScope("github:read")) - `POST /github/triggers` — Create a preview-per-PR or push deploy trigger. (scope: requireScope("github:write")) - `PATCH /github/triggers/{id}` — Update a deploy trigger. (scope: requireScope("github:write")) - `DELETE /github/triggers/{id}` — Delete a deploy trigger. (scope: requireScope("github:write")) - `GET /github/triggers/{id}/deployments` — List the deployments a trigger has produced. (scope: requireScope("github:read")) #### Storage - `POST /storage/provision` — One-click provision: create a bucket + grant + token for a VM/project. (scope: requireScope("storage:write")) — body `{"vmId":"","name":"assets","public":false}` - `GET /storage/grants` — List the team's storage grants across all buckets. (scope: requireScope("storage:read")) - `GET /storage/buckets/{bucketId}/members` — Get a bucket's member policy + access list. (scope: requireScope("storage:read")) - `PUT /storage/buckets/{bucketId}/members` — Replace a bucket's member policy + access list. (scope: requireScope("storage:write")) - `GET /storage/member-access/{userId}` — Read one member's access map across restricted buckets (admin). (scope: requireScope("storage:read")) - `PUT /storage/member-access/{userId}` — Upsert/remove one member's access rows across restricted buckets. (scope: requireScope("storage:write")) Do not infer feature availability, quota, price, or limits from old plan names. Read `/accounts/current`, `/user/usage`, `/user/billing`, and operation errors for the active account. ## Failure protocol When an operation fails: 1. Preserve the VM id and original request. 2. Fetch the current VM record. 3. Read `errorMessage` and stream logs. 4. Classify the failure as auth/scope, quota/payment, conflict/state, rate limit, detection/spec, build, boot, or runtime. 5. Correct only the identified cause. 6. Retry once through `redeploy`, `revive`, or a new create as appropriate. 7. Poll to a terminal outcome and report the real `vm.url` only when running. Handle these HTTP statuses explicitly: - `401`: missing or invalid authentication - `403`: insufficient scope or policy denial - `404`: resource absent or outside the active account/team - `409`: conflicting operation or invalid current state - `402`: payment or credit gate; inspect the response code and billing state - `429`: rate or concurrency limit; respect retry guidance - `5xx`: platform failure; preserve diagnostics and avoid retry loops ## Security contract - Typical deployment keys need `vms:read` and/or `vms:write`; add scopes only for features used. - Dashboard sessions and Bearer API keys are distinct authentication modes. - Treat VM command execution as root-capable and auditable. - Repository URLs and git refs are validated; do not attempt to bypass SSRF or credential protections. - Never expose API keys to browser code. Mint a narrowly scoped short-lived widget token when a browser integration requires direct VM access. ## Output contract for agents For every mutating workflow, return: - resource id - final status - authoritative `vm.url` when present - operation performed - verification result - concise diagnostics when not successful Never report a deployment as ready solely because the create call succeeded.