wordpress
OpenClaw skill that provides a WordPress REST API CLI for posts, pages, categories, tags, users, and custom requests using plain HTTP.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-wordpress
Repository
Skill path: skills/codedao12/wordpress
OpenClaw skill that provides a WordPress REST API CLI for posts, pages, categories, tags, users, and custom requests using plain HTTP.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Backend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install wordpress into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding wordpress to shared team environments
- Use wordpress for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: wordpress
description: OpenClaw skill that provides a WordPress REST API CLI for posts, pages, categories, tags, users, and custom requests using plain HTTP.
---
# WordPress REST API Skill (Advanced)
## Purpose
Provide a production-ready CLI for WordPress REST API automation. This skill focuses on content workflows (posts/pages), taxonomy (categories/tags), user reads, and safe custom requests without external HTTP libraries.
## Best fit
- You want a stable CLI for automation and bot workflows.
- You need JSON-in/JSON-out for pipelines.
- You prefer simple HTTP with no extra dependencies.
## Not a fit
- You must handle OAuth flows or complex browser-based auth.
- You need advanced media uploads (multipart streaming).
## Requirements
- Node.js 18+ (for native `fetch`).
## One-time setup
1. Enable the WordPress REST API (default in modern WordPress).
2. Create an Application Password for a WordPress user.
3. Confirm the user has the right role (e.g., Editor/Admin).
## Install
```bash
cd wordpress
npm install
```
## Run
```bash
node scripts/wp-cli.js help
node scripts/wp-cli.js posts:list --query per_page=5
node scripts/wp-cli.js posts:create '@post.json'
```
You can also use npm:
```bash
npm run wp -- posts:list --query per_page=5
```
## Credentials
Supported options (first match wins):
- Basic auth token: `WP_BASIC_TOKEN` (base64 of `user:app_password`)
- User + app password: `WP_USER` + `WP_APP_PASSWORD`
- JWT bearer token: `WP_JWT_TOKEN`
## Required env
- `WP_BASE_URL` (e.g., `https://example.com`)
## Input conventions
- JSON can be inline or loaded from file with `@path`.
- Query params use `--query key=value` (repeatable) or `--query key1=value1,key2=value2`.
## Command map (high level)
Posts:
- `posts:list`, `posts:get`, `posts:create`, `posts:update`, `posts:delete`
Pages:
- `pages:list`, `pages:get`, `pages:create`, `pages:update`, `pages:delete`
Taxonomy:
- `categories:list`, `categories:create`
- `tags:list`, `tags:create`
Users:
- `users:list`, `users:get`
Advanced:
- `request` (raw method + path)
## Operational guidance
- Prefer `context=view` for read-only list calls.
- Use `status=draft` when staging content.
- Implement retries for `429` and transient `5xx` errors in orchestrators.
## Expected output
- JSON to stdout; non-zero exit code on errors.
## Security notes
- Never log or commit tokens or application passwords.
- Use a dedicated low-privilege WordPress account where possible.
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### scripts/wp-cli.js
```javascript
#!/usr/bin/env node
'use strict';
const { Buffer } = require('buffer');
function parseArgs(argv) {
const args = [];
const flags = {};
for (let i = 0; i < argv.length; i++) {
const token = argv[i];
if (!token.startsWith('--')) {
args.push(token);
continue;
}
const stripped = token.slice(2);
const eqIndex = stripped.indexOf('=');
if (eqIndex >= 0) {
const key = stripped.slice(0, eqIndex);
const value = stripped.slice(eqIndex + 1);
flags[key] = value === '' ? true : value;
continue;
}
const next = argv[i + 1];
if (next && !next.startsWith('--')) {
flags[stripped] = next;
i += 1;
} else {
flags[stripped] = true;
}
}
return { args, flags };
}
function toQueryList(value) {
if (!value) return [];
if (Array.isArray(value)) return value;
if (typeof value === 'string') {
return value.split(',').map((entry) => entry.trim()).filter(Boolean);
}
return [];
}
function buildAuthHeader() {
const basicToken = process.env.WP_BASIC_TOKEN;
if (basicToken) {
return { Authorization: `Basic ${basicToken}` };
}
const user = process.env.WP_USER;
const appPassword = process.env.WP_APP_PASSWORD;
if (user && appPassword) {
const token = Buffer.from(`${user}:${appPassword}`).toString('base64');
return { Authorization: `Basic ${token}` };
}
const jwt = process.env.WP_JWT_TOKEN;
if (jwt) {
return { Authorization: `Bearer ${jwt}` };
}
return {};
}
function resolveBaseUrl() {
const base = process.env.WP_BASE_URL;
if (!base) {
console.error('Missing WP_BASE_URL. Example: https://example.com');
process.exit(1);
}
return base.replace(/\/$/, '');
}
function buildApiUrl(path, query) {
const base = resolveBaseUrl();
const apiRoot = `${base}/wp-json/wp/v2`;
const normalized = path.startsWith('/') ? path : `/${path}`;
const url = new URL(apiRoot + normalized);
if (query && query.length) {
query.forEach((pair) => {
const [key, ...rest] = pair.split('=');
const value = rest.join('=');
if (key) url.searchParams.append(key, value);
});
}
return url;
}
async function requestJson({ method, path, query, body }) {
const headers = {
'Accept': 'application/json',
...buildAuthHeader(),
};
const options = { method, headers };
if (body !== undefined) {
headers['Content-Type'] = 'application/json';
options.body = JSON.stringify(body);
}
const response = await fetch(buildApiUrl(path, query), options);
const text = await response.text();
const payload = text ? safeJsonParse(text) : null;
if (!response.ok) {
const error = payload || { status: response.status, statusText: response.statusText };
throw new Error(JSON.stringify(error));
}
return payload;
}
function safeJsonParse(text) {
try {
return JSON.parse(text);
} catch {
return { raw: text };
}
}
function jsonFromArg(value, label) {
if (!value) throw new Error(`Missing JSON input for ${label}.`);
if (value.startsWith('@')) {
const fs = require('fs');
const filePath = value.slice(1);
if (!fs.existsSync(filePath)) throw new Error(`JSON file not found: ${filePath}`);
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}
return JSON.parse(value);
}
const HELP_TEXT = `
WordPress REST API CLI (OpenClaw skill)
Usage:
node scripts/wp-cli.js <command> [args] [--flags]
Posts:
posts:list [--query key=value]
posts:get <id>
posts:create <jsonOr@file>
posts:update <id> <jsonOr@file>
posts:delete <id> [--query force=true]
Pages:
pages:list [--query key=value]
pages:get <id>
pages:create <jsonOr@file>
pages:update <id> <jsonOr@file>
pages:delete <id> [--query force=true]
Taxonomy:
categories:list [--query key=value]
categories:create <jsonOr@file>
tags:list [--query key=value]
tags:create <jsonOr@file>
Users:
users:list [--query key=value]
users:get <id>
Advanced:
request <method> <path> [jsonOr@file] [--query key=value]
`;
async function main() {
const { args, flags } = parseArgs(process.argv.slice(2));
const command = args[0];
if (!command || command === 'help' || command === '--help') {
console.log(HELP_TEXT.trim());
return;
}
try {
let result;
const queryList = [];
if (flags.query) {
const entries = Array.isArray(flags.query) ? flags.query : [flags.query];
entries.forEach((entry) => {
toQueryList(entry).forEach((item) => queryList.push(item));
});
}
switch (command) {
case 'posts:list': {
result = await requestJson({ method: 'GET', path: '/posts', query: queryList });
break;
}
case 'posts:get': {
const id = args[1];
if (!id) throw new Error('Usage: posts:get <id>');
result = await requestJson({ method: 'GET', path: `/posts/${id}`, query: queryList });
break;
}
case 'posts:create': {
const body = jsonFromArg(args[1], 'post');
result = await requestJson({ method: 'POST', path: '/posts', query: queryList, body });
break;
}
case 'posts:update': {
const id = args[1];
if (!id) throw new Error('Usage: posts:update <id> <jsonOr@file>');
const body = jsonFromArg(args[2], 'post');
result = await requestJson({ method: 'POST', path: `/posts/${id}`, query: queryList, body });
break;
}
case 'posts:delete': {
const id = args[1];
if (!id) throw new Error('Usage: posts:delete <id>');
result = await requestJson({ method: 'DELETE', path: `/posts/${id}`, query: queryList });
break;
}
case 'pages:list': {
result = await requestJson({ method: 'GET', path: '/pages', query: queryList });
break;
}
case 'pages:get': {
const id = args[1];
if (!id) throw new Error('Usage: pages:get <id>');
result = await requestJson({ method: 'GET', path: `/pages/${id}`, query: queryList });
break;
}
case 'pages:create': {
const body = jsonFromArg(args[1], 'page');
result = await requestJson({ method: 'POST', path: '/pages', query: queryList, body });
break;
}
case 'pages:update': {
const id = args[1];
if (!id) throw new Error('Usage: pages:update <id> <jsonOr@file>');
const body = jsonFromArg(args[2], 'page');
result = await requestJson({ method: 'POST', path: `/pages/${id}`, query: queryList, body });
break;
}
case 'pages:delete': {
const id = args[1];
if (!id) throw new Error('Usage: pages:delete <id>');
result = await requestJson({ method: 'DELETE', path: `/pages/${id}`, query: queryList });
break;
}
case 'categories:list': {
result = await requestJson({ method: 'GET', path: '/categories', query: queryList });
break;
}
case 'categories:create': {
const body = jsonFromArg(args[1], 'category');
result = await requestJson({ method: 'POST', path: '/categories', query: queryList, body });
break;
}
case 'tags:list': {
result = await requestJson({ method: 'GET', path: '/tags', query: queryList });
break;
}
case 'tags:create': {
const body = jsonFromArg(args[1], 'tag');
result = await requestJson({ method: 'POST', path: '/tags', query: queryList, body });
break;
}
case 'users:list': {
result = await requestJson({ method: 'GET', path: '/users', query: queryList });
break;
}
case 'users:get': {
const id = args[1];
if (!id) throw new Error('Usage: users:get <id>');
result = await requestJson({ method: 'GET', path: `/users/${id}`, query: queryList });
break;
}
case 'request': {
const method = args[1];
const path = args[2];
if (!method || !path) throw new Error('Usage: request <method> <path> [jsonOr@file]');
const body = args[3] ? jsonFromArg(args[3], 'body') : undefined;
result = await requestJson({ method: method.toUpperCase(), path, query: queryList, body });
break;
}
default:
throw new Error(`Unknown command: ${command}`);
}
console.log(JSON.stringify(result, null, 2));
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
main();
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "codedao12",
"slug": "wordpress",
"displayName": "Wordpress REST API",
"latest": {
"version": "1.0.0",
"publishedAt": 1770027136694,
"commit": "https://github.com/clawdbot/skills/commit/e7eb34ba0622eb8bafe7e04049a37921e3fe979f"
},
"history": []
}
```
### assets/wordpress-rest-api-guide.md
```markdown
# WordPress REST API Guide (Advanced)
## 1) Base URL and routes
- Base site URL: `https://example.com`
- REST base: `https://example.com/wp-json/wp/v2`
## 2) Auth options
- Application Passwords (recommended for server-to-server).
- JWT (if enabled by plugin).
- Basic auth token in `Authorization` header.
## 3) Core endpoints
### Posts
- List: `GET /posts`
- Get: `GET /posts/{id}`
- Create: `POST /posts`
- Update: `POST /posts/{id}`
- Delete: `DELETE /posts/{id}`
Common fields:
- `title`, `content`, `excerpt`
- `status` (draft, publish, pending, private)
- `slug`, `date`, `author`
- `categories` (array of IDs), `tags` (array of IDs)
### Pages
- List: `GET /pages`
- Get: `GET /pages/{id}`
- Create: `POST /pages`
- Update: `POST /pages/{id}`
- Delete: `DELETE /pages/{id}`
### Categories
- List: `GET /categories`
- Create: `POST /categories`
Common fields:
- `name`, `slug`, `description`, `parent`
### Tags
- List: `GET /tags`
- Create: `POST /tags`
Common fields:
- `name`, `slug`, `description`
### Users (read-only by default)
- List: `GET /users`
- Get: `GET /users/{id}`
Common query params:
- `per_page`, `page`, `search`, `context`
## 4) Query parameters (high impact)
- `per_page`, `page` (pagination)
- `search` (keyword search)
- `context` (view, edit, embed)
- `status` (for posts/pages)
- `categories`, `tags` (filter by IDs)
## 5) Write patterns (JSON body)
### Create post
```json
{
"title": "New post",
"content": "Hello",
"status": "draft",
"categories": [3],
"tags": [7]
}
```
### Update post
```json
{
"title": "Updated title",
"status": "publish"
}
```
## 6) Reliability notes
- Retry `429` and transient `5xx` errors with exponential backoff.
- Keep payloads small and avoid large HTML blobs in a single request.
## 7) Security notes
- Always use HTTPS.
- Use a dedicated low-privilege WordPress account for the bot.
```