This is an archived copy of the SandyWP docs. It is kept online as a fallback and may be out of date. The maintained documentation lives at docs.sandywp.com.

CLI

The sandywp CLI is a thin, dependency-free client over the SandyWP API. Authenticate once, then create and manage sandboxes from your terminal.

Installation

# Install globally
npm install -g @sandywp/cli

# …or run without installing
npx @sandywp/cli <command>

Requires Node.js 18+.

Authentication

sandywp auth login uses a browser flow (like gh auth login): it opens SandyWP, you approve the device, and a personal API token is saved to ~/.sandywp/config.json.

sandywp auth login
# → opens your browser
# → click "Authorize this device"
# → ✓ Logged in to https://app.sandywp.com as [email protected]

For servers or CI where no browser is available, pass a token directly:

# CI / headless: skip the browser and pass a token directly
sandywp auth login --token swp_xxxxxxxxxxxxxxxxxxxx

sandywp auth logout revokes the stored token on the server and removes the local config.

Commands

CommandDescription
sandywp auth loginLog in via browser (or --token for headless)
sandywp auth logoutRevoke this device's token and log out
sandywp add [name]Create a sandbox and wait until it's ready
sandywp listList your sandboxes
sandywp statusShow your account, plan, and usage
sandywp login <name>Print a one-time login URL for a sandbox
sandywp pushPush a local WordPress install into a sandbox (run from its folder)
sandywp deploy <name> <plugin.zip>Install and activate a plugin ZIP on a sandbox
sandywp ssh <name>Open a shell in a sandbox (or run one command with --cmd)
sandywp mount <name>Mount a sandbox's files at ~/SandyWP/<name>
sandywp unmount <name>Unmount a sandbox (or --all) and clean up its key
sandywp mountsList active mounts, flagging any that have gone dead
sandywp delete <name>Delete a sandbox by slug
sandywp blueprint list|get|create|update|deleteManage your Blueprints (see below)
sandywp blueprint import|validateFetch a Blueprint from a URL, or validate/adapt one
sandywp blueprint bake <name> [--wait]Bake a Blueprint into a Template
sandywp blueprint runs <name>List (or show) a Blueprint's runs
sandywp template list|get|deleteManage your Templates
sandywp template launch <name> <action>Enable/disable/rotate/update a Template's public launch link

add

Creates a sandbox. The name is optional leave it blank for an auto-generated one. The command waits for provisioning and shows live stages; pass --no-wait to return immediately.

$ sandywp add my-site
Creating sandbox "my-site" https://my-site.sandywp.dev
⠹ Installing WordPress · 9s
✓ Sandbox "my-site" is ready
  url:    https://my-site.sandywp.dev
  status: ready

list

$ sandywp list
SLUG       STATUS  URL
my-site    ready   https://my-site.sandywp.dev
demo       ready   https://demo.sandywp.dev

status

$ sandywp status
Account:   [email protected]
Plan:      free
Sandboxes: 2 / 2 active (0 remaining)

login

Prints a one-time magic-login URL that opens the sandbox already signed in to wp-admin. Add --open to launch it in your browser automatically.

sandywp login my-site --open

deploy

Uploads a plugin ZIP from your machine, then installs and activates it on the sandbox — the fastest way to test a build without touching wp-admin. Pass --no-wait to return as soon as the job is queued instead of waiting for it to finish.

sandywp deploy my-site ./dist/my-plugin.zip

push

Takes the local WordPress install you run it from and mirrors it into a SandyWP sandbox — database and wp-content so a site you're building in Cove, LocalWP, MAMP, or XAMPP goes live on a public, magic-login URL. Run it from anywhere inside the install; it walks up from the current directory to find wp-config.php.

$ cd ~/Sites/my-local-site   # a local WordPress install
$ sandywp push
Found WordPress install at /Users/you/Sites/my-local-site
Exporting database...
Packaging wp-content...
Built archive (48.2 MB).
Restoring your site...
✓ Pushed to "my-local-site".
  url:    https://my-local-site.sandywp.dev
  login:  https://my-local-site.sandywp.dev/…
  Wrote /Users/you/Sites/my-local-site/.sandywp
  Tip: add `.sandywp` to your .gitignore it links this folder to a SandyWP site.

For a step-by-step walkthrough (including troubleshooting), see Push a local site.

First push vs. repush

The first push creates a brand-new sandbox and writes a .sandywp link file next to wp-config.php. Every later push from the same folder syncs into that same linked sandbox in place the URL never changes and the site stays online. Because it overwrites the sandbox's database and files, push asks for confirmation first; pass --force to skip the prompt (useful in scripts).

$ sandywp push
Found WordPress install at /Users/you/Sites/my-local-site
This will OVERWRITE my-local-site's database and files. Continue? [y/N] y
Exporting database...
Packaging wp-content...
Syncing into your existing site...
✓ Pushed to "my-local-site".
  url:    https://my-local-site.sandywp.dev

A push is atomic: if it fails, the existing sandbox is left exactly as it was. If the linked sandbox has been deleted on SandyWP since the last push, push offers to create a fresh one and re-link the folder.

The .sandywp link file

The .sandywp file records which sandbox this folder pushes to (its site id, slug, and URL). Keep it to keep repushing to the same site; delete it to unlink and have the next push create a new sandbox. Add .sandywp to your .gitignore it's specific to your machine, not something to commit.

Requirements & export

Your local database must be running when you push. push exports it with wp-cli when wp is on your PATH; otherwise it falls back to a bundled PHP exporter that reads wp-config.php directly (which works with socket-based stacks like LocalWP). You need either wp or php available with neither, the push can't export the database.

Subdomain multisite is not supported. Regular single-site installs and subdirectory multisite networks push fine. push is one-way local → SandyWP so changes you make directly on the sandbox are overwritten by the next push.

SSH & mounting files

ssh and mount give you a shell inside a sandbox and its files on your local disk with no config, no key management, and no rclone config. Each run turns on SSH for the sandbox, mints a short-lived key that's returned once (never stored on SandyWP), and cleans it up afterwards.

ssh

Opens an interactive shell in the sandbox. Add --cmd "<command>" to run a single command and return instead of opening a shell. Needs only OpenSSH, which ships with macOS and Linux nothing to install.

$ sandywp ssh my-site
# → a shell inside the sandbox, nothing to configure
www-data@sandywp:/var/www/html$ wp core version
6.5.3
$ sandywp ssh my-site --cmd "wp plugin list --status=active"

mount

Mounts the sandbox's WordPress directory at ~/SandyWP/<name> (override with --path) so you can browse and edit its files in Finder or your editor. The mount key lasts up to 24 hours (or the sandbox's remaining lifetime, whichever is shorter) and is not renewed a mount left up past that will fail on rclone's next reconnect. sandywp mounts flags such a mount as KEY EXPIRED; renew it by remounting (sandywp unmount <name> && sandywp mount <name>). Mounting needs rclone and a FUSE provider. On macOS the CLI manages its own official rclone in ~/.sandywp/bin a Homebrew-built rclone works for everything except mount, which it refuses on macOS downloading it on first mount with your confirmation; on Linux it uses the system rclone. For FUSE, install fuse-t (brew install --cask fuse-t) or macFUSE. If FUSE isn't an option, sandywp ssh is the zero-dependency alternative.

$ sandywp mount my-site
✓ "my-site" mounted at /Users/you/SandyWP/my-site
  edit:    code /Users/you/SandyWP/my-site
  unmount: sandywp unmount my-site

For the full walkthrough editing live with an AI agent, requirements per OS, the mount lifecycle, and troubleshooting see Mount a sandbox.

unmount & mounts

sandywp mounts lists active mounts and checks each against the OS mount table, flagging any that have gone DEAD (rclone exited on sleep or a network change) or KEY EXPIRED (still mounted, but the key is past its ceiling and will drop on the next reconnect). sandywp unmount <name> unmounts a sandbox, deletes its key, and removes the empty mountpoint; --all unmounts everything. Unmount still cleans up mounts whose sandbox has since expired or been deleted.

$ sandywp mounts
SLUG     STATE    MOUNTPOINT
my-site  mounted  /Users/you/SandyWP/my-site
old      DEAD     /Users/you/SandyWP/old
Ephemeral keys expire on their own, but a mount keeps a private key in a 0600 file under ~/.sandywp/keys until you unmount the same trust level as the ~/.ssh/id_* keys the manual flow would use anyway.

delete

Deletes a sandbox by its slug (the value shown under SLUG in list).

sandywp delete my-site

Blueprints & Templates

Every Blueprint and Template operation the API exposes has a CLI command a Blueprint or Template is addressed by its SLUG or id, the same as a sandbox.

blueprint list / get

$ sandywp blueprint list
SLUG           NAME           REV  VALID
plugin-recipe  Plugin recipe  1    yes

sandywp blueprint get <name> prints its revision, schema version, validity, and any compatibility findings.

blueprint create / update / delete

Creates or updates a Blueprint from a JSON file, or from stdin when you omit the file (so curl … | sandywp blueprint create "My BP" works). update takes the same optional file plus --name/--description only the fields you pass change.

$ sandywp blueprint create "Plugin recipe" blueprint.json --description "Client starter"
✓ Created Blueprint "plugin-recipe" (id bp_abc123)
sandywp blueprint update plugin-recipe blueprint-v2.json
sandywp blueprint delete plugin-recipe

blueprint import / validate

import fetches a Blueprint from a URL. Without --save it prints the raw JSON to stdout (so you can redirect it to a file or pipe it into validate); with --save "Name" it saves the Blueprint immediately.

$ sandywp blueprint import https://raw.githubusercontent.com/example/project/main/blueprint.json --save "Imported recipe"
✓ Created Blueprint "imported-recipe" (id bp_def456)

validate checks a local file (or stdin) against SandyWP's compatibility rules and exits non-zero when it's invalid handy as a CI gate. Add --adapt to print an adapted, SandyWP-compatible document to stdout instead (with --source-url to resolve bundled sibling files during the rewrite).

$ sandywp blueprint validate blueprint.json
✓ Valid Blueprint (schema v2, 3 finding(s))
  [warning] $.landingPage unsupported_field: …

blueprint bake

Bakes a Blueprint into a Template (async on the server). Pass --wait to poll until the bake finishes the primary way to use this in CI. --template-id <id> rebakes an existing Blueprint-backed Template in place, keeping its slug and public launch URL stable across runs.

$ sandywp blueprint bake plugin-recipe --wait
Baking Blueprint "plugin-recipe" into Template "plugin-recipe"...
✓ Baked Template "plugin-recipe" (status ready)
  launch: https://app.sandywp.com/launch/dLN_rizhL4o-xHrh1jAAMfwu_DJATEiu

blueprint runs

Lists a Blueprint's runs (create/apply/bake attempts); add --run <runId> to show one run's steps and log.

$ sandywp blueprint runs plugin-recipe
RUN ID                    MODE  STATUS     STARTED                   FINISHED
bpr_cc60384fa4011cd5f55d  bake  succeeded  2026-07-01T10:00:01.000Z  2026-07-01T10:00:04.000Z

template list / get / delete

$ sandywp template list
SLUG           NAME           STATUS  WP      PHP
plugin-recipe  Plugin recipe  ready   latest  8.3

sandywp template get <name> also prints its public launch URL when one is enabled.

template launch

Enables, disables, rotates, or updates a Template's public "launch this demo" link. enable/rotate require a paid plan; update takes --quota-mode, --entry-mode, and/or --landing-path.

$ sandywp template launch plugin-recipe enable
✓ Launch link enabled for "plugin-recipe".
  url: https://app.sandywp.com/launch/dLN_rizhL4o-xHrh1jAAMfwu_DJATEiu
Two Template operations have no dedicated CLI command yet: snapshotting a live sandbox into a new Template, and creating a sandbox from a Template. Both are one curl call away see the API reference.

Workspaces

sandywp workspace list lists every Workspace you belong to. sandywp workspace use <id-or-slug> changes the saved default.

sandywp workspace list
sandywp workspace use acme

Flags

FlagApplies toDescription
--base-url <url>auth loginTarget a non-default API host (defaults to https://app.sandywp.com)
--token <token>auth loginLog in headlessly with an existing token
--no-browserauth loginPrint the authorize URL instead of opening a browser
--no-waitaddReturn immediately instead of waiting for the sandbox to be ready
--forcepushSkip the overwrite confirmation (and auto-recreate a deleted linked site)
--openlogin, pushOpen the login URL in your browser
--cmd <command>sshRun one command and exit instead of opening a shell
--path <dir>mountMount at a specific directory instead of ~/SandyWP/<name>
--allunmountUnmount every active mount
--workspace <id-or-slug> / --org <id-or-slug>GlobalRun this command against a specific Workspace without changing the saved default

Configuration

Credentials are stored at ~/.sandywp/config.json (file permissions 0600):

{
  "baseUrl": "https://app.sandywp.com",
  "token": "swp_xxxxxxxxxxxxxxxxxxxx"
}
Need to automate against the same endpoints? See the API reference — the CLI is just a wrapper around it.