Setyenv · Docs
  • English
  • Español
  • 中文
  • 日本語
  • العربية
  • Deutsch
  • Français
  • हिन्दी
  • Bahasa Indonesia
  • Italiano
  • Nederlands
  • Português
  • Русский
  • Türkçe

REST API reference

Complete reference for all WP-PFManagement REST API endpoints. Base URL: /wp-json/pfm/v1/

Authentication

Most endpoints require WordPress authentication via X-WP-Nonce header. Entity CRUD endpoints use a permission resolver that evaluates the current user's roles against the entity's role columns.

Entity & schema management

MethodRouteDescription
GET/meta/entitiesList all entities
POST/meta/entitiesCreate entity + reconcile table
GET/meta/entities/{slug}Get single entity
PUT/meta/entities/{slug}Update entity + reconcile
DELETE/meta/entities/{slug}Delete entity + drop table
POST/meta/entities/{slug}/dry-run-reconcilePreview reconcile plan without applying
POST/meta/entities/{slug}/fieldsAdd a field
PATCH/meta/entities/{slug}/fields/{fieldName}Patch field config
DELETE/meta/entities/{slug}/fields/{fieldName}Remove a field
POST/meta/entities/{slug}/fields/reorderReorder fields

Form layouts

MethodRouteDescription
GET/meta/form-layout/{slug}Get form layout
PUT/meta/form-layout/{slug}Save form layout sections

Auto-numbering

MethodRouteDescription
GET/meta/entities/{slug}/numberingGet auto-numbering config
PUT/meta/entities/{slug}/numberingSet auto-numbering
DELETE/meta/entities/{slug}/numberingRemove auto-numbering
POST/meta/entities/{slug}/numbering/backfillBackfill pfm_number for existing rows
GET/meta/numberingList all numbering sequences
MethodRouteDescription
GET/meta/navigationFull apps + modules tree
GET/meta/applicationsList applications
POST/meta/applicationsCreate application
PUT/meta/applications/{slug}Update application
DELETE/meta/applications/{slug}Delete application
POST/meta/applications/reorderReorder applications
GET/meta/applications/{slug}/modulesList modules
POST/meta/applications/{slug}/modulesCreate module
PUT/meta/applications/{slug}/modules/{slug}Update module
DELETE/meta/applications/{slug}/modules/{slug}Delete module
POST/meta/applications/{slug}/modules/reorderReorder modules

Templates

MethodRouteDescription
GET/meta/templatesList available entity templates
POST/meta/templates/{slug}/installInstall template (creates entity + fields + table)

Records

MethodRouteDescription
GET/{entity}List records. Params: filter (EQL), search (free text), limit, offset, sort, fields, include
POST/{entity}Create record. Anonymous hardening applies (honeypot + rate limit)
POST/{entity}/_bulkTransactional bulk create ({ "records": [ … ] }, all-or-nothing, max 500)
POST/{entity}/import-csvCSV import
GET/{entity}/schemaConsumer field catalog (types, relations, options) — read-gated, only readable fields listed
GET/{entity}/{sys_id}Get single record. include param embeds referenced records
PATCH/{entity}/{sys_id}Update record (optimistic lock via sys_version)
DELETE/{entity}/{sys_id}Delete record
GET/{entity}/{sys_id}/relatedInverse-related records grouped by source entity/field
POST/bulk-updateTransactional cross-entity update ({ "updates": [ … ] }, all-or-nothing)

filter is STRICT EQL: a query that does not parse comes back as 400 pfm_eql_unparseable, never as a successful list with no rows — an integration must be able to tell "your query is wrong" from "nothing matches". Free text belongs in search, which looks through the table's text fields and, when both are sent, narrows inside the filter instead of widening past it.

?include=<field>[,<field>…] deep-loads forward RELATION fields: the target records are returned in an included block keyed by field then by sys_id, so the FK carried on each row joins to its record without an N+1 fetch. Every include honours the same read gates as a direct read (entity ACL, api_exposable, row-visibility, field projection); an unreadable target is silently omitted.

Rate limits

Two ceilings, per caller (per user when signed in, per IP otherwise), counted in one-minute windows. Both answer 429 with a Retry-After header rather than failing silently, and both are bypassed for WP-CLI and cron.

WhatCeilingNotes
Reads on admin/metadata routes240 / minutecode pfm_rate_limited
Writes (POST / PUT / PATCH / DELETE, any route)120 / minutecode pfm_write_rate_limited

The write ceiling is two changes a second sustained, which no person and no screen of ours reaches — the widest burst the product itself produces is a planning save, one record per moved node. It exists so a leaked cookie or a runaway script cannot create rows until the disk fills. If your instance genuinely needs more (a bulk loader of your own, or a limiter already in front of the site), raise or remove it:

add_filter( 'pfm_write_rate_limit_per_minute', fn() => 600 ); // 0 disables it

Anonymous record creation carries its own separate hardening (honeypot + per-entity limit) on top of this.

Response headers, and which ones we control

Every response on this namespace carries X-Request-ID (quote it in a support request), Server-Timing: pfm;dur=<ms> (how long our dispatch took — elapsed time only, no internals), X-Frame-Options, X-Content-Type-Options and Referrer-Policy, plus Vary: Cookie, Origin on user-scoped routes.

X-Powered-By is removed: PHP announces its exact version there by default, and that is precisely what someone probing for a known vulnerability wants.

The Server header is NOT ours. Apache (or nginx) adds it after PHP has finished, so no plugin can remove or change it — verified rather than assumed. If your server announces its version and you would rather it did not, that is a one-line change in YOUR server configuration:

ServerTokens Prod
server_tokens off;

API keys (machine callers)

Integrations authenticate with Authorization: Bearer pfmk_…, resolved to a least-privilege service user. A key carries a { entities, verbs } scope that INTERSECTS the service user's role grants (it never widens them), and it can only reach entities an operator has flagged api_exposable. Keys are schema-manager-gated; the plaintext token is shown once at mint.

MethodRouteDescription
GET/api-keysList keys (metadata only, never the token)
POST/api-keysMint a key for a service user — returns the plaintext ONCE
DELETE/api-keys/{id}Revoke a key

Journal

MethodRouteDescription
GET/{entity}/{sys_id}/journalList journal entries
POST/{entity}/{sys_id}/journalCreate journal entry
DELETE/{entity}/{sys_id}/journal/{entry_id}Delete journal entry

Events

MethodRouteDescription
GET/events/catalogList all registered events
POST/events/catalogRegister a new event
GET/events/{slug}Get event registry entry
PUT/events/{slug}Update event
DELETE/events/{slug}Delete event
POST/events/{slug}/fireFire an event with payload
GET/events/logQuery event log

Actions

MethodRouteDescription
GET/actionsList all actions
POST/actionsCreate action
GET/actions/{id}Get action
PUT/actions/{id}Update action
DELETE/actions/{id}Delete action
POST/actions/{id}/invokeInvoke action on a record
GET/actions/{entity}/{sys_id}List visible actions for a record

Permissions

MethodRouteDescription
GET/rolesList all roles
POST/rolesCreate custom role
GET/roles/{id}Get role
PUT/roles/{id}Update role
DELETE/roles/{id}Delete role
GET/groupsList groups
POST/groupsCreate group
GET/groups/{id}Get group
PUT/groups/{id}Update group
DELETE/groups/{id}Delete group
PUT/groups/{id}/usersReplace member set
PUT/groups/{id}/rolesReplace role set
GET/me/permsEffective permissions for current user

Business Rules

MethodRouteDescription
GET/business-rulesList rules
POST/business-rulesCreate rule
GET/business-rules/{id}Get rule
PUT/business-rules/{id}Update rule
DELETE/business-rules/{id}Delete rule

UI Policies

MethodRouteDescription
GET/ui-policiesList policies
POST/ui-policiesCreate policy
GET/ui-policies/{id}Get policy
PUT/ui-policies/{id}Update policy
DELETE/ui-policies/{id}Delete policy

Row Visibility

MethodRouteDescription
GET/row-visibilityList row visibility rules
POST/row-visibilityCreate rule
GET/row-visibility/{id}Get rule
PUT/row-visibility/{id}Update rule
DELETE/row-visibility/{id}Delete rule

User Preferences

MethodRouteDescription
GET/users/me/list-prefs/{entity}Get per-entity preferences
PUT/users/me/list-prefs/{entity}Save preferences
GET/users/me/global-prefsGet global preferences
PUT/users/me/global-prefsSave global preferences

Public / Front-end

MethodRouteDescription
GET/public/nonceFresh nonce for cached pages
GET/public/entity/{slug}Entity definition (visibility-gated)

Agent & system

MethodRouteDescription
GET/agent/manifestAgent manifest for LLM tooling
GET/localesList platform locales
PUT/localesSet extra locales
GET/users-mapWP users id → name map

Error format

{
  "status": "fail",
  "error": "entity_not_found",
  "detail": "No entity exists with slug 'tickets'"
}