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/jsonon writes. - Sandboxes are addressed by their
idin 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
/api/account/meReturns the authenticated user.
curl https://app.sandywp.com/api/account/me \
-H "Authorization: Bearer $SANDYWP_TOKEN" { "id": "user_123", "email": "[email protected]" } /api/account/usageReturns your plan and current sandbox usage.
{ "email": "[email protected]", "plan": "free", "limit": 2, "activeSites": 1 } Tokens
/api/account/tokensCreates 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
}
} /api/account/tokensLists your active tokens (metadata only — never the token value).
{
"tokens": [
{ "id": "apitok_123", "label": "ci-pipeline", "createdAt": "…", "lastUsedAt": "…" }
]
} /api/account/tokens/:idRevokes a token. Returns { "success": true }.
Sandboxes
/api/app/sitesCreates a sandbox. All fields are optional; sensible defaults are used (latest WordPress, PHP 8.3, standard preset, auto worker).
| Field | Type | Description |
|---|---|---|
siteName | string | Display name; the slug is derived from it |
wordpressVersion | string | e.g. latest |
phpVersion | string | e.g. 8.3 |
provisioningPreset | string | standard or debug |
templateId | string | Create from a saved template instead |
background | boolean | Return 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
}
} 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./api/app/sitesLists your sandboxes.
{ "sites": [ { "slug": "my-sandbox", "status": "ready", "publicUrl": "https://…", "adminUsername": "admin", "adminPassword": "SandyWP…" } ] } /api/sites/:id/statusReturns 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" } ]
} /api/sites/:id/magic-loginIssues 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"
} /api/app/sites/:idDeletes a sandbox. Returns the deleted site record.