wirldocs

For agents: sign in and deploy

How a coding agent signs in to wirl, deploys a first app, and what to do when wirl refuses a request.

An agent cannot create a wirl account: sign-in is Google, the company's Google Workspace domain is the organisation, and a person approves every agent in the browser.

Commands here run as written, except a word in <angle brackets>: replace it, with the app's name for <your-app>, the address the person gave you for <their-email> (never one you made up), and so on. npx fetches the CLI from npm the first time and needs Node 22.13 or later (in a terminal, npm first asks "Ok to proceed? (y)": answer y).

1. Check#

Run npx wirl whoami, or call wirl_whoami over MCP. Signed in, it prints scope: full or scope: deploy and one line for each organisation you belong to: go to step 4. Signed out, the CLI says "Not signed in.": go to step 2. To see which account, run npx wirl whoami --json: it adds email. If that is the wrong person, have the right one approve a new npx wirl login; a WIRL_TOKEN in the environment wins over the saved sign-in until it is unset.

The hosted MCP server and the CLI sign in separately. The hosted wirl_deploy and wirl_preview hand back a command for the CLI to run, so check the CLI too before you deploy. The local MCP server (npx -y @wirl/mcp) uses the CLI's sign-in.

2. Sign in#

Sign in the way that fits where you run. Never print, paste or save a token anywhere: not in a message, a file or a log. The CLI keeps its sign-in in ~/.config/wirl/credentials.json; leave that file alone. A hosted MCP sign-in does not sign in the CLI, so deploying through it needs npx wirl login too.

  • Claude Code. Run claude mcp add --transport http wirl https://app.wirl.dev/mcp. claude mcp add only saves the server's configuration. Then sign in: run /mcp inside Claude Code and follow the steps in the browser, or run claude mcp login wirl in a terminal. If you will need to share, ask the person to tick the full-access box on the consent page (step 3).
  • Any shell. npx wirl login prints a link and a code for the person to approve in their browser: show them both. It then waits for the approval and saves the sign-in; the code expires after 10 minutes. Run it where you can read its first lines while it waits (in the background, if your shell tool returns only when a command ends). If you will need to share, ask the person to tick full access on the approval page (step 3).
  • CI. The person creates a token on the Connect an agent page, https://app.wirl.dev/settings/tokens, and puts it in the job's secrets as WIRL_TOKEN; it is shown once, and no token can make another. A WIRL_TOKEN in the environment is used before the saved sign-in.

3. Access#

The person decides how much an agent may do. npx wirl login gives build-and-update access unless the person ticks full access on the approval page. On the MCP consent page the box "Also give it full access" is that choice: the person ticks it or leaves it. A token made for CI has deploy access unless the person picks "can do everything".

  • Deploy access deploys, previews, reads logs, rolls back and pulls.
  • Full access also shares an app, changes who can open it, deletes, restores, moves or transfers it, manages connections and members, creates organisations and revokes tokens.
  • No token makes an app public: a person does that on the app's Access page.

With deploy access, a request that needs full access is refused with a 403 whose sentence names the page where the person does it: show them that page.

4. First app#

Before you write the app, read The contract: the module's shape, env.DB, migrations written one statement a line, and who is asking. When the folder's name is not the app's, npx wirl init <folder> --server --app <your-app> names the app.

Step CLI MCP tool
Start a folder npx wirl init <your-app> --server, then cd <your-app> none: it writes files on this machine
Try it here npx wirl dev none
Preview it: never live, its own address and database npx wirl preview wirl_preview
See what the preview answers npx wirl fetch / --preview <preview-name> wirl_preview_fetch
Deploy it npx wirl deploy wirl_deploy
Share it, with an address the person gave you npx wirl share <their-email> wirl_share, with full access

Run each command inside the app's folder. A folder you already have needs no init: a deploy takes the app from its wirl.json, else from the folder's name, and makes the app if it is new; if that name is already an app you can deploy to, it updates that app, so run npx wirl apps first when unsure. It prints the URL and who can open it. npx wirl dev keeps running until stopped: start it in the background, read its output for the address, and stop it when you are done. The hosted wirl_deploy and wirl_preview cannot read your files, so they hand back the command to run in a terminal; the local MCP server uploads for itself. If a command names several organisations and asks which, ask the person, then pass --org <slug>.

5. When wirl refuses#

Read the sentence first: it says what happened and usually what to do. The CLI commands on this page and the MCP tools print the sentence and its hint, and usually not the status, the code or the Retry-After header: those are the API's, and the table gives them so you can match a sentence. Wait as long as the sentence says (a minute, 30 seconds, or at least N seconds in the hint of migrations_rate_limited); if it gives no time, wait a minute. Then send the same request again, once, and do not retry in a loop; if it is refused again, tell the person what the sentence says. A program that calls the API itself reads Retry-After: a number of seconds.

API status What you see What to do
401 Not signed in. with no sign-in at all; Not signed in, or this token cannot do that. for a 401, or a 403 with no sentence of its own. The API's 401 says unauthenticated, the hosted MCP server's unauthorized. Sign in (step 2). If you just did, this token cannot do that: tell the person. On the local MCP server, call wirl_login.
403 insufficient_scope: This token can deploy, but not <action>. Do it at <page>, or connect again with a full token. Stop and show the person that page. If they want you to do it, they connect you again with full access: the box ticked on the consent page or the approval page, or "can do everything" for a CI token (step 3).
404 not_found: Not found, or you do not have access to it: app "<app>" in organisation "<org>". wirl answers the same for an app that is not there and one the person cannot open, so do not try other names to find out which. Check the name, --org and --app, and what npx wirl apps or wirl_list_apps lists.
409 migration_needs_approval: <file> <what it does> on the live database. A person approves it at <page>, then deploy again; nothing was applied. With several files, <n> destructive files wait for a person before they change the live database: ... and each file's page. Show the person that page, or each file's page, and deploy again once they say they approved. Never type the file's name or send a confirmation yourself.
409 slug_taken: An app named <name> already exists in this organisation; choose another name. Or, for an app that was deleted, An app named <name> was deleted on <date>; its data is kept until <date>. and the way back. Choose another name. A deleted app comes back only if the person asks for it: ask before you run npx wirl apps restore <name>.
409 migrations_busy: a sentence about applies of migrations under way, ending Try again in a minute; nothing was applied. (or Try again after that; nothing was applied.), and a Retry-After. Wait as long as the sentence says, then deploy again.
409 preview_busy: A preview named <name> is being deployed right now. Try again in a moment. Or too_many_previews: This app already has <n> previews. Delete the oldest first: wirl preview --delete <name>. For the first, wait a moment. For the second, ask the person before you delete a preview you did not make: npx wirl previews lists them.
413 too_large: Bundle is larger than 100MB. or The body is over 1 MiB, the most wirl reads of a request other than a deploy's or a preview's upload, so the request was refused. Or manifest_too_large: wirl.json is larger than 262144 bytes. Send less. For a deploy, leave out what the app does not serve, or list it in .wirlignore (Limits).
429 rate_limited: Too many attempts. Try again shortly. or a sentence saying what ran out. migrations_rate_limited: This org is too close to the database calls wirl allows one org in five minutes for another apply to start. Try again in a minute; nothing was applied. Each with a Retry-After. npx wirl login says Could not start sign-in (429). Wait as long as the sentence says, or a minute when it says none, as Too many attempts. Try again shortly. and npx wirl login do. Then try again once, and do not retry in a loop; if it is refused again, tell the person.
502 app_answers_full or answers_full: npx wirl egress lists it as 502 app_answers_full for an outgoing call of the app: the vendor answered, but wirl could not hold the answer. The vendor may have acted on the call: send it again only if repeating it is safe. No Retry-After. This is the app's own outgoing call, not your request to wirl. Send it again only if repeating the call is safe.
503 too_many_uploads: ... nothing changed. Send it again once one of them has finished. (or ... once the other has finished.) uploads_busy: ... nothing changed. Send it again in 30 seconds. database_unavailable: The control plane could not get a database connection, so nothing was changed. Try again in a few seconds. Each with a Retry-After. For too_many_uploads, wait for your other deploy or preview to finish. For the others, wait as long as the sentence says. Then send the same request again. If the sentence says part of it may have been made (so this request did not finish, and part of it may have been made. Check the app before you send it again.), check with npx wirl versions or wirl_list_versions first.

Any other refusal comes with a sentence, a code or at least a status: read it before you send the request again.

6. Where to go next#