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
| Command | Description |
|---|---|
sandywp auth login | Log in via browser (or --token for headless) |
sandywp auth logout | Revoke this device's token and log out |
sandywp add [name] | Create a sandbox and wait until it's ready |
sandywp list | List your sandboxes |
sandywp status | Show your account, plan, and usage |
sandywp login <name> | Print a one-time login URL for a sandbox |
sandywp push | Push 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 mounts | List active mounts, flagging any that have gone dead |
sandywp delete <name> | Delete a sandbox by slug |
sandywp blueprint list|get|create|update|delete | Manage your Blueprints (see below) |
sandywp blueprint import|validate | Fetch 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|delete | Manage 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.
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 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 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
| Flag | Applies to | Description |
|---|---|---|
--base-url <url> | auth login | Target a non-default API host (defaults to https://app.sandywp.com) |
--token <token> | auth login | Log in headlessly with an existing token |
--no-browser | auth login | Print the authorize URL instead of opening a browser |
--no-wait | add | Return immediately instead of waiting for the sandbox to be ready |
--force | push | Skip the overwrite confirmation (and auto-recreate a deleted linked site) |
--open | login, push | Open the login URL in your browser |
--cmd <command> | ssh | Run one command and exit instead of opening a shell |
--path <dir> | mount | Mount at a specific directory instead of ~/SandyWP/<name> |
--all | unmount | Unmount every active mount |
--workspace <id-or-slug> / --org <id-or-slug> | Global | Run 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"
}