Skip to content

Powered by Grav + Helios

Demo Mode

Demo Mode

Control the demo-mode reset engine, for public demo sites. See Demo Mode for setting a demo up. A demo account is any account with access.api.demo set. Demo accounts can read, but every write is refused except the grants listed in plugins.api.demo.writable (read-only when empty, the default) a...

Control the demo-mode reset engine, for public demo sites. See Demo Mode for setting a demo up. A demo account is any account with access.api.demo set. Demo accounts can read, but every write is refused except the grants listed in plugins.api.demo.writable (read-only when empty, the default) and the plugin routes in plugins.api.demo.allow_routes. Reads that reveal server paths, logs, backups or the audit trail are hidden from them. A refused request gets a 403 with the error code demo_mode_write_blocked.

A baseline is a saved copy of the folders a reset restores. Once one is captured, those folders are put back to it every reset_interval minutes, either on the first API request after the interval (reset_on_request) or by a scheduler job (reset_on_schedule), or both. Nothing is reset until a baseline exists.

Permissions: Demo Status needs any authenticated account. Capture Baseline and Reset Demo need a super admin (a scoped API key needs the admin.super scope), and demo accounts are always refused on them, even when they are super, so a visitor can't save a vandalized state as the baseline or force a reset.

Status fields

All three routes return the same status object:

Field Meaning
baseline_exists Whether a baseline has been captured
baseline_at Unix timestamp of the baseline, or null when there is none
writable plugins.api.demo.writable as configured: the grants demo accounts may use, such as api.pages.update or the shorthand api.pages.write
roots The folders a reset restores: pages, media and config (plus environments/<name> for each environment's settings folder) from the grants, plugins and themes with include_code, and each extra_roots folder by its path under user/, where they exist
reset_interval Minutes between resets
reset_on_request Whether a reset starts on the first API request after the interval
reset_on_schedule Whether a scheduler job performs resets
last_reset Unix timestamp of the last reset (or of the baseline, if no reset has run since), or null
seconds_until_reset Seconds until the next reset is due; 0 means due now, null when there is no baseline

Demo Status

GET /demo/status
The demo-mode reset state that drives the Admin Next demo banner and its countdown. Any authenticated account can call it; no specific permission is needed. See the [collection introduction](/2/api/endpoints/demo) for the fields.
JSON
{"data": {"baseline_exists": true, "baseline_at": 1789998200, "writable": ["api.pages.create", "api.pages.update", "api.media.upload"], "roots": ["media", "pages"], "reset_interval": 30, "reset_on_request": true, "reset_on_schedule": true, "last_reset": 1790000000, "seconds_until_reset": 1234}}

Response Codes

200 Status returned.
401 Unauthorized.

Capture Baseline

POST /demo/baseline
Save the folders a reset restores (worked out from `plugins.api.demo.writable`, `include_code` and `extra_roots`) as the baseline. When there are none, as in a read-only demo, nothing is captured and `captured` is `false`, still with a 200. On success it fires `onApiDemoBaselineCaptured` and sends `X-Invalidates: demo:status`. Requires a super admin (a scoped API key needs the `admin.super` scope); demo accounts get 403 even when they are super.
JSON
{"data": {"captured": true, "message": "Demo baseline captured.", "status": {"baseline_exists": true, "baseline_at": 1789998200, "writable": ["api.pages.create", "api.pages.update", "api.media.upload"], "roots": ["media", "pages"], "reset_interval": 30, "reset_on_request": true, "reset_on_schedule": true, "last_reset": 1790000000, "seconds_until_reset": 1800}}}

Response Codes

200 Capture attempted; `captured` says whether a baseline was saved.
401 Unauthorized.
403 Not a super admin, a scoped key without `admin.super`, or a demo account.

When nothing was captured, message is "No writable demo resources are configured, so there is nothing to capture."

Capturing replaces any previous baseline and restarts the reset timer, so last_reset is set to the capture time.

Reset Demo

POST /demo/reset
Restore the demo content from the captured baseline now, waiting for any background reset already in progress. Without a baseline nothing happens and `reset` is `false`, still with a 200. Fires `onApiDemoReset` and sends `X-Invalidates: demo:status`. Requires a super admin (a scoped API key needs the `admin.super` scope); demo accounts get 403 even when they are super.
JSON
{"data": {"reset": true, "message": "Demo content reset to baseline.", "status": {"baseline_exists": true, "baseline_at": 1789998200, "writable": ["api.pages.create", "api.pages.update", "api.media.upload"], "roots": ["media", "pages"], "reset_interval": 30, "reset_on_request": true, "reset_on_schedule": true, "last_reset": 1790000000, "seconds_until_reset": 1800}}}

Response Codes

200 Reset attempted; `reset` says whether content was restored.
401 Unauthorized.
403 Not a super admin, a scoped key without `admin.super`, or a demo account.