WordPress ExecModule
Overview
WordPressModule connects ValkyrAI workflows to the WordPress REST API v2 through the native map I/O ExecModule ABI. It covers the post, page, user-discovery, and taxonomy action families emphasized by n8n while enforcing ValkyrAI's IntegrationAccount, validation, retry, redaction, and observability contracts.
The module provides 14 operations: create, get, list, update, and delete for posts and pages; category and tag listing; and read-only user get/list. GET requests retry transient failures conservatively. Creates, updates, and deletes are single-attempt because a transport failure can leave their provider outcome ambiguous. Content deletion moves records to trash by default; permanent deletion requires two explicit controls.
Usage
- Use WordPress 5.6 or newer with HTTPS, REST API access, and Application Passwords enabled.
- Create a dedicated WordPress user with only the capabilities required for the workflow.
- Generate an Application Password for that user.
- Store the username in encrypted
IntegrationAccount.username, the Application Password in encryptedIntegrationAccount.password, and the public site origin inIntegrationAccount.accountId. - Set the account status to
READYand bind it throughExecModuleConfig.authConfig.integrationAccount. - Choose one documented operation and provide only its typed resource ID, record, filters, and pagination controls.
- Require human or policy approval before enabling permanent deletion.
Credentials, authorization headers, arbitrary HTTP methods, alternate REST namespaces, insecure TLS, and private-network targets cannot come from workflow input.
Inputs
| Name | Type | Requirement | Default | Description and constraints |
|---|---|---|---|---|
operation | string | Required | None | One of the 14 documented operations. |
siteUrl | string | Required unless the account supplies it | IntegrationAccount.accountId | Public HTTPS WordPress origin, optionally with a bounded subdirectory. Credentials, custom ports, query strings, fragments, localhost, and IP literals are rejected. |
resourceId | integer | Get/update/delete | None | Positive post, page, or user ID. |
record | object | Create/update | None | Non-empty post/page allowlist; at most 40 properties and 512 KiB. Credential-like fields are rejected. |
query | object | Optional list filter | {} | Up to 24 resource-specific filters with bounded scalar or scalar-array values. |
page | integer | Optional list start | 1 | First provider page, 1–100,000. |
perPage | integer | Optional | 100 | WordPress page size, 1–100. |
returnAll | boolean | Optional | false | Follow pages until exhaustion or the 10,000-item safety cap. |
forceDelete | boolean | Delete only | false | false requests reversible trash; true requests permanent deletion. |
confirmPermanentDelete | boolean | Permanent delete only | false | Must be true when forceDelete is true. |
timeoutMs | integer | Optional | 30000 | Total/connect/read/write timeout, 100–300,000 ms. |
Create operations require a bounded record.title. Allowed content fields include title, content, excerpt, status, slug, author, featured media, discussion status, template, metadata, and publication date. Posts additionally support format, sticky state, category IDs, and tag IDs; pages support parent and menu order. Password-protected content and user/password writes are intentionally excluded so workflow payloads do not carry credentials.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
status | string | Always | success or error. |
operation | string | Always | Normalized operation name. |
resource | string | Valid operation | posts, pages, categories, tags, or users. |
data / id | object / integer | Single-resource success | Provider resource and verified positive ID. |
items / count | array / integer | List success | Bounded collection resources and emitted count. |
hasMore / nextPage | boolean / integer | List success | Provider pagination state from X-WP-TotalPages. |
httpStatus | integer | Single-resource success | WordPress HTTP status. |
attempts | integer | Always | Total provider attempts, including page reads. |
error | object | Failure | Safe {code, message, retryable} details. |
Application Passwords, Basic authorization values, and credential-bearing provider text are redacted from errors and outputs.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | WordPress REST API v2 site |
accountName | Human-readable editorial automation identity |
accountId | Public HTTPS site origin, for example https://content.example.com |
username | Dedicated WordPress username or account email |
password | Encrypted WordPress Application Password |
status | READY; every other status fails closed |
| Capability | Minimum WordPress role/capabilities for the selected reads or approved content writes |
Application Passwords inherit the WordPress user's capabilities. Use a dedicated minimum-access user, rotate the password through the IntegrationAccount lifecycle, revoke unused passwords, and never place the username/password pair in workflow JSON.
Configuration
{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:wordpress-editorial"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "EXPONENTIAL",
"initialDelayMs": 1000,
"maxDelayMs": 5000,
"jitter": false
},
"executionConfig": {"timeoutMs": 30000},
"payloadConfig": {
"parameters": "{\"operation\":\"list_posts\",\"perPage\":50,\"query\":{\"status\":\"publish\"}}"
}
}
The integration-account value is symbolic. Persisted workflows use the generated relationship, not embedded credentials.
Operations
| Operation | WordPress behavior | Side effect |
|---|---|---|
create_post | Creates a post from bounded editorial, taxonomy, discussion, and publication fields. | Content write; single attempt. |
get_post | Reads one post by ID. | Read-only; safe retries. |
list_posts | Lists posts by status, search, author, taxonomy, time, slug, and ordering filters. | Read-only; paginated safe retries. |
update_post | Updates allowlisted post fields and verifies the returned ID. | Content write; single attempt. |
delete_post | Moves a post to trash by default or permanently deletes after explicit confirmation. | Reversible by default; single attempt. |
create_page | Creates a page with bounded hierarchy, order, template, and editorial fields. | Content write; single attempt. |
get_page | Reads one page by ID. | Read-only; safe retries. |
list_pages | Lists pages by status, search, author, parent, time, slug, and ordering filters. | Read-only; paginated safe retries. |
update_page | Updates allowlisted page fields and verifies the returned ID. | Content write; single attempt. |
delete_page | Moves a page to trash by default or permanently deletes after explicit confirmation. | Reversible by default; single attempt. |
list_categories | Lists category taxonomy terms with bounded filters. | Read-only; paginated safe retries. |
list_tags | Lists tag taxonomy terms with bounded filters. | Read-only; paginated safe retries. |
get_user | Reads one visible WordPress user by ID. | Read-only; safe retries. |
list_users | Lists visible users by role, publication, search, slug, and ordering filters. | Read-only; paginated safe retries. |
Errors and Failure Modes
| Code | Typical cause | Retryable | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid origin, credential, ID, body, status, taxonomy IDs, filter, timestamp, pagination, or delete acknowledgement. | No | Correct the named field; validation failures send no request. |
UNSUPPORTED_OPERATION | Unknown operation. | No | Select a documented operation. |
INTEGRATION_ACCOUNT_REQUIRED | No bound account. | No | Bind a WordPress IntegrationAccount. |
INTEGRATION_ACCOUNT_NOT_READY | Account is not READY. | No | Repair or reconnect the account. |
WORDPRESS_REST_* | WordPress returned a structured REST error. | Depends on status | Correct site capabilities, resource state, or payload. |
WORDPRESS_HTTP_401 / 403 | Username/Application Password is invalid, revoked, or under-scoped. | No | Rotate the password or grant the minimum required capability. |
WORDPRESS_HTTP_404 | Resource or REST route is absent. | No | Verify REST API availability, permalinks, and the resource ID. |
WORDPRESS_HTTP_429 / 5xx | Rate limit or transient site failure. | Yes for reads | Honor Retry-After; reconcile writes before repeating them. |
NETWORK_ERROR | DNS, TLS, timeout, or connectivity failure. | Yes for reads | Verify the site; never blindly retry an ambiguous mutation. |
RESPONSE_TOO_LARGE | One response exceeded 5 MiB. | No | Narrow filters or lower perPage. |
RESOURCE_ID_MISMATCH | A single-resource response returned another ID. | No | Stop and reconcile proxy/plugin behavior before retrying. |
Example
Publish an approved platform update:
{
"operation": "create_post",
"siteUrl": "https://content.example.com",
"record": {
"title": "Quarterly platform update",
"content": "<p>Highlights from the quarter...</p>",
"status": "publish",
"slug": "quarterly-platform-update",
"categories": [7],
"tags": [19]
}
}
Expected result:
{
"status": "success",
"operation": "create_post",
"resource": "posts",
"id": 842,
"httpStatus": 201,
"attempts": 1,
"data": {"id": 842, "status": "publish", "slug": "quarterly-platform-update"}
}
Notes
- Pagination: list operations set
pageandper_page, consumeX-WP-TotalPages, stop after one page unlessreturnAllis enabled, and cap aggregate results at 10,000. - Rate limits: GET operations retry HTTP 408, 429, 500, 502, 503, and 504 up to three attempts with bounded backoff and integer
Retry-After. Mutations never retry automatically. - API limits: responses are capped at 5 MiB, records at 512 KiB and 40 fields, filters at 24 fields, page size at 100, and aggregate results at 10,000. Origins, strings, arrays, IDs, and timestamps are validated before dispatch.
- Idempotency: get/list operations are safe reads. Create, update, and delete are not assumed idempotent; a timeout is ambiguous and requires provider reconciliation. Use stable slugs and caller correlation data where the editorial process supports them.
- Destructive behavior: delete requests use
force=falseby default, which asks WordPress to move supported content to trash. Permanent deletion requires bothforceDelete: trueandconfirmPermanentDelete: true. - Privacy: user responses and private/draft content can contain personal or confidential information. Limit downstream propagation, logging, retention, and workflow audiences.
- Routing: production requests use the validated public HTTPS site origin plus the fixed
/wp-json/wp/v2namespace. HTTP downgrade, embedded credentials, custom ports, arbitrary paths, localhost, and IP literals are rejected. - External verification: request construction, Basic authentication, pagination, validation, retry safety, response mapping, ID checks, redaction, and metadata discovery are deterministic local tests. Live WordPress roles, plugins, themes, REST filters, reverse proxies, and provider data require separately authorized site credentials and are not exercised in repository tests.
- Deferred operations: media upload, comments, revisions, custom post types, custom taxonomies, user create/update/delete, password-protected content, WordPress.com OAuth2, webhooks/triggers, batch endpoints, and arbitrary raw requests.
- Functional references: n8n WordPress integration source, WordPress REST API handbook, and WordPress Application Passwords guide.