Contents

REST API - Plugixa Advanced User Role Editor

Everything the admin app does, it does over REST, under the namespace plugixa-aure/v1. Anything the screens can do is therefore scriptable, and every rule the screens enforce is enforced at the route rather than in the page.

This is the API the admin app uses. It is not a separately versioned public API, and the plugin makes no promise that routes, parameters or response shapes stay the same between versions. Pin the plugin version you build against, and test after updating.

Authentication

The plugin adds no authentication of its own. It relies on whatever WordPress provides, and then checks the permissions of the user WordPress identified.

Caller How
The admin app The logged-in cookie plus a REST nonce in the X-WP-Nonce header, the normal WordPress way
A script or another system Any method WordPress accepts for REST requests, such as an application password for an account that holds the permissions below

No route is open to visitors. Read routes are protected too, because they return the complete picture of who can do what on the site.

Permissions

Each route requires one of the plugin’s own capabilities. Administrators hold all four from activation. Other roles get the first two when they are added under Who can use this editor in Settings.

Called in the tables below Capability required
View plugixa_aure_view, or any of the three below
Edit roles plugixa_aure_edit_roles
Edit people plugixa_aure_edit_users
Settings plugixa_aure_manage_settings

A caller without the capability gets the error code plugixa_aure_forbidden.

Passing the route’s permission is not the end of it. Every write also goes through the safety guardrails: nobody can grant a permission they do not hold, lock themselves out, or remove the last administrator, whichever way the request arrives.

Response shape

A successful response wraps its payload:

{
  "success": true,
  "data": { }
}

A refusal is a standard WordPress REST error with a code starting plugixa_aure_, a readable message and an HTTP status. An unexpected failure returns plugixa_aure_error with status 500 and no detail.

Routes

Paths are relative to /wp-json/plugixa-aure/v1.

Start-up

Method Route Needs What it does
GET /bootstrap View Version, settings, feature list, interface strings, what the caller may do, and site details

Roles

Method Route Needs What it does
GET /roles View Every role with its permissions. Each role carries a version used by the changes routes
POST /roles Edit roles Create a role. slug and name are required; from copies an existing role; capabilities sets its permissions
PATCH /roles/{slug} Edit roles Rename (name), change permissions (capabilities), or both
DELETE /roles/{slug} Edit roles Delete a role. reassign_to names the role its holders move to
POST /roles/{slug}/preview Edit roles What a set of capabilities would change for one role, without saving
GET /catalogue View The plain-language permission areas, the permissions in them, the new-role templates and WordPress’s default roles

Capabilities

Method Route Needs What it does
GET /capabilities View Every permission on the site and where it comes from
POST /capabilities Edit roles Add a permission by name (cap)
GET /capabilities/orphans View Leftover permissions that nothing on the site claims
DELETE /capabilities/{cap} Edit roles Remove a leftover permission from every role

Users

Method Route Needs What it does
GET /users Edit people A page of people. page, per_page (default 25, at most 100), search, role
GET /users/{id} Edit people One person: roles, own permissions, and a version used by the changes routes
PATCH /users/{id} Edit people Set roles (in order), capabilities (the account’s own), or both
POST /users/{id}/preview Edit people What a new list of roles would change for one person, without saving
GET /users/migrate Edit people The state of the background move between roles
POST /users/migrate Edit people Start a move. from, to and mode (add, replace or remove) are required
DELETE /users/migrate Edit people Cancel the move

Changes: preview and apply

Method Route Needs What it does
POST /changes/preview Edit roles or Edit people What a set of changes to roles and people would do, and whether it would be allowed. Saves nothing
POST /changes/apply Edit roles or Edit people Saves the set, or refuses all of it

Either capability lets a caller in; the body is then checked part by part. The roles part needs Edit roles and the users part needs Edit people. See the worked example below.

History

Method Route Needs What it does
GET /history Edit roles The History list. per_page (default 50, at most 200)
POST /history Edit roles Take a restore point now. Optional label
POST /history/{id}/restore Edit roles Restore a point, roles and people
GET /snapshots Edit roles The stored restore points. per_page (default 50, at most 200)
POST /snapshots Edit roles Take a restore point now. Optional label
POST /snapshots/{id}/restore Edit roles Restore a point
DELETE /snapshots/{id} Edit roles Delete a restore point

Transfer

Method Route Needs What it does
GET /transfer/export Edit roles The export as filename and bundle. roles is an optional comma-separated list of slugs
POST /transfer/inspect Edit roles Reads a file’s contents (file, as text) and reports what importing it would do
POST /transfer/import Edit roles Imports file. decisions maps each role slug to add, overwrite or skip

The file is sent as a string in the JSON body, not as an upload, and may be at most one megabyte. A decision that is not one of the three words is treated as skip.

Redirects

Method Route Needs What it does
GET /redirects Edit roles The redirect rules, the roles, the events and the destinations available
PATCH /redirects Edit roles Replaces every rule. Send roles and site whole

Settings

Method Route Needs What it does
GET /settings Settings The settings, the feature list, and the roles that can be given access to the editor
PATCH /settings Settings settings changes values; modules switches features on or off by id

The settings object accepts show_deprecated, show_administrator, confirm_dangerous, snapshot_retention (1 to 200) and delegate_roles. Other keys are ignored.

Insights

Method Route Needs What it does
GET /insights/health Edit roles The Health check’s findings and a summary
GET /insights/menu View The stored admin menu as a tree, with the permission each item needs
GET /insights/screens Edit roles Admin screens grouped by the permission that opens them

Access

Method Route Needs What it does
GET /access/actions View The actions Check access can ask about
POST /access/check View Whether user can do action, optionally on a specific object, with the reasons
GET /posts View Finds content to check against. type, search, per_page (default 20, at most 50)
POST /simulate View Whether user holds cap, with optional args, and how WordPress decides. Described in full below

Admin menu PRO

Method Route Needs What it does
GET /menu-control Edit roles The menu rules, the roles, the stored menu and the items that can never be hidden
PATCH /menu-control Edit roles Replaces every rule. roles is required and maps role slug to mode (hide or allow) and items

A top-level item’s key is its menu slug, such as edit.php. A submenu item’s key is parent|slug, such as edit.php|post-new.php.

Screen clean-up PRO

Method Route Needs What it does
GET /admin-cleanup Edit roles The clean-up rules, the roles, what has been seen on the site, and the block editor panels
PATCH /admin-cleanup Edit roles Replaces every rule. roles is required and maps role slug to the list of keys that role hides

The keys are widget:<id>, toolbar:<id>, meta_box:<post type>:<id>, panel:<name>, notices and front_toolbar. Unknown keys are dropped.

Activity log PRO

Method Route Needs What it does
GET /activity Edit roles A page of entries, newest first. page, per_page (default 25, at most 100) and the filters below
GET /activity/export Edit roles The matching entries as filename and csv, oldest first. Takes the same filters
POST /activity/{id}/revert Edit roles Undo one entry
GET /activity/settings Settings The log’s settings
PATCH /activity/settings Settings retention_days (30, 90, 180, 365 or 0 for forever), alert_critical, alert_email
Filter Value
actor An account id
source screen, restore, import, revert, cli, background, rescue, api or admin
operation A kind of change, such as role.capabilities or user.roles
target A role slug, permission or account id
from, to Dates as YYYY-MM-DD

Features that are switched off

A feature switched off in Settings does not register its routes at all. Calling one returns WordPress’s ordinary “no route” 404, not a permission error. The same is true of the three Pro groups on the free edition, where that code is not present.

Worked example: preview, then apply

The role and people screens collect every unsaved edit, send the set to /changes/preview, show the result for review, and then send the same set to /changes/apply. One apply is one restore point.

The body

Both routes take the same body.

{
  "roles": {
    "editor": {
      "read": true,
      "edit_posts": true,
      "publish_posts": true,
      "install_plugins": true
    }
  },
  "users": {
    "12": {
      "roles": ["author", "editor"],
      "capabilities": { "edit_theme_options": true }
    }
  },
  "versions": {
    "role:editor": "3f9c2a71b0de",
    "user:12": "a41d07c95e22"
  },
  "acknowledged": []
}
Key Contents
roles Role slug to that role’s whole permission map. true grants, false blocks
users Account id to roles (the whole list, in order) and, optionally, capabilities (the account’s whole own map)
versions Optional. role:<slug> or user:<id> to the version you loaded from /roles or /users/{id}
acknowledged The key of each warning you have confirmed

Send whole maps. The difference is worked out on the server against what is stored. A permission the role holds now and that is missing from the map you send is taken away. The map above is shortened for the page; a real one lists every permission the role should end up with.

Values that are not true or false are dropped rather than read as a block.

If you send versions and one no longer matches, the change is refused with plugixa_aure_changed_elsewhere: somebody changed that role or person after you loaded it.

1. Preview

POST /wp-json/plugixa-aure/v1/changes/preview
{
  "success": true,
  "data": {
    "roles": [
      {
        "role": "editor",
        "name": "Editor",
        "people": 5,
        "granted": ["install_plugins"],
        "revoked": [],
        "denied": [],
        "cleared": []
      }
    ],
    "users": [
      {
        "user": 12,
        "name": "Sam Carter",
        "rolesAdded": ["editor"],
        "rolesRemoved": [],
        "reordered": false,
        "granted": ["edit_theme_options"],
        "revoked": [],
        "denied": [],
        "cleared": []
      }
    ],
    "totals": { "changes": 3, "people": 5 },
    "warnings": [
      {
        "key": "role:editor:install_plugins",
        "cap": "install_plugins",
        "severity": "critical",
        "target": "Editor",
        "explain": "…",
        "confirm": true
      },
      {
        "key": "user:12:edit_theme_options",
        "cap": "edit_theme_options",
        "severity": "high",
        "target": "Sam Carter",
        "explain": "…",
        "confirm": false
      }
    ],
    "blockers": [],
    "allowed": true
  }
}
Field Meaning
granted, revoked, denied, cleared Permissions newly granted, taken away, newly blocked, and no longer blocked
people How many people the change reaches. null when head counts are switched off
warnings Grants rated critical or high. Those with confirm: true must be acknowledged
blockers Reasons the change would be refused, each with a code, a message and the cap it concerns
allowed true when there are no blockers

A warning’s key has one of three forms:

Key Raised when
role:<slug>:<cap> A role is granted a dangerous permission
user:<id>:<cap> One account is granted a dangerous permission
user:<id>:role:<slug> A person is given a role that holds a full-control permission

Only critical grants need confirming; a high one is reported with confirm: false. confirm is false for every warning when the confirm_dangerous setting is switched off.

In this example the Editor role had four holders, and the person being given it makes five.

2. Apply

Send the same body again, with the key of every warning that has confirm: true copied into acknowledged.

POST /wp-json/plugixa-aure/v1/changes/apply
{
  "roles": { "editor": { "read": true, "edit_posts": true, "publish_posts": true, "install_plugins": true } },
  "users": { "12": { "roles": ["author", "editor"], "capabilities": { "edit_theme_options": true } } },
  "versions": { "role:editor": "3f9c2a71b0de", "user:12": "a41d07c95e22" },
  "acknowledged": ["role:editor:install_plugins"]
}
{
  "success": true,
  "data": {
    "snapshot": 42,
    "totals": { "changes": 3, "people": 5 }
  }
}

snapshot is the restore point taken before the save. It is 0 when the body changed nothing.

When apply refuses

Code Status Meaning
plugixa_aure_unacknowledged 422 A warning that needs confirming is missing from acknowledged
plugixa_aure_changed_elsewhere 409 A version you sent no longer matches
plugixa_aure_forbidden 403 The body has a part the caller may not change
plugixa_aure_administrator_locked 403 The body changes Administrator while editing it is switched off in Settings
plugixa_aure_unknown_role 404 A role slug does not exist
plugixa_aure_unknown_user 404 An account id does not exist
plugixa_aure_no_roles 422 A person would be left with no role
plugixa_aure_cap_is_role 409 A personal permission has the same name as a role the person holds

Apply runs the same plan as preview and refuses the whole set if the plan has any blocker, returning the first one. The acknowledgement is checked on the server, so a script cannot skip it by not showing a dialog. A guardrail refusal comes back with its own code and message.

Asking one question: /simulate

POST /simulate answers whether one account holds one capability and returns each step of the decision. It needs View, reads the account’s saved roles and permissions, and changes nothing. It is a POST so that the account and the question travel in the body and do not end up in server access logs.

/access/check asks in terms of the actions the Check access screen lists. /simulate takes a capability name directly, including meta capabilities such as edit_post.

The simulate body

{
  "user": 12,
  "cap": "edit_post",
  "args": [41]
}
Key Required What it holds
user Yes An account id
cap Yes A capability name. It is lower-cased and stripped of anything that is not a letter, digit, underscore or hyphen
args No The extra arguments WordPress takes for this capability, in order. For edit_post that is the post id. Only plain values are kept; nested arrays and objects are dropped

The simulate answer

{
  "success": true,
  "data": {
    "cap": "edit_post",
    "allowed": false,
    "user": { "id": 12, "login": "maria", "name": "Maria Lopez", "roles": ["author"] },
    "required": ["edit_others_posts", "edit_published_posts"],
    "trace": [
      { "layer": "meta_cap", "effect": "rewrite", "source": "map_meta_cap", "cap": "edit_post", "detail": "..." },
      { "layer": "role", "effect": "absent", "source": "", "cap": "edit_others_posts", "detail": "..." },
      { "layer": "role", "effect": "grant", "source": "author", "cap": "edit_published_posts", "detail": "..." }
    ],
    "unexplained": false
  }
}
Key What it holds
cap The capability that was checked, after cleaning
allowed WordPress’s own answer for that account
user The account’s id, login, name and roles in the order it holds them
required The capabilities WordPress actually requires for this question and these arguments
trace The steps, in the order they were worked out
unexplained true when the roles and the account’s own permissions do not account for allowed

Each step in trace has a layer, an effect, a source, a readable detail and, except for the super administrator step, the cap it concerns.

layer source What the step says
super_admin network On multisite, the account is a network super administrator. Nothing else is checked
meta_cap map_meta_cap rewrite: WordPress turned cap into the capabilities in required. deny: WordPress refuses the request outright and no capability can grant it
role A role slug That role allows (grant) or blocks (deny) the capability. absent, with an empty source, when no role the account holds mentions it
user The account’s login The capability is allowed or blocked on the account itself, which comes after every role
filter user_has_cap Present only when unexplained is true. Other code granted or blocked the capability

When unexplained is true, a plugin, the theme or the site’s own code changed the answer through the user_has_cap filter. The route reports the disagreement. It cannot say which code was responsible, and a filter that depends on the page being requested may answer differently on the real screen.

An account id that does not exist is not an error. The route returns allowed: false with one user step that says so.

The same check is available on the command line as wp plugixa-aure explain. See WP-CLI commands.

Adding your own routes

plugixa_aure_register_rest_routes fires after the plugin has registered its routes, with the namespace and the service container. See Hooks.

Troubleshooting

Symptom Usual cause
401 or 403 from the browser The nonce is missing or stale. Reload the admin
plugixa_aure_forbidden from a script The account lacks the capability the route needs
404 on a route that should exist Its feature is switched off in Settings, or it is a Pro route on the free edition
Apply refuses with plugixa_aure_unacknowledged Copy each warning key with confirm: true from the preview into acknowledged
Apply took permissions away you did not mean to The role’s map was not sent whole

What to do next

Quick Links