CLI
read as.mdThe guuey CLI is the code path onto the platform: it scaffolds an agent project, runs it locally, and deploys it to Guuey hosting.
Install
Section titled “Install”The CLI ships on npm as @guuey/cli:
npm install -g @guuey/cliguuey --versionThe CLI talks to Guuey’s production API out of the box — no endpoint configuration needed. To point it somewhere else, set GUUEY_API_URL (keep the /v1 suffix — route paths are appended to it); the environment variable always wins over the built-in default.
Log in
Section titled “Log in”guuey loginThis opens your browser, authenticates you with your Guuey account, and delivers an API key back to the CLI via a localhost callback. On a remote or SSH session, guuey login --no-browser prints the auth URL instead — open it anywhere and paste the token back at the prompt. For headless or CI use, pass a pre-minted API key:
guuey login --token guuey_user_...Check who you are with guuey whoami; clear credentials with guuey logout.
The agent definition: guuey.json
Section titled “The agent definition: guuey.json”Every project is described by a single guuey.json at the project root. The agent section is the deployable definition — framework, model, system prompt, and MCP servers. A minimal valid file:
{ "schema": "1", "agent": { "framework": "claude-agent-sdk", "model": "claude-sonnet-5-5", "systemPrompt": { "file": "prompts/system.md" } }}Everything else defaults. agent.mcpServers gets the ggui server at https://mcp.ggui.ai, which gives your agent generative UI, unless it declares "ggui": false. An agent created in the console declares it, so a guuey.json you pull or eject from one carries "ggui": false, and so does a new project from @guuey/create-agentic-app 0.34.0 and later. Delete that entry to turn generative UI on. See the ggui docs for what it enables. Servers you declare merge on top of it, and the platform is otherwise MCP-server-agnostic.
guuey.json MCP servers with credential: 'oauth' accept authMode: 'upfront' | 'lazy'. 'upfront' schedules the broker’s sign-in before the first turn: the agent’s first ask carries metadata.authMode = "upfront", and @guuey/chat renders the connect-first step instead of an aside mid-conversation. Absent = today’s on-demand behaviour — an existing embedder sees no change unless its app’s builder opts a server in. Whatever the mode, the model reads each OAuth server’s state for the turn in the briefing’s Connected services section — see What wraps your prompt.
agent.tools gates which tools the model may call: allowlist lists the only tools it may call, and denylist removes tools from its catalog outright. Each entry is "<server>.<tool>" (one tool of one server), "<server>.*" (every tool of that server), or a bare tool name (that tool on every connected server). Leave allowlist out to allow every tool of every connected server. To allow no tools at all, write "allowlist": ["@none"]: the model then gets no MCP tool of any server, cards, memory and hand-off included. Built-in file tools are removed only by denylist. "@none" may stand alone in the agent’s own allowlist, or in a mode’s when the agent declares no allowlist of its own. Never mix it with other entries, and never write it in a denylist. An empty allowlist ([]) never means no tools: it reads like a missing one, so a deploy that adds one is refused with a message saying so: write ["@none"] to allow no tools, omit the key to allow every tool, list the tools to allow, or deny-list a whole server with "<server>.*" in denylist. A mode’s empty tools.allowlist is refused the same way. An agent already deployed with [] keeps working as it is served: the platform still redeploys and rolls it back, and a console edit that leaves its tools as they are still deploys. guuey deploy checks the local file and refuses [] and a misplaced "@none" either way.
Choosing a model
Section titled “Choosing a model”agent.model takes a provider model id. Which vendor’s ids apply follows the framework your agent runs on: the Claude Agent SDK runs Anthropic models, the OpenAI Agents SDK runs OpenAI models, and Google ADK runs Gemini models. Every list below is checked against the platform’s model registry on every docs build, so an id you read here is one the registry lists — and a row marked announced is in the registry but not yet serving on Guuey; the console’s model rack shows it the same way.
Anthropic (Claude Agent SDK)
- Claude Sonnet 5.5 (
claude-sonnet-5-5) — Guuey’s default: the cost-per-quality call for a hosted agent that runs all day. - Claude Fable 5.1 (
claude-fable-5-1) — Anthropic’s model for the most demanding reasoning (adaptive thinking is always on); in Guuey’s Anthropic lineup. - Claude Opus 5.5 (
claude-opus-5-5) — Anthropic’s newest Opus; in Guuey’s Anthropic lineup. - Claude Haiku 5.5 (
claude-haiku-5-5) — the cheapest model in Guuey’s Anthropic lineup. - Claude Fable 5 (
claude-fable-5) — legacy in Anthropic’s lineup but still active and available (retirement not before June 9, 2027). - Also available, behind See all models in the console: Claude Sonnet 5 (
claude-sonnet-5), Claude Haiku 4.5 (claude-haiku-4-5), Claude Opus 5 (claude-opus-5), Claude Sonnet 4.6 (claude-sonnet-4-6), Claude Opus 4.8 (claude-opus-4-8). An app already on one of them keeps running.
OpenAI (OpenAI Agents SDK)
- GPT-6 Luna (
gpt-6-luna) — Guuey’s default for OpenAI-framework agents, and what the scaffold writes. - GPT-5.6 Terra (
gpt-5.6-terra) — in Guuey’s OpenAI lineup, served through the managed key. - GPT-6 Sol (
gpt-6-sol) — in the lineup, served through the managed key. - GPT-6 Astra (
gpt-6-astra) — in the lineup, served through the managed key. - Also available, behind See all models in the console: GPT-5.6 Sol (
gpt-5.6-sol), GPT-5.6 Luna (gpt-5.6-luna), GPT-5.5 (gpt-5.5), GPT-5.4 (gpt-5.4). - GPT-4o (
gpt-4o) and GPT-4o Mini (gpt-4o-mini) — legacy: no longer offered for new apps. OpenAI shuts them down on 2027-01-14 and names GPT-6 Luna as the replacement; an app still on one keeps running until then, and its console overview shows the date and how to move.
Google (Google ADK)
- Gemini 3.8 Flash (
gemini-3.8-flash) — in Guuey’s Google lineup, served through the managed key. - Gemini 3.6 Flash (
gemini-3.6-flash) — Guuey’s default for Google-framework agents, and what the scaffold writes. - Gemini 3.5 Flash Lite (
gemini-3.5-flash-lite) — in the lineup. - Also available: Gemini 3.5 Flash (
gemini-3.5-flash), Gemini 3.1 Pro (gemini-3.1-pro), Gemini 2.5 Flash (gemini-2.5-flash), Gemini 2.5 Pro (gemini-2.5-pro).
On the Google ADK facet, outputTokens now counts every generated token including reasoning (Gemini’s candidatesTokenCount + thoughtsTokenCount, guarded against double-counting where the provider’s total already balances on candidates); reasoningTokens carries the thoughts breakdown separately. Exception: @google/adk’s Interactions-API route reports no thoughts, so there outputTokens stays exclusive and reasoningTokens is absent (upstream gap). Re-baseline any per-token budget you set against the new count.
On the Claude facet, two usage changes arrive together. First, inputTokens now counts every input token the provider processed: the uncached input plus cache reads plus cache writes. Before, it counted the uncached input only. This applies to every usage entry, and cacheReadTokens and cacheWriteTokens still carry the cache part on its own. Second, turn.done.usage is now flagged cumulative: false (it was true), because its token counters cover that turn alone. Its costUsd is still the running cost of the agent’s whole query, which spans turns, so it carries costScope: "query" whenever a cost is reported: take the latest figure, and never add turns’ costUsd together. The per-model byModel entries remain running totals (cumulative: true). OpenAI and Google ADK agents on Guuey report the same numbers as before. The same release changes the ADK facet only on the Gemini Live API (there, totalTokens becomes input plus output plus tool-use input, and totalTokensRaw carries Google’s own total where it differs), and Guuey’s Gemini models don’t run on the Live API. Hosted agents report the new values once they run the new runtime: a no-code app when the platform rolls it forward (a pinned one on its next deploy), and a code-mode app on its next deploy, because its image carries the runtime it was built on (every guuey deploy queues a new build, even for an unchanged project; see runtime updates). Under guuey dev, they arrive with @guuey/cli 0.29.0. Guuey’s own metering reads the provider’s raw usage, so what you’re billed doesn’t change. Re-baseline any per-token budget you set against Claude’s inputTokens.
Managed-model usage is metered per Plans & billing; each vendor’s list prices are on its own pricing page (Anthropic’s is here).
Long-context pricing: OpenAI prices a prompt with more than 272K input tokens at 2× input and cache rates and 1.5× output, for the full request, on the models that accept one: GPT-6 Sol, Astra and Luna, GPT-5.6 Terra, Sol and Luna, GPT-5.5 and GPT-5.4. Google’s Pro models (Gemini 3.1 Pro, Gemini 2.5 Pro) have their own higher price above 200K prompt tokens. Guuey’s usage meter applies each vendor’s tier to the whole request, the way the vendor bills it.
One thing to know if you switch a code-mode agent to Fable 5.1: it rejects non-default sampling parameters (temperature, top_p, top_k), forced tool_choice, and manual thinking budgets — adaptive thinking is always on. Guuey’s own runners send none of those; if your worker code sets them, drop them first.
Create the file in an existing directory with:
guuey config initOn a code-mode project, your first guuey deploy from an interactive terminal asks who the agent is for — Is this agent for you, or for your customers?, 1 Just for me or 2 For my customers — then for an app name, creates the app, and writes its appId and your answer (as app.builtFor) into the file — you don’t write either by hand. When guuey.json already declares app.builtFor, the deploy prints Built for: … (from guuey.json) and skips the question. (The question is as of @guuey/cli 0.27.0; what app.builtFor means is in Agents as code.) That auto-create is TTY-only and code-mode-only: in CI or any non-interactive run, and for a declarative project, bind the app first — pass --app-id <id>, run guuey pull --app-id <id> to bind an existing app, or create one with guuey apps create --name "My App" (add --for personal or --for customers; without it the app reads as customers). (workspaceId is only needed when you deploy hosted MCP servers, supplied via guuey.json, --workspace, or $GUUEY_WORKSPACE.) Secrets never go in guuey.json. guuey env set KEY=VALUE stores a value as plain app configuration. To keep one sealed at rest, add it under Secrets in the console (Settings → Deployment & scaling). There’s no CLI command for that yet. Either way, the value reaches your colocated MCP servers and the agent’s worker as a plaintext environment variable, in a no-code and a code-mode app alike: the worker is Guuey’s own runtime worker in a no-code app and your code in a code-mode app. Some names are never passed on. Guuey keeps names for the platform, including any that start with AWS_, KUBERNETES_, GUUEY_EXECUTION_ or GUUEY_MINT_. Model-credential names stay out too: ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, OPENAI_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY, GOOGLE_GENAI_API_KEY and OPENROUTER_API_KEY. So do settings that preload or inspect code, such as NODE_OPTIONS, the BUN_INSPECT family, the OPENSSL_CONF family and the dynamic loader’s LD_ names. A provider key belongs under BYOK, in the console under Behavior → Model: Secrets refuses ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY and OPENROUTER_API_KEY, and guuey env set stores them but never passes them on. As of @guuey/cli 0.19.0 every deploy names its target and where it came from — Deploying to app <id> (from --app-id | GUUEY_APP_ID | guuey.json | global-config). — in that precedence: --app-id wins for that deploy only, then the GUUEY_APP_ID environment variable, then the guuey.json binding, then the global default in ~/.guuey/config.json (used only when there is no guuey.json at all). A project whose guuey.json carries no appId is unbound: GUUEY_APP_ID is ignored there (GUUEY_APP_ID=<id> ignored: this project has no binding of its own — pass --app-id <id> to target it. Creating a new app instead.) and the global default is deliberately not used — an interactive deploy of a code-mode project offers to create and link a new app; a non-interactive run — or a declarative project, whatever the terminal — stops with No app linked, naming the ways out (--app-id <id>, guuey pull --app-id <id>, or an interactive code-mode deploy).
Create a project
Section titled “Create a project”Start from a working scaffold:
guuey create my-agent --framework claude-agent-sdkSupported frameworks for create are claude-agent-sdk and openai-agents-sdk. Add --for personal (just for you) or --for customers to record who the agent is for as app.builtFor in guuey.json; the first guuey deploy sends it when it creates the app, and without it that deploy asks. You can also scaffold without installing the CLI first:
npx @guuey/create-agentic-app my-agentThe scaffold carries zero analytics bytes by default; --analytics posthog opts a project into PostHog, reading VITE_POSTHOG_KEY (and an optional VITE_POSTHOG_HOST) at build time — nothing is sent without that key. The Google ADK template scaffolds @google/adk ^2.1.0. After scaffolding, pnpm bootstrap writes the brand, copy and the chat theme document locally; pnpm bootstrap -- --link pushes them to the platform — the allowed origins you name, then the brand accent, then the chat theme (theme.json, kept in step with guuey.app.json’s mode and accent); each push prints ✓ or a named refusal, and the run keeps going. See Your first agentic app.
Prefer a guided walkthrough? Your first agentic app takes the scaffold from npx to a deployed agent step by step.
Built your agent without code? guuey pull --app-id <id> brings its live definition into a local guuey.json, externalizing the system prompt to prompts/system.md so the project stays editable. Check that into your repo and manage it with guuey agent apply from then on: edit, apply, and the live agent matches the checkout (see Agents as code).
Deploy
Section titled “Deploy”guuey deploydeploy auto-detects which of two modes your project uses:
- Declarative mode —
guuey.jsononly, no build step. Your agent is the definition: prompt, model, MCP servers. - Code mode — builds and deploys your
guuey.worker.jsworker bundle, deploying your MCP servers, generative UI registration, and the agent itself in one command.
As of @guuey/cli 0.33.0, deploy is for code mode only. On a declarative project it deploys nothing and prints the command that does (with --app-id or --env-file carried over when you passed them):
guuey deploy deploys code-mode apps. This project is declarative (no-code), so its command is guuey agent apply:
guuey agent apply --waitA CLI before 0.33.0, which a scaffold may pin, still deploys a declarative project with guuey deploy. guuey agent apply works on both (see Agents as code).
In code mode, the worker bundle implements the Guuey Worker Protocol v1 — @guuey/worker on npm ships the contract types and the tiered worker SDK (typed interpreter + serve loop), and guuey worker verify checks a built guuey.worker.js for conformance before you deploy. The create-agentic-app scaffold above is a working code-mode starting point.
Code mode is Node today. The worker is built from a package.json whose build produces guuey.worker.js, and the worker SDK ships only on npm. Python is not a supported language for code-mode workers. A root Dockerfile doesn’t add another language: Guuey builds every worker image from its own base and never runs your Dockerfile. It does change the deploy, though. With a root Dockerfile and no agent.mode in guuey.json, deploy takes a legacy code path that skips the build step and the MCP and generative-UI steps. Set agent.mode to "code" in guuey.json, or pass --code, to stay on the worker path. From another language, the route that works is MCP: write your tools as an MCP server in any language, host it yourself, and connect it to your agent by URL as an external server (see Hosting).
With a CLI before 0.33.0, a code deploy replaces an app’s live no-code deployment in place. Creating an app in the console queues a no-code starter deployment. Once that starter is live, the first guuey deploy from a code-mode project replaces it without asking. Only the deployment changes: the app keeps its id, its endpoint host and its conversation threads. If the starter is still queued or deploying, the code deploy is refused: [CONFLICT] App <id> has a live nocode deployment; undeploy it before deploying as code (mode switches aren't supported in place yet). The message says to undeploy, but you don’t need to: wait for the starter to go live, then run guuey deploy again. If the first code build fails, the app stays up on its no-code build. Run guuey deploy again once the build is fixed. Once you deploy as code, the server refuses a no-code deploy while that code deployment is queued, in progress or live, and guuey agent apply is for declarative apps only (see Agents as code).
As of 0.33.0, only guuey eject makes that switch. guuey deploy on an app whose live build is no-code stops with App <id> is live on guuey's no-code runtime. A code deploy would switch it to code mode for good, so guuey deploy will not do it on its own. and names guuey eject --app-id <id> to switch, or guuey agent apply to stay no-code. guuey eject asks first, because the switch can’t be undone, then scaffolds a code project bound to the app, pulls its live definition into it and deploys it. Outside a terminal it needs --yes. See Switch to code mode for its question, its flags (--dir, --framework, --yes) and what changes after.
When nothing binds an app, deploy stops with a remedy for the mode you are in — set appId in guuey.json, pass --app-id <id>, or guuey pull --app-id <id> to bind an existing app; in code mode it adds “run guuey deploy in an interactive terminal to create one”, in declarative mode “guuey apps create --name <name> and put its id in guuey.json”. An app that deploy creates carries the manifest’s framework and model, and who it is for (app.builtFor, declared in guuey.json or answered at the prompt). After Live at <url>, a one-time Look: line prints when the app renders the Guuey theme (no chat theme of its own), naming the two write paths — the console’s Design → Chat theme, or app.theme in guuey.json, which guuey deploy (code apps) and guuey agent apply both apply; it never prints when the app has a theme. When guuey.json carries app.theme and the app has no chat theme yet, deploy writes it right after Live at and prints a Theme: line; an app that already has a theme is never overwritten by a deploy — the line names guuey apps update --chat-theme-file and the console as the doors that change it (a refused document prints the field and the door; the deploy itself still succeeds). The Look: line and the theme write need a @guuey/cli release after 0.31.1. In 0.31.1, deploy never writes app.theme, never prints the Look: line, and, when guuey.json sets app.theme, prints a Theme: line saying the app already has a chat theme even when it has none. On 0.31.1, set the theme with guuey apps update <appId> --chat-theme-file theme.json or in the console.
In code mode, deploy does not apply app.access from guuey.json. The auth mode, issuer binding, allowed domains and guest access you declare there are left as the app has them, and nothing in the output says so, so an app in your own sign-in mode can go live verifying tokens against the wrong issuer. Apply them yourself after the deploy: guuey apps update <appId> --auth-mode byo --issuer-url <issuer> --audience <audience> --domains <domains> sets the auth mode, issuer and domains, and guest access is the console’s switch for a workspace app (guuey apps access <appId> --guests on|off for a personal one). Don’t reach for guuey agent apply here: it is for declarative (no-code) apps, and on a code-mode app both it and its --dry-run are refused ([CONFLICT] App <id> is code mode: deploy it with `guuey deploy`. `guuey agent apply` is for declarative (no-code) apps.).
Force code mode with --code (a CLI before 0.33.0 also takes --declarative). Pick a runtime pod size with --size (xs | sm | md | lg | xl, default xs — see Plans & billing for what each size buys) and tag the version with --label. Code-mode builds run in a separate build job sized by --build-size (sm | md | lg | xl, default md). In code mode the deploy first checks for node_modules at the project root; without it the deploy installs for you (pnpm install, before anything is pushed) and then builds. Pass --no-install to stop instead — No node_modules in <root> — you passed --no-install; run "pnpm install" and re-run guuey deploy. (auto-install as of @guuey/cli 0.20.0 — there is no 0.19.1 on npm; the 0.19.0 --install flag is gone and a stale --install is silently ignored; the scaffold installs by default too, --no-install to skip).
Code-mode build requirements
Section titled “Code-mode build requirements”The code-mode build runs on your machine, not on Guuey’s side, and it always runs through pnpm, whatever package manager the project uses. deploy runs pnpm build with the pnpm on your PATH. With no pnpm there, it runs npx --yes pnpm@11.2.2 build instead (and npx --yes pnpm@11.2.2 install for the install step). The build must leave guuey.worker.js at the project root, or the file guuey.json names in worker; the deploy stops if it is missing, and uploads the project without node_modules once it is there.
pnpm 11 has three defaults that stop a project the scaffold didn’t create:
- Dependency build scripts are refused. An install fails with
ERR_PNPM_IGNORED_BUILDSwhen a dependency has a build script nobody allowed, andtsupbrings inesbuild, which has one. - Every
pnpm buildchecks the install first. If the project was installed with yarn or npm, that check runs a freshpnpm install, which then meets the build-script refusal above. - Very new versions are held back. pnpm 11 holds back package versions published less than a day ago (
minimumReleaseAge). A just-released SDK version can fail the install, or pnpm writes it into aminimumReleaseAgeExcludelist inpnpm-workspace.yaml.
A project created by create-agentic-app already carries settings for these. For any other project, including an agent folder inside a yarn or npm app, add a pnpm-workspace.yaml at the project root:
allowBuilds: esbuild: trueverifyDepsBeforeRun: falseallowBuilds lets esbuild’s build script run. verifyDepsBeforeRun: false lets pnpm build run over the node_modules you already have, so a yarn or npm install keeps working. If you need a version that was published less than a day ago, list the package under minimumReleaseAgeExclude in the same file, or wait a day.
Scaling and runtime updates
Section titled “Scaling and runtime updates”Two live knobs ride guuey deploy and also stand alone on guuey agent config, which reads and patches the hosted app without a redeploy (changes converge within ~5 minutes):
--max-pods <n>sets the app’s pod limit. Your plan sets the ceiling, and the server refuses more, naming the limit. How many pods run depends on the scaling mode below. With no scaling mode chosen, a hand-set count is stored as fixed. See Plans & billing.--runtime-auto-update on|off—on(today’s default) keeps a no-code agent on the platform’s current runtime image;offpins it at the image captured by each deploy. A pin holds against the automatic roll only: a deploy, size change or rollback still captures the current image (details).
A third knob stands on guuey agent config alone: --worker-reservation-mib <n> sets the per-turn worker memory reservation in MiB — the memory each concurrent conversation is given (platform default 256 MiB; your plan’s ceiling is on the readout and the server gates at it; the clamped value is decided at deploy time, and a live app takes it without a redeploy). --clear-worker-reservation returns to the platform default. The readout prints it as Worker memory: … — ceiling … MiB on this plan.
--scaling auto|fixed also stands on guuey agent config alone, and it sets what the pod limit means. Under auto, the default for an app that never hand-set a count, the app runs between one pod and the limit, and it may stay at one pod. A code-mode app stays at one pod under auto. Under fixed, the app runs exactly the limit. Setting --max-pods without ever choosing a mode lands fixed, and guuey agent config with no flags prints the mode the platform runs.
guuey agent config with no flags prints the app’s current hosting config; add --json for machine-readable output.
After deploying:
guuey test "hello" # send a test message, print the responseguuey logs --follow # live-tail runtime logsguuey deployments list # list deployment buildsguuey undeploy # tear down the deployment (keeps the app)guuey test talks to your agent as an anonymous visitor by default, and each run starts a new visitor and a new conversation. To continue one, pass the thread id the first turn printed: guuey test "and tomorrow?" --thread <id>. The CLI remembers which visitor started each thread on this machine (under ~/.guuey/test-threads/), so --thread continues only conversations guuey test started here. Add --app-id <id> to test another app without changing your guuey.json binding. (--thread and --app-id are as of @guuey/cli 0.27.0.)
To test as someone signed in, choose who the turn is sent as. As of @guuey/cli 0.34.0, --as-me sends it signed in as you, for an app on Guuey’s own sign-in. The CLI asks Guuey for a short-lived test token and keeps it in memory for that one run. It needs your own guuey login with full access: a read-only key, a workspace key or a service token can’t get a test token. To test as one of the app’s signed-in users, pipe that user’s token on stdin with --bearer-stdin (0.33.0 and later): the access token for an app on Guuey’s sign-in, or, for an app on your own sign-in, the ID token your identity provider issues for the app’s audience. Pass one of the two, not both. The CLI refuses a token passed on the command line, and --thread with the same user’s token continues their signed-in thread. An app on your own sign-in can’t get a test token, so --as-me tells you to use --bearer-stdin there.
Local dev
Section titled “Local dev”Run your built worker locally behind the same SSE endpoint the hosted pod serves:
guuey dev --serve --port 6790Hosted on Guuey, your agent connects out over TCP and IPv4 to public addresses only. It can’t reach a host inside your VPC, behind a VPN or on an office network. UDP isn’t available beyond the name lookups Guuey makes for it, and an IPv6-only host can’t be reached. guuey dev doesn’t apply these rules, so something that works here can time out once deployed. See hosted network rules.
This gives you pod-parity iteration: GET /readyz answers 200 while it is serving; 503 means it is draining or degraded (retry); a refused connection means it isn’t listening yet, and POST /agent/invoke streams against localhost before you deploy — the same two routes the hosted pod serves. (Plain guuey dev — a device bridge with a QR code — is coming soon; the CLI says so itself.)
Deploying from CI: service tokens
Section titled “Deploying from CI: service tokens”Your own login (guuey login) mints a user key — it belongs to you, and it stops working if you log out or rotate. A CI pipeline needs a credential that belongs to the app, not to a person:
guuey tokens create --label "github-actions" --app-id <appId>This mints an app-scoped service token (guuey_svc_*). The secret is printed once — store it as a CI secret and pass it through GUUEY_API_KEY. Service tokens don’t expire; guuey tokens revoke <tokenId> is the kill switch (auth stops within seconds), and guuey tokens list shows prefix, label, and lifecycle — never the secret.
A service token is deliberately narrow: it can call guuey agent apply / guuey agent status / guuey agent rollback (the reconcile routes) and read deployment history for its one app — nothing else. guuey deploy needs a user key. The CI verb is guuey agent apply — one idempotent call that converges the hosted agent to your checked-in guuey.json, never rolls the pod for unchanged bytes, and answers with content hashes your pipeline can gate on. See Agents as code for the manifest, the drift gate, and a GitHub Actions example.
Service tokens are for personal apps: an app owned by a workspace can’t mint one. A workspace-owned app authenticates CI with a workspace API key (guuey_wkz_*) instead, passed through the same GUUEY_API_KEY variable — see Workspace-owned apps.
Beyond the basics
Section titled “Beyond the basics”The rest of the surface, briefly:
- Apps —
guuey apps create|list|get|update|delete|recover|access|publish|unpublish.apps deletestarts a 30-day pending deletion;apps recovercancels it inside the window (the widget signing key comes back with it, but billing does not resume on its own).apps create --for personal|customersrecords who the app is for, andapps getshows it asBuilt for:.apps listandapps getshow an app’s trial status and end date — see the 7-day trial.apps accesssets the guest-chat policy (--guests on|off,--guest-limit), andapps publish|unpublishmanage the store listing.apps createalso reports the app’s first impression — the greeting and suggestion chips ggui prepares for a fresh app before anyone types, a paid-plan feature: on an entitled account it is requested at creation; on a free wallet the command prints one line saying so with a link to choose a plan (firstImpressionin--json, withentitledand, when refused, themessageandbillingUrl).apps listshows each app’s Status and hides archived apps unless you pass--all(--jsonfollows the same filter, so a script that relied on archived rows adds--all). Pass--issuer-urland--audiencewithapps update --auth-mode byo, because an app inbyowithout them refuses sign-ins from its own identity provider and, by default, pauses its standalone page and widget embed. See bring your own auth.apps update --chat-theme-file <path>sets the app’s chat theme from a theme JSON document (--clear-chat-themeremoves it) — the write goes through the same validator as the console’s Design → Chat theme editor, and a refusal names the offending key or token.apps get <appId>prints the theme’s name and light accent (the effective theme);apps get <appId> --theme-jsonprints the stored chat theme document as JSON — what a write actually put on the app,courtsincluded — and round-trips intoapps update --chat-theme-file. See Theme the hosted surfaces. - Custom domains and the slug —
guuey domains add|list|verify|removeserves your agent on your own domain (see Custom domains);guuey slug claim|releasemanages the free short name every plan gets. - Hosted MCP servers —
guuey mcp new <name>scaffolds a server,guuey mcp deploy|list|status|logs|deleteruns it on Guuey,guuey mcp secrets set|list|unsetmanages its secrets, andguuey mcp state list|export|wipeinspects or erases a user’s stored state. - Third-party MCP authorizations —
guuey mcp connections [revoke],guuey mcp connect <server> --app <id>— see Connecting third-party accounts. - Provider keys (BYOK) — add your own model key in the console (Behavior → Model, or the workspace Provider keys panel); that is the only way in today. A key set with
guuey env set ANTHROPIC_API_KEY=…is stored as an ordinary app variable, does not switch the app to your key, and is not passed to your agent’s code or your colocated MCP servers (details). A dedicatedguuey byokcommand is on the roadmap. See Credential custody. - Environment variables —
guuey env set|list|unset.guuey env setrefuses a few names that belong to the runtime, includingNODE_OPTIONS,NODE_EXTRA_CA_CERTS,NODE_TLS_REJECT_UNAUTHORIZED,NODE_PATH,OPENSSL_CONF,GUUEY_SANDBOX,GLIBC_TUNABLES, and the dynamic loader’s ownLD_*settings (LD_PRELOAD,LD_LIBRARY_PATH,LD_AUDIT,LD_BIND_NOW,LD_BIND_NOT,LD_DEBUG,LD_DEBUG_OUTPUT,LD_DYNAMIC_WEAK,LD_HWCAP_MASK,LD_ORIGIN_PATH,LD_PREFER_MAP_32BIT_EXEC,LD_PROFILE,LD_PROFILE_OUTPUT,LD_SHOW_AUXV,LD_TRACE_LOADED_OBJECTS,LD_USE_LOAD_BIAS,LD_VERBOSE,LD_WARN,LD_ASSUME_KERNEL). Other names that start withLD_, such asLD_SDK_KEY, are fine. The environment API answers a reserved name with a400(“… is reserved by the platform and cannot be set”). Names are matched exactly and are case-sensitive. - Billing —
guuey billingshows the plan per app, the saved card on file, and the account’s next invoice (total, issue date, per-agent shares) plus recent invoices;guuey billing invoice [--json]shows the next invoice alone.guuey billing topup --app <appId> --amount <usd>adds credits that pre-pay your next invoices: the amount is a whole-dollar figure from the account’s offered list (guuey billingprints it; any other amount is refused by name), the command prints the Checkout URL and opens it, and the balance is credited only once Stripe confirms that checkout — never at the command. When the account holds bonus credit,guuey billingprints that too (Bonus credit: $X — applied to usage lines on your next invoices, never the plan fee), and while a top-up promotion is open, the bonus percentage and its end date.guuey apps subscribe <appId> --plan starter|pro|scaleputs an app that has no paid plan on one, using your saved card, or prints and opens a checkout page when there is no usable card. Changing, cancelling or resuming a plan stays in the console. See Plans & billing.guuey billing movements [--json]lists the wallet’s credit movements newest first (the latest 50) — paid (credits purchased, applied to invoices) beside bonus (earned, applied to usage).guuey billing auto-rechargeshows the wallet’s auto-recharge setting;--on --amount <usd> --threshold <usd> --cap <usd>turns it on — the platform adds that amount of credit whenever the balance would fall under the threshold, never past the monthly cap — after printing the terms and asking you to typeyes(--yesanswers for scripts;--receipts on|offsets whether each recharge emails a receipt — leave it out to keep the current setting);--offturns it off. The console’s Billing page carries the same setting. - Usage export —
guuey usage export(as of@guuey/cli0.27.0) prints one month of an app’s usage, the figures its meters recorded: managed-LLM cost at the provider’s list price (in total and per model), input and output tokens, generative-UI renders, answers, sessions by the surface that served them, widget opens, pod unit-hours, and storage.--month YYYY-MMpicks the month (default: the current UTC month),--format csv|jsonthe shape (default: CSV, one row per metric),--out <file>writes it to a file. The export is per app per month and says so: every export lists what it cannot attribute (per-end-user splits, workspace-level MCP hosting, calls on your own provider key). The console’s Plan & usage page downloads the same CSV. - Housekeeping —
guuey config show|set|unsetfor CLI configuration (see Configuration and environment variables for how the layers resolve),guuey statusfor connectivity,guuey pullto refreshguuey.jsonfrom hosted state, andguuey worker verifyfor worker conformance.
Run guuey --help for the full surface, and guuey open dashboard to jump to the console.
Configuration and environment variables
Section titled “Configuration and environment variables”The CLI resolves its configuration through layers — the first layer that supplies a value wins:
- Environment variables — always win.
- A repo-local
amplify_outputs.json— picked up when your project sits inside a checkout that has one; most projects don’t, and then this layer simply isn’t there. ~/.guuey/config.json— user-level defaults, managed withguuey config show|set|unset.- Built-in production defaults — a fresh install talks to Guuey’s production API and console with zero setup.
Two environment variables cover the endpoints:
GUUEY_API_URL— the REST API base every API call targets. Keep the/v1suffix — route paths are appended to it.GUUEY_HOST— the platform console URL thatguuey loginopens andguuey open dashboardjumps to.
If you override one, override both together: an API key is minted by a console, and a key minted by one console does not authenticate against a different endpoint’s API (that mismatch surfaces as an authentication error — see Troubleshooting).
GUUEY_APP_ID overrides the configured app ID (see the deploy precedence above). As of @guuey/cli 0.19.0 this is the variable’s name — it was GGUI_APP_ID before, and there is no alias: scripts that exported GGUI_APP_ID must export GUUEY_APP_ID; the old name is ignored. (Breaking under 0.x, hence the 0.19.0 minor.)
Per-invocation credentials: GUUEY_API_KEY
Section titled “Per-invocation credentials: GUUEY_API_KEY”GUUEY_API_KEY overrides the stored login for every authenticated command — set it and that invocation runs as that key, no guuey login involved. It accepts any key the platform mints: your user key, a service token (guuey_svc_*), or a workspace API key (guuey_wkz_*).
Two properties make it the right tool for switching identities or endpoints:
- The stored login is a single slot.
guuey loginwrites one credential to~/.guuey/auth.json; it is not keyed by host, so logging in against a different endpoint replaces what was there.GUUEY_API_KEYsidesteps the slot entirely. --config <path>moves configuration only, never credentials. The flag relocatesconfig.json(non-secret defaults); the stored login stays where it is. Don’t reach for--configto isolate credentials — setGUUEY_API_KEYinstead.
GUUEY_API_KEY="$MY_CI_KEY" guuey agent statusTroubleshooting
Section titled “Troubleshooting”- A deploy is refused with
TRIAL_EXPIRED— the app’s 7-day trial ended without a plan, so the app is paused. Choosing a plan resumes it automatically, no redeploy needed — see Plans and the 7-day trial. - Requests fail with an authentication error — check
guuey whoami(who you are) andguuey status(connectivity); a key minted against one environment does not work against another’s API URL.