Contents
REST API - Plugixa Permalink Manager
Everything the admin screens do goes through a REST API, and you can use it from your own code. It lives under:
/wp-json/plugixa-permalink-manager/v1/
Authentication and permissions
Every route requires a signed-in user with the right permission; there are no public routes. Use a cookie with a REST nonce from inside WordPress, or an application password from outside.
| Permission | Needed for |
|---|---|
| View | Reading, except the import sources (Import) |
| Edit | Changing addresses, and POST /uris/check |
| Redirects | Adding, changing and deleting redirects PRO |
| Tools | Running the bulk tools |
| Import | Running an import |
| Settings | Changing settings, patterns and integrations |
The capability behind each one is listed in Roles and permissions.
Administrators have all of them. Changing an address also requires that the
user may edit that post or term in WordPress. Nothing is ever changed by a
GET request.
The envelope
A successful response:
{ "success": true, "data": { }, "meta": { "total": 18, "page": 1, "per_page": 50, "pages": 1 } }
meta is present on lists. An error is a standard WordPress REST error with a
code, a message and the HTTP status.
| Status | Meaning |
|---|---|
| 400 | The request is malformed |
| 401 / 403 | Not signed in, or not allowed |
| 404 | No such item, job, tool or source |
| 409 | The address is taken, or the item changed since you read it |
| 413 | More than 200 changes in one /uris/bulk request. Other over-size requests return 400 |
| 422 | The value is not acceptable (reserved address, unknown setting, broken regular expression) |
Addresses
| Method and route | Does |
|---|---|
GET /uris |
List items. Parameters: kind (post or term), type, search, status, filter (all, custom, native), orderby, order, page, per_page (up to 100) |
GET /uris/types |
Content types and taxonomies with their counts |
GET /uris/{post|term}/{id} |
One item |
PUT /uris/{post|term}/{id} |
Set the address and/or mode |
GET /uris/{post|term}/{id}/default |
The address its pattern would give. Writes nothing |
GET /uris/{post|term}/{id}/history |
Its change history |
GET /uris/{post|term}/{id}/redirects |
Its old addresses |
POST /uris/check |
Check up to 100 addresses without saving |
POST /uris/bulk |
Save up to 200 changes; each reports its own result |
Setting an address:
PUT /uris/post/36
{ "uri": "shop/tents/skyline-2", "mode": -1, "expected_modified_at": "2026-10-08 12:23:38" }
uri- the address. An empty string, or"reset": true, returns the item to its default.mode-0follow the global setting,-1keep,-2keep and skip bulk tools,1update on save,2disable the custom permalink.expected_modified_at- optional. Send themodified_atyou read; if the item changed since, the save is refused with a 409 instead of overwriting.
A taken address returns 409 with the owner and a free suggestion.
Patterns
| Method and route | Does |
|---|---|
GET /permastructures |
Every pattern, with the tags each type offers |
PUT /permastructures/{post_type|taxonomy}/{name} |
Save a pattern: { "pattern": "shop/%product_cat%/%postname%" } |
DELETE /permastructures/{post_type|taxonomy}/{name} |
Restore the WordPress structure |
POST /permastructures/preview |
Preview a pattern on real items, with any unknown tags |
Tools and jobs
| Method and route | Does |
|---|---|
POST /tools/regenerate |
Start a regenerate run: kind, type, mode (default or native), include_manual |
POST /tools/consistency |
Start a health check |
POST /tools/find-replace PRO |
Start a find and replace: find, replace, regex, case_sensitive, kind, type |
GET /jobs/{id} |
A job’s state |
POST /jobs/{id}/tick |
Do a few seconds of work and return the job |
POST /jobs/{id}/cancel |
Cancel a running job |
POST /jobs/{id}/undo |
Start an undo job for a finished run |
Every tool is a preview unless you send "dry_run": false. Starting a tool
returns a job; call tick until its status is no longer queued or
running. The job reports total, processed, changed, skipped, failed
and a sample of the items it touched.
POST /tools/regenerate
{ "dry_run": false, "kind": "post", "type": "product", "mode": "default" }
Redirects PRO
| Method and route | Does |
|---|---|
GET /redirects |
List: group (old, manual, external), search, page, per_page |
POST /redirects |
Add: source, target, status_code (301, 302, 307, 308), note |
PUT /redirects/{id} |
Change status_code, is_enabled, note; and source / target on manual ones |
DELETE /redirects/{id} |
Delete one |
POST /redirects/bulk-delete |
Delete up to 500: { "ids": [ ] } |
Settings and the rest
| Method and route | Does |
|---|---|
GET /settings |
All settings and the choices each one offers |
PUT /settings |
Change some settings. Send only the keys you are changing; an unknown key refuses the whole request |
GET /integrations |
The integration cards and their state |
PUT /integrations/{id} |
Switch one on or off: { "enabled": false } |
GET /import |
Import sources found on the site |
POST /import/{source} |
Start an import (a job, preview by default) |
GET /dashboard |
The dashboard’s numbers |
GET /bootstrap |
The admin app’s startup data: permissions, modes, modules |
GET /conflicts |
Other permalink plugins found, and whether routing is serving |
POST /conflicts/deactivate-permalink-manager |
Deactivate Permalink Manager (Lite or Pro) so this plugin can serve addresses. Returns 404 when it is not active. Needs the WordPress permission to activate and deactivate plugins, not one of the plugin’s own |
GET /debug/resolve?url=... |
What the Debug screen shows. Add live=1 to request the address too |
Import sources
{source} in POST /import/{source} is one of these. GET /import lists the
ones registered on the site, with present, active and counts for each.
| Source | Imports from | Edition |
|---|---|---|
pmp |
Permalink Manager (Lite or Pro) | Free |
custom-permalinks |
Custom Permalinks | Free |
rank-math |
The exact-match redirects of Rank Math | Pro |
yoast |
The exact-match redirects of Yoast SEO Premium | Pro |
seopress |
The exact-match redirects of SEOPress | Pro |
An unknown source returns 404. More in Import.
Setting keys
These are the keys PUT /settings accepts. Each one is a field on a tab of the
Settings screen, where
its effect is described.
Addresses
| Key | Values | Default |
|---|---|---|
auto_update_uris |
0, 1 or 2, in the order of the choices under When content is saved |
0 |
force_custom_slugs |
0 the WordPress slug, 1 a slug made from the title, 2 the parent’s custom address plus the slug |
0 |
trailing_slashes |
0 follow the WordPress permalink setting, 1 always add one, 2 never add one |
0 |
keep_accents |
true or false |
false |
disable_slug_sanitization |
true keeps punctuation and capitals as typed |
false |
Content
| Key | Values | Default |
|---|---|---|
partial_disable |
{ "post_types": [ ], "taxonomies": [ ] }: the types that do not have custom addresses |
The four post types listed in Content settings, no taxonomies |
partial_disable_strict |
true skips content types that are not meant to be visited |
true |
exclude_post_ids, exclude_term_ids |
A string of IDs and ranges, such as "12, 40-45, 318" |
Empty |
ignore_drafts |
0 drafts get an address, 1 no address for drafts, 2 no address for drafts and pending items |
1 |
Redirects
| Key | Values | Default |
|---|---|---|
redirect_status |
301, 302, 307, 308, or 0 for no redirects at all |
301 |
setup_redirects |
true keeps the old address when an address changes |
true |
extra_redirects |
true serves redirects from old addresses |
true |
canonical_redirect |
true sends WordPress addresses to the custom ones |
true |
trailing_slash_redirect |
true redirects a missing or extra trailing slash |
true |
copy_query_redirect |
true keeps the query string when redirecting |
true |
sslwww_redirect |
true corrects http/https and www in the same redirect |
true |
external_redirect_policy |
allowlist or any |
allowlist |
external_redirect_hosts |
A list of host names | Empty |
max_redirects_per_item |
1 to 500 |
50 |
Advanced
| Key | Values | Default |
|---|---|---|
edit_uris_cap |
edit_posts, publish_posts, publish_pages or edit_theme_options |
publish_posts |
history_retention_days |
0 to 3650. Stored, but not yet acted on in version 1.0.0 |
90 |
safe_mode |
true or false |
false |
delete_data_on_uninstall |
true or false. Changing it also needs the WordPress permission to manage options |
false |
From PHP
If your code runs inside WordPress, the functions and hooks are simpler than calling the API.