API

A small HTTP/JSON API for managing WordPress sandboxes. Everything the CLI and dashboard do runs through these endpoints.

Base URL

https://app.sandywp.com

Authentication

All endpoints require a personal API token, sent as a bearer token:

Authorization: Bearer swp_xxxxxxxxxxxxxxxxxxxx

Get a token with sandywp auth login, from the account menu in your dashboard (API keys), or by calling the tokens endpoint. Requests without a valid token return 401.

Conventions

  • Requests and responses are JSON. Send Content-Type: application/json on writes.
  • Sandboxes are addressed by their id in the API. The CLI resolves the friendlier slug to an id for you.
  • Errors use a consistent envelope:
{
  "error": {
    "code": "plan_limit_reached",
    "message": "Your free plan allows 2 active sandboxes.",
    "details": { "plan": "free", "limit": 2, "activeSites": 2 }
  }
}

Account

GET /api/account/me

Returns the authenticated user.

curl https://app.sandywp.com/api/account/me \
  -H "Authorization: Bearer $SANDYWP_TOKEN"
{ "id": "user_123", "email": "[email protected]" }
GET /api/account/usage

Returns your plan and current sandbox usage.

{ "email": "[email protected]", "plan": "free", "limit": 2, "activeSites": 1 }

Tokens

POST /api/account/tokens

Creates a personal API token. The plaintext token is returned once — store it now. Optional body: label.

curl -X POST https://app.sandywp.com/api/account/tokens \
  -H "Authorization: Bearer $SANDYWP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"label":"ci-pipeline"}'
{
  "token": "swp_xxxxxxxxxxxxxxxxxxxx",
  "apiToken": {
    "id": "apitok_123",
    "label": "ci-pipeline",
    "createdAt": "2026-06-20T10:00:00.000Z",
    "lastUsedAt": null
  }
}
GET /api/account/tokens

Lists your active tokens (metadata only — never the token value).

{
  "tokens": [
    { "id": "apitok_123", "label": "ci-pipeline", "createdAt": "…", "lastUsedAt": "…" }
  ]
}
DELETE /api/account/tokens/:id

Revokes a token. Returns { "success": true }.

Sandboxes

POST /api/app/sites

Creates a sandbox. All fields are optional; sensible defaults are used (latest WordPress, PHP 8.3, standard preset, auto worker).

FieldTypeDescription
siteNamestringDisplay name; the slug is derived from it
wordpressVersionstringe.g. latest
phpVersionstringe.g. 8.3
provisioningPresetstringstandard or debug
templateIdstringCreate from a saved template instead
backgroundbooleanReturn immediately with status: "creating" instead of waiting for the sandbox to be ready
curl -X POST https://app.sandywp.com/api/app/sites \
  -H "Authorization: Bearer $SANDYWP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"siteName":"my-sandbox"}'
{
  "site": {
    "id": "site_abc123",
    "name": "my-sandbox",
    "slug": "my-sandbox",
    "status": "ready",
    "publicUrl": "https://my-sandbox.eu1.sandywp.dev",
    "wordpressVersion": "latest",
    "phpVersion": "8.3",
    "workerCode": "eu1",
    "adminUsername": "admin",
    "adminPassword": "SandyWP…",
    "magicLoginUrl": "https://my-sandbox.eu1.sandywp.dev/?sandywp_magic=…",
    "permanent": false,
    "expiresAt": "2026-06-27T10:00:00.000Z",
    "createdAt": "2026-06-20T10:00:00.000Z",
    "readyAt": "2026-06-20T10:00:02.000Z",
    "failureReason": null
  }
}
By default creation is synchronous: the request waits until the sandbox is ready and returns status: "ready" with the admin credentials and login URL. If provisioning takes longer than ~60s the response comes back with status: "creating" and HTTP 202 — poll the status endpoint until it becomes ready. Pass background: true to skip the wait and return immediately.
GET /api/app/sites

Lists your sandboxes.

{ "sites": [ { "slug": "my-sandbox", "status": "ready", "publicUrl": "https://…", "adminUsername": "admin", "adminPassword": "SandyWP…" } ] }
GET /api/sites/:id/status

Returns a sandbox with its recent provisioning events and jobs — useful for showing live progress.

{
  "site": { "slug": "my-sandbox", "status": "ready", "publicUrl": "https://…", "magicLoginUrl": "https://…" },
  "events": [ { "kind": "worker.job_event", "message": "Finalizing WordPress settings.", "createdAt": "…" } ],
  "jobs": [ { "type": "cold_provision_site", "status": "completed" } ]
}
POST /api/sites/:id/magic-login

Issues a one-time login link that opens the sandbox already signed in to wp-admin.

{
  "token": "…",
  "url": "https://my-sandbox.eu1.sandywp.dev/?sandywp_magic=…",
  "expiresAt": "2026-06-27T10:00:00.000Z"
}
DELETE /api/app/sites/:id

Deletes a sandbox. Returns the deleted site record.

Prefer a ready-made client? The SandyWP CLI wraps every endpoint here.