REST API
Snipfire's editor runs on a REST API that you can use too: from deploy scripts, CI, or your own tools.
Base URL: https://example.com/wp-json/snipfire/v1/
Snipfire's routes never run your snippets, so a broken snippet can't block the API.
Authentication
Use any authentication WordPress's REST API accepts:
-
Application passwords (built into WordPress: Users → Profile → Application Passwords) with HTTP Basic authentication. Best for scripts.
curl -u 'jane:abcd efgh ijkl mnop qrst uvwx' https://example.com/wp-json/snipfire/v1/snippets -
Cookies and a nonce, from JavaScript running in the WordPress admin: send the
X-WP-Nonceheader with awp_restnonce.
Permission levels
Each route needs one of three team access levels. Without Snipfire Pro, only administrators have access (all levels).
| Level | Routes |
|---|---|
| view | Reading: snippets, history, schema, export, activity |
| design | Changing snippets. Each change is also checked against the user's level, for the snippet as it is and as it would be (a designer can't create PHP, for example) |
| full | Imports, folder renames, settings, exporting as a plugin, site status, system info, Git sync, preview links, running scheduled snippets |
Two routes for the block editor use WordPress capabilities instead: see Generators and the block editor. A few Pro routes need more than a level: the secrets vault, the AI settings and the agency hub need full access and an administrator (super admin on multisite), and network snippets need a super admin. Team access can't give those.
GET /status and GET /system-info stay readable for administrators while DISALLOW_UNFILTERED_HTML makes Snipfire read-only, since they change nothing.
A refused change returns 403 with the reason in message.
Snippets
| Method and route | Level | What it does |
|---|---|---|
GET /snippets |
view | All snippets, without code. ?with_code=1 includes the code. ?uid=<uid>,<uid> returns only the snippets with these permanent IDs (the way to find the same snippet on several sites) |
POST /snippets |
design | Create a snippet (body: snippet fields) |
GET /snippets/{id} |
view | One snippet, with code |
PUT / PATCH / POST /snippets/{id} |
design | Update; only the fields you send change |
DELETE /snippets/{id} |
design | Delete |
POST /snippets/{id}/enable |
design | Switch on (checks and test-loads) |
POST /snippets/{id}/disable |
design | Switch off |
POST /snippets/{id}/run |
design | Run a PHP or HTML snippet once; returns { "output": "…", "error": null } |
POST /snippets/{id}/duplicate |
design | Copy (switched off); returns the copy, 201 |
POST /snippets/{id}/editing |
design | Check in while the snippet is open: returns others (who else has it open), modified, modified_by and by_name (who saved it last) |
DELETE /snippets/{id}/editing |
design | Check out when you close the snippet |
POST /validate |
design | Check a snippet without saving (body: snippet fields); returns { "errors": [], "warnings": [] } |
Create, update, enable and disable return:
{
"snippet": { "id": 12, "title": "…", "status": "active", "code": "…" },
"errors": [],
"warnings": [],
"deactivated": false
}
Status 201 for a new snippet, 200 for an update, 400 when it couldn't be saved at all. A snippet that was saved but has problems is returned with errors (each { "message": "…", "line": 3 }) and switched off; deactivated is true when you asked for it to be on and it isn't.
The body can include a settings form (fields, values), smart_tags, and for Pro features schedule (with "run": "schedule") and selector (with "hook": "element:selector"): see Snippet fields.
On an update, values changes only the keys you send and keeps the others. Send "values_mode": "replace" to replace them all (fields you leave out go back to their defaults).
Example: create a CSS snippet and switch it on:
curl -u "$USER:$APP_PASSWORD" -X POST https://example.com/wp-json/snipfire/v1/snippets \
-H 'Content-Type: application/json' \
-d '{"title":"Hide store notice","type":"css","code":".woocommerce-store-notice{display:none}","status":"active"}'
Saving over someone else's changes
To avoid overwriting a change someone else made after you loaded a snippet, send the snippet's modified value (as you loaded it) as base_modified with an update:
{ "code": "…", "base_modified": 1790000000 }
If someone else has saved the snippet since, the update is refused with 409 and nothing changes:
{
"snippet": null,
"errors": [ { "message": "Maria saved this snippet at 14:32, after you opened it. Saving now would overwrite their changes.", "line": 0 } ],
"warnings": [],
"deactivated": false,
"conflict": { "user": 7, "name": "Maria", "time": 1790000100 }
}
To save anyway, send the update again with "force": true. Updates without base_modified aren't checked. The editor does this for you: see When someone else has the snippet open.
History
| Method and route | Level | What it does |
|---|---|---|
GET /snippets/{id}/revisions |
view | Earlier versions, newest first, with who saved each |
GET /snippets/{id}/revisions/{rev} |
view | One version, with code and settings |
POST /snippets/{id}/revisions/{rev}/restore |
design | Restore it (checked and test-loaded; the current version is kept) |
Bulk actions and folders
POST /bulk (design):
{ "ids": [12, 15, 18], "action": "enable", "value": "" }
Actions: enable, disable, delete, move (value = folder, "" for none), tag and untag (value = tag). Returns done (IDs), errors (ID → message) and items (the updated list). Enabling several snippets runs one test load.
POST /folders/rename (full): { "from": "Old/Name", "to": "New/Name" }. Subfolders move too. Returns { "moved": 4 }.
Export and import
| Method and route | Level | What it does |
|---|---|---|
GET /export |
view | Export file of all snippets, or ?ids=12,15 |
POST /import |
full | Import an export file (the JSON as the body). Arrives switched off unless ?keep_status=1. Returns { "created": [ids], "errors": [] } |
POST /export-plugin |
full | Export snippets as a plugin. Body: ids (list, or "12,14") and/or folder, name, slug, version, include (IDs of snippets with issues to export anyway, or "all"), dry_run. Returns the report (snippets, each with export, blocked and issues; exported) and, unless it's a dry run, filename, size and zip (the zip file, base64-encoded) |
Snippet library
| Method and route | Level | What it does |
|---|---|---|
GET /library |
view | The snippet library: packs, and items with their code and this site's state (new, current, changed, update), snippet id, status, missing plugins and needs_edit |
POST /library/install |
design | Install. Body: slugs (list) and/or pack (pack ID), activate (true to switch them on after the checks and one test load). Returns installed (slug → ID), activated, skipped and errors (slug → reason) |
POST /library/update |
design | Update installed snippets to the library's version. Body: slugs, or "all": true. Returns updated, kept (slug → settings kept), replaced (slugs whose code edits were replaced), skipped, errors |
Each snippet is checked against the user's access, so a designer's request skips PHP snippets.
Generators and the block editor
| Method and route | Level | What it does |
|---|---|---|
GET /generators |
view | The generators: id, title, description, notes, group, type, hook, priority, conditions, smart_tags, consent, fields, code, requires (plugins), missing (plugins not active here), pro (needs Snipfire Pro) and locked (why it can't be used on this site, for example Needs Snipfire Pro., or ""). To make a snippet from one, send its settings to POST /snippets with your values, or use wp snipfire generate |
GET /block-snippets |
edit_posts capability |
Shortcode snippets for the Snipfire block and the page-builder elements: id, title, shortcode, active, and atts (the attribute names the code reads, with defaults). People with Snipfire access get all of them; other writers only the switched-on ones |
GET /page-snippets/{post} |
view, and may edit the post | The snippets on a post's page, as in the Snipfire panel: runs, maybe, everywhere (PHP on early hooks, which runs on every request), lists (HTML with the excerpt or in lists of posts) and shortcodes, each a list of id, title, type, hook, and a note for everywhere and lists rows |
Page code is stored with the post, in the _snipfire_page_scripts meta field (head, body, footer, skip_global). It's in WordPress's own REST API, for example /wp/v2/pages/{id}, and only people who may change page code can write it.
Importers (other plugins)
| Method and route | Level | What it does |
|---|---|---|
GET /import/sources |
full | Plugins with snippets to import |
GET /import/sources/{source} |
full | Preview: each snippet, how it maps, notes, Check first |
POST /import/sources/{source}/run |
full | Import. Body: ids (the other plugin's IDs; all if left out), update (re-import changed ones) |
POST /import/sources/{source}/switch |
full | Switch snippets off in the other plugin. Body: ids. Returns the Snipfire IDs to switch on next |
Switching over is two requests on purpose: call switch, then switch the returned IDs on with POST /bulk ("action": "enable") in a new request, so no function is declared twice. Sources: wpcode, code-snippets, fluent-snippets, hfcm, custom-css-js, post-snippets, woody, customizer-css, wpcodebox.
The editor
| Method and route | Level | What it does |
|---|---|---|
GET /design-tokens |
view | The site's design tokens for CSS autocomplete: tokens (each name, value, label, kind and source: theme, elementor or bricks) and sources. Cached until a source changes; ?fresh=1 rebuilds it |
POST /welcome |
view | The Getting started screen for the current user: { "dismissed": true } stops it opening by itself, false brings it back |
Site status and system info
| Method and route | Level | What it does |
|---|---|---|
GET /status |
full | A short summary for tools that look after several sites (such as the agency hub, or your own monitoring) |
GET /system-info |
full | The system info: sections (each a title and rows of label and value) and text, ready to paste |
GET /status returns:
{
"format": 1,
"time": 1790000000,
"site": { "url": "https://example.com/", "name": "Example", "environment": "production", "multisite": false },
"snipfire": { "version": "0.17.0", "update": "" },
"wordpress": "7.1",
"php": "8.3.12",
"snippets": { "total": 42, "active": 37, "errors": 1, "by_type": { "php": 20, "html": 10, "css": 8, "js": 4 } },
"errors": [ { "id": 12, "uid": "…", "title": "…", "type": "php", "status": "inactive", "message": "…", "line": 4, "time": 1789990000 } ],
"safe_mode": false,
"php_disabled": false,
"read_only": false,
"addons": { "pro": { "version": "0.17.0" } }
}
update is a newer Snipfire version WordPress already knows about, or "". errors lists up to 50 snippets with a recorded error. safe_mode is site-wide safe mode (SNIPFIRE_SAFE_MODE); php_disabled and read_only are SNIPFIRE_DISABLE_PHP and DISALLOW_UNFILTERED_HTML. Neither route includes code, secrets or email addresses, and neither contacts another server.
WordPress abilities
Snipfire also registers WordPress abilities (WordPress 6.9+), for AI assistants and tools that use the Abilities API. See AI assistants and WordPress abilities.
Schema
GET /schema (view) returns what the editor needs to build its forms: condition types with their operators and options, the picker groups, the locations per type with their stage, the snippet types and CSS languages. Useful to build a UI or validate input.
Scheduled snippets Pro
| Method and route | Level | What it does |
|---|---|---|
GET /snippets/{id}/runs |
view | The run log (the last 50 runs, newest first) and the next run |
POST /snippets/{id}/runs |
full | Run the saved snippet now and record it (Run now) |
To put a snippet on a schedule, save it with "run": "schedule" and a schedule: see Snippet fields.
Snippet performance Pro
| Method and route | Level | What it does |
|---|---|---|
GET /profile |
view | The performance figures of each snippet (from the last week), and the settings |
DELETE /profile |
full | Delete all measurements |
Secrets Pro
Full access and an administrator (super admin on multisite). Values go in, never out: no route returns a secret's value.
| Method and route | What it does |
|---|---|
GET /secrets |
Names, masked hints (••••abcd), dates, the key state and where each secret is used |
POST /secrets |
Add one: { "name": "stripe_key", "value": "…" }. 409 if the name exists |
POST /secrets/{name} (or PUT) |
Replace its value: { "value": "…" } |
DELETE /secrets/{name} |
Delete it |
POST /secrets/rekey |
Re-encrypt every secret with the current key |
AI assistant Pro
| Method and route | Level | What it does |
|---|---|---|
GET /ai |
full, administrator | The AI assistant's settings (never the key) and this hour's usage |
POST /ai/settings |
full, administrator | Change settings: enabled, provider, model, base_url, timeout, max_tokens, access; key is stored in the vault as ai_api_key |
POST /ai/test |
full, administrator | A tiny request to check the settings |
POST /ai/explain |
the level in the settings | { code, type, hook, run, title, context } → { text } |
POST /ai/fix |
the level in the settings | { code, type, hook, run, title, errors, context } → { code, explanation } |
POST /ai/write |
the level in the settings | { prompt, type, context } → { snippet, notes } (not saved) |
context: true adds the site context. Each request counts towards the user's hourly limit. Errors come back with a message fit to show: 409 when there's no key or it's switched off, 429 over the limit, 400 for bad input, 504 on a timeout and 502 for errors from the provider.
Activity log Pro
GET /activity (view): newest first, 50 per page.
Query: snippet (ID), user (ID), action (see log actions), search, page.
Returns { "items": [...], "more": true, "actions": { "created": "Created", ... } }.
Settings form values Pro
POST /snippets/{id}/values (design): save only the settings form values of a snippet, including one the user can't otherwise edit, such as a PHP snippet. Body: { "values": { "key": "…" } }; only the keys you send change. The values are checked and test-loaded like any save; the code, fields and other settings don't change. Returns the same as an update. 400 if the snippet has no settings form.
Live CSS preview Pro
POST /live-css (design): compile CSS, SCSS or LESS for the live preview without saving. Body: code, lang (css, scss, less), optional id (for the snippet's partials), fields and values. Returns { "css": "…", "error": null }, or the compile error with its line.
Testing mode Pro
POST /preview/new-key (full): make a new preview link; old links stop working. Returns { "url": "https://example.com/?snipfire-preview=…" }.
Git sync Agency
| Method and route | Level | What it does |
|---|---|---|
GET /git |
full | Settings (without the token), last sync, conflicts |
POST /git/settings |
full | Change settings: provider, repo, branch, root, scope, token, api, schedule. Only the fields you send change |
POST /git/check |
full | Test the connection: { "ok": true, "message": "Connected to …" } |
POST /git/sync |
full | Sync now. Optional body { "resolve": { "<uid>": "site" \| "git" } } settles conflicts first. Returns report plus the status |
POST /git/webhook |
none (signed) | For GitHub and GitLab webhooks; see Webhooks. Each delivery is acted on once: one seen in the last hour (same delivery ID or body), or a GitHub push older than an hour, is answered 200 with Delivery already received; ignored. or Delivery is too old; ignored. |
The token is write-only: it's never returned.
Agency hub Agency
Full access and an administrator (super admin on multisite), on a site with the Agency plan. See Agency hub.
| Method and route | What it does |
|---|---|
GET /hub |
The sites and what the hub last saw (makes no requests) |
POST /hub/sites |
Add a site: { url, user, password, name? }. The hub tests the connection first |
POST /hub/sites/authorize |
Start WordPress's authorization flow for { url }; returns the address to send the browser to |
POST /hub/sites/{id} |
Rename: { name } |
DELETE /hub/sites/{id} |
Remove a site |
POST /hub/sites/{id}/refresh |
Ask one site now |
GET /hub/sites/{id}/snippets |
The snippets on the site that came from the hub (?refresh=1 asks the site) |
POST /hub/refresh |
Ask every site now |
POST /hub/push |
{ ids, sites, activate, resolve: { "<site>": { "<uid>": "overwrite" \| "keep" } } } |
GET /hub/snippets/{id} |
Where a snippet runs (?refresh=1 asks the sites) |
POST /hub/rollback |
{ snippet, site, action: "previous" \| "disable" \| "remove", rev? } |
Application passwords are never returned. The hub talks to client sites through their ordinary routes (GET /status, GET /snippets?uid=…, POST /snippets, PUT /snippets/{id}…), with the header X-Snipfire-Hub.
Network snippets Agency
Multisite, Agency plan. See Network snippets.
On the network (super admins), the same routes as a site's snippets under network/: network/snippets (GET, POST), network/snippets/{id} (GET, PUT, DELETE; answers add required and off_sites), …/{id}/enable, disable, run, duplicate, editing, revisions, revisions/{rev}, revisions/{rev}/restore, plus:
| Method and route | What it does |
|---|---|
POST /network/snippets/{id}/required |
{ "required": true }: sites can't switch it off |
POST /network/snippets/{id}/sites/{site} |
{ "on": true } switches it back on for that site (false off) |
POST /network/bulk, /network/folders/rename, /network/validate |
As on a site |
GET /network/export, /network/activity |
Export file; the network's activity log |
GET / POST /network/settings |
Network settings; { "safe_mode": true } switches network safe mode on |
On each site:
| Method and route | Level | What it does |
|---|---|---|
GET /network-snippets |
view | The network snippets, as the site's editor shows them (From the network) |
POST /network-snippets/{id}/site |
full on that site | { "on": false } switches one off for this site (not a required one); true back on |
Something unclear or missing? Tell us