Skip to content

CLI Reference

There is no standalone pywa-cloud executable. The pywa-cloud-cli package declares only a cloud subcommand, which pywa’s own CLI mounts automatically whenever both packages are installed — so every command below is reached as pywa cloud <command>. A handful of the most common ones also get a top-level shortcut directly on pywa: pywa login, pywa logout, pywa whoami, pywa deploy — shown below wherever they apply. Run pywa cloud --help or pywa cloud <command> --help (or, for a shortcut, pywa <command> --help) for the exact same information this page describes, straight from the source.

Terminal window
pywa login [--api-url <url>] # real account: opens a device-code flow
pywa login --token <token> [--api-url <url>] # CI / scripted login

(equivalent to pywa cloud login)

With no --token, login starts a real OAuth2 device-code flow (RFC 8628-flavored): it prints a short code and opens the control plane’s confirmation page in your browser, where you sign in with your account email/password (there’s no separate signup command yet — sign up through the control plane’s own /auth/signup API, or ask whoever runs it to invite you to a team) and approve the code; the CLI polls until you do, or the code expires after 10 minutes. --token <token> skips all of that and logs in directly with an existing token — the CI path, and also how you’d log in with a deploy token’s owner-equivalent credential in a script.

Either way, the resulting token (and API URL) is stored at an OS-standard config location — never in the current project directory:

  • macOS: ~/Library/Application Support/pywa-cloud/auth.json
  • Linux: $XDG_CONFIG_HOME/pywa-cloud/auth.json (default ~/.config/...)
  • Windows: %APPDATA%\pywa-cloud\auth.json

$PYWA_CLOUD_CLI_CONFIG_DIR overrides this entirely — useful for running more than one isolated login on one machine. For actual CI deploys, a deploy token is what you want instead of this personal login (see setup-ci below) — PYWA_CLOUD_TOKEN/PYWA_CLOUD_BOT_ID bypass the stored config file entirely.

Forgets the stored login. (equivalent to pywa cloud logout)

Shows the identity the current login resolves to, and which API URL it’s pointed at. (equivalent to pywa cloud whoami)

Terminal window
pywa cloud bots create <name> --slug <slug> [--entrypoint-override <module:var>] [--bot-directory <path>]
pywa cloud bots list
pywa cloud bots get <bot-id>
pywa cloud bots delete <bot-id>
pywa cloud bots set-entrypoint <bot-id> [<module:var>]
pywa cloud bots set-directory <bot-id> [<path>]

--slug must be lowercase alphanumeric with internal hyphens only (e.g. my-bot) and unique across the control plane. Deleting a bot also stops its running container, if any. set-entrypoint sets or (called with no module:var) clears the bot’s entrypoint override — see Deploying a Pywa Bot for when you’d need one. set-directory sets or (called with no path) clears the bot’s application directory, for deploying one member of a monorepo — see the same guide’s “Monorepo support” section.

Terminal window
pywa cloud env set <bot-id> <key> <value> [--secret]
pywa cloud env get <bot-id> <key>
pywa cloud env list <bot-id>
pywa cloud env delete <bot-id> <key>

See Environment Variables & Secrets for the full semantics — set is create-or-update, and --secret is fixed at creation.

There’s no scaffolding command in pywa-cloud-cli itself — use pywa’s own project generator, with --cloud so the result is actually deployable:

Terminal window
pywa new --cloud [--async] [-o <directory>]

This writes main.py (credentials read from PYWA_PHONE_ID/PYWA_TOKEN/PYWA_VERIFY_TOKEN, no hardcoded values, no ngrok) plus a real pyproject.toml and .gitignore — ready for pywa deploy . as-is. Plain pywa new (no --cloud) is pywa’s own default scaffold instead: hardcoded placeholder credentials and a local ngrok tunnel, meant for editing by hand and running locally — not deployable without changes. See pywa new --help (or pywa’s own docs) for the full set of options. Deploying a Pywa Bot covers what a project needs to be deployable if you’d rather write one from scratch or adapt an existing project.

Terminal window
pywa cloud link <bot-id> [path]
pywa cloud unlink [path]

path defaults to the current directory. See Deploying a Pywa Bot for how this interacts with deploy.

Terminal window
pywa deploy [path] [--bot <bot-id>]

(equivalent to pywa cloud deploy)

--bot is required on a directory’s first deploy (and links it); later deploys in the same directory reuse the link. See Deploying a Pywa Bot for what actually happens and what’s excluded from the upload.

Terminal window
pywa cloud deployments list [--bot <bot-id>]
pywa cloud deployments get <deployment-id> [--bot <bot-id>]
pywa cloud deployments log <deployment-id> [--bot <bot-id>]
pywa cloud deployments rollback <deployment-id> [--bot <bot-id>]

deployments list shows past deployments for a bot (status, Python version, port, timestamp). deployments get fetches full metadata for a specific deployment attempt. deployments rollback re-runs a previous successful deployment’s already-built container image directly — no re-upload, no rebuild — using the bot’s current env vars (so a secret rotated since then still applies), and records a new deployment rather than mutating the old one. Requires the Admin/Owner team role. deployments log (alias logs) views the deployment’s full build and pre-run discovery validation log.

Terminal window
pywa cloud status [--bot <bot-id>]
pywa cloud stop [--bot <bot-id>]
pywa cloud start [--bot <bot-id>]
pywa cloud restart [--bot <bot-id>]

See Managing a Running Bot for what each one actually does, and what Docker’s --restart unless-stopped policy does and doesn’t recover from automatically.

Terminal window
pywa cloud health [--bot <bot-id>]
pywa cloud metrics [--bot <bot-id>]

health does a real HTTP request into the container (an unauthenticated GET, expecting pywa’s own webhook challenge handler to answer 403) — stronger proof of life than status, which only reflects whether Docker’s process is running, not whether it’s actually serving traffic correctly. metrics shows live CPU/memory usage (docker stats) next to the platform’s flat resource limits, plus request-volume/latency stats (total count, counts by status code, p50/p95/p99) for traffic the ingress has actually proxied to this bot.

Terminal window
pywa cloud connect [--bot <bot-id>]
pywa cloud connect-status [--bot <bot-id>]
pywa cloud waba list [--bot <bot-id>]
pywa cloud waba connect-manual <waba-id> <business-token> [--app-secret <secret>] [--bot <bot-id>]
pywa cloud whatsapp assign <connection-id> <phone-id> [--bot <bot-id>]
pywa cloud whatsapp disconnect [--bot <bot-id>]

See Connecting a WhatsApp Number. A WABA connection is a team-level resource, shared across every bot on the team - connect is the interactive entry point: it starts (or reuses) a team’s Meta Embedded Signup connection, then walks you through picking one of its phone numbers for the given bot; the Embedded Signup link itself is single-use and expires after 15 minutes. connect-status shows whether (and to which WABA/phone) the bot is currently connected, including a warning if Meta’s own account_update webhook has since flagged the underlying WABA as disconnected/restricted/banned. waba list/waba connect-manual and whatsapp assign/disconnect are the non-interactive equivalents - list a team’s WABA connections, connect one directly with a token from your own separate Meta App, or assign/clear a bot’s phone number by id without the interactive picker.

Terminal window
pywa cloud integrations list [--bot <bot-id>]
pywa cloud integrations connect <provider> [--bot <bot-id>]

Curated OAuth2-PKCE connectors to external database providers — Pywa Cloud doesn’t host a database itself, this just makes connecting an existing provider’s account less annoying. integrations list shows which providers are available; integrations connect neon (or connect supabase) opens a browser to the provider’s own consent screen and, once authorized, auto-provisions a Postgres database and writes its connection string as this bot’s DATABASE_URL secret — no copy-pasting a connection string by hand. neon and supabase today; the link is single-use and expires after 15 minutes. Doesn’t trigger a redeploy on its own — your next real deploy is what picks the new secret up.

Terminal window
pywa cloud logs [--bot <bot-id>] [--tail <n>] [--since <timestamp>] [--follow] [--interval <seconds>]

See Logs. --tail defaults to 200 lines; --since (RFC3339, e.g. 2024-01-01T00:00:00Z) is a one-shot filter and is ignored together with --tail when combined with --follow, which manages its own cursor; --follow polls every --interval seconds (default 2.0) until Ctrl+C.

Terminal window
pywa dev --tunnel [--bot <bot-id>]

The usual way to develop against real Meta webhooks: pywa dev (pywa’s own local dev server) with --tunnel also relays this bot’s inbound webhook traffic here instead of to its deployed container, so you don’t need a separate tunnel tool or a repointed callback URL. --bot picks which Pywa Cloud bot to relay from (defaults to the current directory’s linked bot). Note that --tunnel only relays webhook traffic — it does not fetch or inject the bot’s real WhatsApp credentials into your environment; pywa dev still runs against whatever PYWA_TOKEN/ PYWA_PHONE_ID/etc. your own project already provides (a local .env, hardcoded values, etc.).

Terminal window
pywa cloud tunnel [--port <local-port>] [--bot <bot-id>]

The same relay as a standalone, foreground command — for pairing with anything other than pywa dev (a different framework runner, a manual process, etc.). --port (default 8000) is the port your own bot process is listening on. Opt-in and explicit either way: only active while the command is running; Ctrl+C (or any disconnect) hands traffic straight back to the deployed container.

Terminal window
pywa cloud deploy-tokens create <bot-id> <name> [--expires-in-days <n>] # n: 1-365, default 90
pywa cloud deploy-tokens list <bot-id>
pywa cloud deploy-tokens delete <bot-id> <token-id>
pywa cloud deploy-tokens regenerate <bot-id> <token-id> [--expires-in-days <n>]

A deploy token is scoped to exactly one bot and can only be used for deploy — nothing else. The token value is shown once, at creation (or regeneration), and never again; losing it means regenerate, not looking it up. For CI, skip pywa login entirely:

Terminal window
export PYWA_CLOUD_TOKEN=<the deploy token>
export PYWA_CLOUD_BOT_ID=<bot-id>
pywa deploy

This bypasses the stored personal login file completely — nothing CI runs ever touches it. PYWA_CLOUD_API_URL overrides the control plane URL the same way; --bot on deploy itself works too, instead of PYWA_CLOUD_BOT_ID.

Terminal window
pywa cloud setup-ci [--bot <bot-id>] [--branch <name>] [--force]

Scaffolds .github/workflows/deploy.yml in the current project, wiring up the deploy-token flow above as an automated on-push deploy. Reads repository secrets PYWA_CLOUD_TOKEN, PYWA_CLOUD_BOT_ID, and PYWA_CLOUD_API_URL (there’s no fixed Pywa Cloud API endpoint yet, so this one has to come from you). --branch defaults to whatever branch the current project’s own git checkout is on; --force overwrites an existing workflow file. The generated workflow installs both pywa and pywa-cloud-cli (pywa alone doesn’t pull the cloud package in) and runs pywa cloud deploy.

Billing/usage metering — a business decision that hasn’t been made yet, not just an unbuilt command. Teams, roles, invites, and account management (login/logout/whoami above already cover real multi-user JWT auth) are implemented — see Accounts, Teams & Permissions for the full account-management surface, which doesn’t have CLI commands of its own yet since it’s mostly one-time setup done through the API directly.