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.
pywa login
Section titled “pywa login”pywa login [--api-url <url>] # real account: opens a device-code flowpywa 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.
pywa logout
Section titled “pywa logout”Forgets the stored login. (equivalent to pywa cloud logout)
pywa whoami
Section titled “pywa whoami”Shows the identity the current login resolves to, and which API URL it’s pointed at. (equivalent
to pywa cloud whoami)
pywa cloud bots create <name> --slug <slug> [--entrypoint-override <module:var>] [--bot-directory <path>]pywa cloud bots listpywa 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.
Environment variables & secrets
Section titled “Environment variables & secrets”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.
Scaffolding a project
Section titled “Scaffolding a project”There’s no scaffolding command in pywa-cloud-cli itself — use pywa’s own project generator,
with --cloud so the result is actually deployable:
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.
Linking a directory
Section titled “Linking a directory”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.
Deploying
Section titled “Deploying”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.
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.
Lifecycle
Section titled “Lifecycle”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.
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.
Connecting WhatsApp
Section titled “Connecting WhatsApp”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.
Database integrations
Section titled “Database integrations”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.
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.
Local development
Section titled “Local development”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.).
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.
Deploy tokens (CI)
Section titled “Deploy tokens (CI)”pywa cloud deploy-tokens create <bot-id> <name> [--expires-in-days <n>] # n: 1-365, default 90pywa 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:
export PYWA_CLOUD_TOKEN=<the deploy token>export PYWA_CLOUD_BOT_ID=<bot-id>pywa deployThis 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.
CI setup
Section titled “CI setup”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.
Not built yet
Section titled “Not built yet”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.