v2.8.0: webflow apps, the canonical namespace for Webflow Cloud

v2.8.0 introduces webflow apps, the new canonical command namespace for managing Webflow Cloud apps. It replaces the piecemeal webflow cloud commands with one consistent surface across the whole app lifecycle, designed so a CI job or an AI agent can drive it end-to-end without scraping human-oriented output: structured --json on read commands, --dry-run previews on every write, and predictable exit codes throughout. Reads also retry transient rate limits (429) automatically with bounded backoff, so a busy CI runner doesn’t need its own retry loop.

webflow cloud init and webflow cloud deploy keep working unchanged — they now print a one-time deprecation notice pointing at the apps equivalents.

An app is standalone by default — it gets its own subdomain and isn’t tied to a Webflow site. Attaching to an existing site (mounted at a path, alongside a Designer-built site) is fully supported too; it’s just one additional flag away.

App lifecycle: apps init / apps deploy

# Standalone — the default. Owns its own subdomain, no Webflow site involved.
webflow apps init -n my-app -f astro --new -w <workspaceId>
webflow apps deploy -e main

Once webflow.json has an app_id, subsequent apps deploy calls need no flags at all — it’s the standalone path that gets simpler over time, since there’s no site or mount path to keep in sync.

apps deploy also takes --dry-run and --json, same as every other write in this namespace:

webflow apps deploy -e main --dry-run
webflow apps deploy -e main --json

--dry-run resolves everything from flags, WEBFLOW_APP_ID, and webflow.json — no network calls, no prompts, no changes — and reports which of create / deploy / select the real run would take. --json suppresses the build/upload progress output and emits a single document: {appId, environmentId, deploymentId, deployUrl} on success, an error document on failure, or the dry-run plan when combined with --dry-run.

Attaching to an existing Webflow site instead just adds --site-id and a --mount path at init:

# Site-attached — mounted at a path on an existing Webflow site.
webflow apps init -n my-app -f astro -s <siteId> -m /app
webflow apps deploy -e main --site-id <siteId> --mount /app

apps init also supports creating an app directly from an existing GitHub repository, either standalone or attached to a site:

webflow apps init --import https://github.com/<owner>/<repo> --new --dry-run
webflow apps init --import https://github.com/<owner>/<repo> --site-id <siteId> --mount /app --dry-run
webflow apps init --import https://github.com/<owner>/<repo> --new --skip-clone
webflow apps init --import https://github.com/<owner>/<repo> --branch staging --new --idempotency-key $GITHUB_RUN_ID-$REPOSITORY

The Webflow GitHub App must be installed on the repository and connected to your workspace. --dry-run previews the app that would be created; --branch picks which branch to build (defaults to the repository’s default branch); --skip-clone creates the app from the remote repository without cloning it into a local directory. --idempotency-key is required for non-interactive runs (CI, --no-input) — reuse a retry-stable key unique to the repository and target so a retry replays the original app instead of creating a second one.

webflow apps link <appId> --workspace-id <workspaceId> --dry-run

Points the current directory at an app and environment that already exist by writing the resolved IDs into webflow.json. It only verifies the app — it doesn’t create or deploy anything, so it’s the fastest way to get a checked-out repo working with apps deploy / apps env-vars / apps logs against an app someone else created. A workspace ID is persisted only when --workspace-id is passed explicitly, since it can’t be derived or verified from the app itself. Use --dry-run to preview.

Inspect apps: apps list / apps get / apps domains

webflow apps list --json
webflow apps get <appId> --fields id,name,siteId,sourceUrl --json
webflow apps domains <appId>

list returns every app the token can see — standalone and site-attached alike — and optionally narrows with an exact --site or --name, or a case-insensitive substring --q; combine --site and --name to resolve to at most one app. All three support --fields to project columns and --json for machine-readable output.

Manage apps: apps update / apps delete

webflow apps update <appId> --name "New name" --description "Internal tools dashboard" --dry-run --json
webflow apps update <appId> --github-source https://github.com/<owner>/<repo> --json
webflow apps delete <appId> --dry-run --json
webflow apps delete <appId> --yes --json

Update an app’s name, description, and/or GitHub source (at least one of --name / --description / --github-source is required). --github-source attaches a repository to an app that has none, or repoints one that already has a different repo — it doesn’t deploy on its own, so follow it with apps deployments trigger. apps delete deletes (archives) an app outright; it refuses to run non-interactively without --yes (a safe --dry-run preview is exempt).

Both commands preview with --dry-run before making any change, and both support --json on every path:

  • update --json prints the plan (only the keys actually being changed, plus dryRun: true) on a dry run, and the full updated app object on a real run.
  • delete --json prints one document shape on both the dry run and the real run — { appId, name, action: "archive" | "hard_delete", siteId, siteName, permanent, dryRun, message } — so a caller can parse the same fields either way; message is null until the delete actually happens.

Environments: apps environments list/create/update/delete

webflow apps environments list <appId> --branch main --json
webflow apps environments create <appId> --branch feature/checkout --mount /checkout --dry-run
webflow apps environments update <appId> <envId> --branch main --dry-run
webflow apps environments delete <appId> <envId> --dry-run

list filters by an exact --branch or a substring --q on branch name. create provisions a new environment bound to a branch and served at a mount path — it starts empty; run apps deployments trigger to build it. update changes an environment’s branch and/or mount (at least one is required) but doesn’t deploy the change itself. delete removes an environment. All three writes support --dry-run, and resolve the app/environment from positional args, WEBFLOW_APP_ID / WEBFLOW_APP_ENVIRONMENT_ID, webflow.json, or an interactive picker.

Deployments: apps deployments list/get/redeploy/trigger

webflow apps deployments list --status failed --json
webflow apps deployments get <deploymentId> --wait --interval 10 --timeout 300 --json
webflow apps deployments trigger --wait --json
webflow apps deployments redeploy <deploymentId> --idempotency-key $GITHUB_RUN_ID --wait --json

list --status filters on an exact status (success, failed, starting, building, deploying, canceled, unstaged). get --wait polls until the deployment reaches a terminal status and exits 0/1 accordingly — built for CI gating; --interval and --timeout tune the poll (floored at 5s, capped at 30min).

trigger builds a GitHub-connected environment’s current HEAD on demand; redeploy re-runs a past deployment at the same commit, the rollback path. Both require a GitHub-connected app (not eligible for apps deployed from local files via apps deploy) and now also take --wait (with the same --interval/--timeout): since enqueuing a build only returns a 202 with no deployment ID to poll, --wait identifies the new deployment itself and then blocks on it exactly like deployments get --wait — so a CI job can trigger a build and gate on its outcome in one command. All three writes support --dry-run.

Logs: apps logs build / apps logs runtime

webflow apps logs build <deploymentId> --since <iso> --q <text>
webflow apps logs runtime [envId] --limit 200 --json

Both support --fields, --json, and cursor-based pagination via --cursor.

Environment variables: apps env-vars list/set/delete/import

The headline addition this release: full, non-interactive management of Cloud app environment variables, so secrets no longer require a trip to the dashboard.

webflow apps env-vars list --fields key,isSecret --json
webflow apps env-vars list --q API --json
printf %s "$VALUE" | webflow apps env-vars set API_KEY --secret
webflow apps env-vars delete OLD_KEY --dry-run
webflow apps env-vars import .env --secret --dry-run
  • --app-id / --environment-id default from webflow.json (cloud.app_id / cloud.environment_id) when present — as shown above — so most invocations inside a project directory need neither flag. Pass them explicitly (--app-id <id> --environment-id <id>) to target a different app, for example from a shared CI runner.
  • list also takes an exact --key or a case-insensitive substring --q, same filtering pattern as apps list and apps environments list.
  • set <key> [value] takes the value three ways, safest first: piped stdin (shown above — works in CI with no TTY), an interactive prompt (masked for secrets) if you omit the value entirely, or a positional argument. A positional secret still works but prints a warning, since it lands in your shell history and process list — stdin or the prompt avoid that.
  • --secret marks a variable encrypted. Secret values are never echoed backset on a secret confirms only that it was written, and list never returns a secret’s value — it comes back masked. Non-secret values are hidden from the default output too, and shown only if you ask for them with --fields value or --json.
  • import <file> bulk-loads a .env (or any KEY=value) file in one call instead of setting variables one by one. --dry-run parses and previews the resulting key set — secret values shown redacted — without making any API calls or writing anything.
  • Every write command (set, delete, import) supports --dry-run and --json.

Also in this release

  • apps init --site-id <id> --no-input now correctly attaches the new app to the existing site and runs the DevLink export, instead of silently falling back to a standalone app without one.
  • The publicUrl field on an environment (apps environments list/create/update) replaces the old deployUrl field name.

Upgrading

npm install -g @webflow/webflow-cli@latest