Content Module
Create, read, update, and delete WordPress posts, pages, and custom post types. The foundation of everything content-related in the Abilities API.
Overview
The Content module manages WordPress WP_Post objects β posts, pages, and any registered custom post type. It wraps WP_Query, wp_insert_post(), wp_update_post(), and wp_delete_post() behind a consistent, permission-aware interface.
All write and delete abilities are pro-gated by default. Read abilities are available on the free tier. The permissions filter wp_abilities_suite_permissions controls access per user role.
Block markup note
content/get returns raw Gutenberg block markup. If you pass this directly to content/update, the block structure is preserved. Use the Blocks module (blocks/parse, blocks/insert) to surgically modify content without touching the full post.Ability Reference
| Ability | Type | Description |
|---|---|---|
| content/list | R | List posts with filtering by post_type, status, author, taxonomy terms. Supports pagination. |
| content/get | R | Get a single post by ID. Returns full block markup in post_content. |
| content/get-text | R | Get a post's readable text content with block markup and HTML stripped. |
| content/get-by-slug | R | Retrieve a post by post_name (slug) and post_type. |
| content/find-by-url | R | Find a post by its full permalink URL. Useful when you have a URL but not an ID. |
| content/get-snapshot | R | Complete post data in one call: fields, all meta, taxonomy terms, and author info. |
| content/get-site-map | R | Full hierarchical page tree β ID, title, slug, parent, and children. |
| content/list-structure | R | List post metadata without loading the content field. Fast for structural surveys. |
| content/discover-types | R | List all registered post types with their capabilities and REST support status. |
| content/search | R | Full-text search across post titles and content with relevance ordering. |
| content/create | W | Create a new post. Accepts post_type, title, content (block markup), status, date, meta. |
| content/update | W | Update an existing post. Accepts any post fields. Preserves block structure if content is omitted. |
| content/append | W | Append block markup to the end of an existing post without reading first. |
| content/search-replace | W | Bulk find-and-replace in post_content. Supports dry_run, post_type filter, post_ids filter. |
| content/change-type | W | Convert a post between post types. Returns old/new permalink; warns about orphaned taxonomy terms. |
| content/delete | D | Move a post to trash (default) or permanently delete with force: true. |
| content/batch-update | W | Update multiple posts in a single call. Accepts an array of post objects with IDs. |
Common Workflows
Publish a blog post
1. content/create β post_status: "draft", post_type: "post"
2. taxonomies/assign β attach category + tag term IDs
3. media/upload β upload featured image, get attachment ID
4. meta/update-post-meta β set _thumbnail_id to attachment ID
5. content/update β post_status: "publish"
Survey site content structure
1. content/discover-types β see all post types
2. content/get-site-map β hierarchical page tree
3. content/list-structure β posts list without content (fast)
4. taxonomies/list-terms β categories and tags in use
Surgical content edit
1. blocks/parse β get block array from post_content
2. blocks/find-in-post β locate target block by type/attribute
3. blocks/update-attributes β change only what needs changing
4. content/update β write modified block markup back
Related Modules
Known Gotchas
Block markup preservationIf you pass a plain string (not block markup) to
content/update as post_content, WordPress will save it as a Classic block. Always use blocks/serialize if you're constructing content programmatically.post_author not yet supported
content/create and content/update do not accept a post_author parameter. Posts default to the bridge user. Known gap β GitHub issue filed, queued for next sprint.search-replace on large sitesAlways use
dry_run: true first. On sites with thousands of posts, consider using post_ids to batch the operation.