Introducing the WordPress Abilities API

## Metadata
- Author: [[WordPress Developer Blog]]
- Full Title: Introducing the WordPress Abilities API
- Category: #articles
- Document Note: ---
tags: [WordPress, Abilities API, Developer Experience]
author: [MISSING]
source: WordPress Developer Blog
reference: [https://developer.wordpress.org/news/2025/11/introducing-the-wordpress-abilities-api/]
date created: 2025-11-14 00:00
---
# 🏷️ Title: Introducing the WordPress Abilities API
## 🧭 Table of Contents
- [📝 Summary](#-summary)
- [📖 Main Content](#-main-content)
- [🗒️ Notes](#️-notes)
- [📚 References](#-references)
- [🗣️ Paraphrase](#️-paraphrase)
- [📰 Newsletter](#-newsletter)
- [✅ Action Items](#-action-items)
- [🧪 Hypotheses](#-hypotheses)
- [📊 Methodology](#-methodology)
- [❓ Questions](#-questions)
- [🔮 Future Research](#-future-research)
- [💡 Implications](#-implications)
- [🧠 Learning Styles Assessment](#-learning-styles-assessment)
- [📊 Accessibility Assessment](#-accessibility-assessment)
- [🌍 DEIB Assessment](#-deib-assessment)
- [✅ Logical Validity](#-logical-validity)
- [🛡️ DARVO Analysis](#-darvo-analysis)
- [🎯 Conclusion](#-conclusion)
### 🔗 References
- Include links cited in or relevant to the article.
## 📝 Summary
- What happened: WordPress introduces a first-class Abilities API (server-side merging in 6.9) to register, discover, and execute capabilities uniformly across PHP, REST, and JS.
- Why it matters: Standardizes capability exposure for core, plugins, and themes; improves interoperability, security, automation, and AI integrations.
- Key stakeholders: Core AI team, plugin/theme developers, site builders, external automation/AI tools.
- Risks and tradeoffs: Early-stage JS client; migration/learning curve; permission design mistakes could expose data if misconfigured.
- Bottom line: A unified, discoverable, secure way to define and run “abilities” will streamline dev workflows and unlock cross-context automation.
## 📖 Main Content
- Core arguments: WordPress lacks a consistent way to expose functionality; Abilities provide a registry with schemas, permission gates, and built-in REST/JS access.
- Context/definitions:
- Ability: a named, registered capability with input/output schemas and permission logic.
- Registry: list, fetch, or execute abilities; available via PHP and REST; JS client planned.
- Uncertainties: Final JS package inclusion timeline (targeting Gutenberg/WordPress 7.0); community adoption speed.
### 🗒️ Notes
- Abilities = “first-class, cross-context functional API” for WordPress.
- Goals: discoverability, interoperability via uniform schema, security-first, gradual adoption (Composer → Core).
- Example plugin “List All URLs” shows replacing bespoke REST/block wiring with one registered ability.
- Register via wp_register_ability() on wp_abilities_api_init; define label, description, category, input_schema, output_schema, execute_callback, permission_callback.
- Input/output schemas enable automatic validation; execute_callback reuses existing business logic.
- Developer ergonomics: wp_get_abilities(), wp_has_ability(); CLI-friendly inspection.
- REST: enable meta.show_in_rest to get endpoints under wp-json/wp-abilities/v1; respects permission_callback; supports list/fetch/run.
- Status: server-side approved for WordPress 6.9; JS client available in repo, aiming for Gutenberg/7.0.
- Vision: standardized surface for PHP/JS/REST and AI agents (e.g., via WordPress MCP adapter).
## 📚 References
- WordPress 6.9: Help test call (Core blog) https://make.wordpress.org/core/
- Core AI team updates https://make.wordpress.org/core/tag/ai/
- WordPress Slack #core-ai https://make.wordpress.org/chat/
- Gutenberg project https://github.com/WordPress/gutenberg
- Model Context Protocol (MCP) overview (for adapters) https://modelcontextprotocol.io/
## 🗣️ Paraphrase
- Paragraph A: The Abilities API gives WordPress a standard, secure way to register and run capabilities so other code and tools can discover and call them.
- Paragraph B: With schemas and a permission callback, the same ability can be executed from PHP, REST, or JavaScript without bespoke wiring.
## 📰 Newsletter
- WordPress 6.9 lands server-side Abilities: a unified, schema-driven way to expose plugin/theme features. Expect simpler automation, safer permissions, and smoother AI workflows. Try enabling meta.show_in_rest to get REST endpoints “for free,” and watch for the JS client maturing toward Gutenberg/7.0. Explore examples, join #core-ai, and begin migrating repeated REST/block glue to a single ability for reliability and reuse.
## ✅ Action Items
#### DevRel
- Publish examples converting existing plugin features into abilities; live demos at meetups.
- Create quickstarts: register ability, enable REST, execute from a block.
- Record office hours on input/output schemas and permission patterns.
- Track adoption metrics: abilities registered, REST opt-ins, JS executions.
#### OSPO
- Draft internal policy for registering/org-namespace abilities.
- Add review checklist for permission_callback and schema validation.
- Integrate SBOM notes for Composer package usage.
#### Community
- Run contributor sprints: convert popular hooks/REST endpoints to abilities.
- Pairing sessions for block authors to consume abilities.
#### WordPress
- Core: finalize docs, examples, and REST auth guidance.
- Core Performance: benchmark ability execution vs bespoke endpoints.
- Core AI: publish AI agent patterns invoking abilities.
- Playground: add labs showcasing abilities in PHP/REST/JS.
- Documentation: DevHub pages with examples-first patterns.
- Plugins/Themes: lint rules for schemas/permissions; sample tests.
#### The WPCC
- Microgrants for early adopters converting large plugins.
- Documentation sprints to catalog best practices.
### FAIR
- Add provenance/signing guidance for Composer installs.
- Publish standard ability charters and scorecards for security review.
## 🧪 Hypotheses
- Abilities reduce plugin code for REST/block integrations by ≥30%.
- Schema validation lowers input-related bugs by ≥25%.
- Enabling meta.show_in_rest increases API adoption among plugin users.
- Centralized permission_callback reduces privilege escalation incidents.
### 📊 Methodology
- Compare LOC and defect rates pre/post migration in sample plugins.
- A/B test REST usage with/without meta.show_in_rest.
- Security audit diff: legacy endpoints vs abilities with schemas.
### ❓ Questions
- What migration patterns best map existing REST endpoints to abilities?
- How should multi-tenant permission strategies be standardized?
- When will the JS client ship as a Gutenberg package?
### 🔮 Future Research
- Tooling for auto-generating schemas from PHP signatures.
- IDE helpers for ability discovery and type inference.
- Patterns for long-running/async abilities and batching.
### 💡 Implications
- Practice: simpler blocks/REST by executing one canonical ability.
- Policy: clearer permission points.
- Security: fewer bespoke auth paths.
- Education: teach schema+permission fundamentals.
- Funding: sponsor migration of top plugins.
### 🧠 Learning Styles Assessment
- Reading level: Flesch-Kincaid Grade ≈ 10–11.
#### 👀 Visual learners
- Present: high-level examples.
- Improve: architecture diagram of PHP/REST/JS paths.
#### 🔊 Aural learners
- Improve: short audio summary of registration+execution.
#### 🗣️ Verbal learners
- Present: definitions and step lists.
- Improve: glossary for schema fields.
#### 🧩 Kinesthetic learners
- Present: suggested steps.
- Improve: Playground lab to register/run an ability.
#### 🧠 Logical learners
- Present: clear API flow.
- Improve: decision tree for permission design.
#### 🧑🤝🧑 Social learners
- Present: Slack channel.
- Improve: cohort-based migration clinics.
#### 🧘♀️ Solitary learners
- Improve: self-check quiz on schemas/permissions.
#### 🌿 Naturalistic learners
- Improve: ecosystem diagrams showing interoperability wins.
### 📊 Accessibility Assessment
#### Screen reader semantics
- Improve: ensure code examples have descriptive captions.
#### Keyboard navigation
- Present: standard site nav.
- Improve: confirm focus states on code tabs.
#### Contrast and readability
- Present: typical dev.blog defaults.
- Improve: check 4.5:1 on code blocks.
#### Text resizing and reflow
- Present: responsive layout.
- Improve: prefers-reduced
- Summary: WordPress 6.9 introduces the Abilities API. Learn what this new Ability will unlock for developers, and how to use it in your plugins and themes now.
- URL: https://developer.wordpress.org/news/2025/11/introducing-the-wordpress-abilities-api/
Since the official announcement of the [WordPress Core AI team](https://wordpress.org/news/2025/05/announcing-the-formation-of-the-wordpress-ai-team/), one of the most exciting projects this team has been working on is the brand new [Abilities API](https://github.com/WordPress/abilities-api). The Abilities API is a **first-class, cross-context functional API that other tools and applications can use to interface with WordPress.**
This API is designed to bring a unified, discoverable, and secure approach to how WordPress core, plugins, and themes define and expose their capabilities — or “abilities.” The first implementations of the Abilities API will be [included in WordPress 6.9](https://core.trac.wordpress.org/ticket/64098), laying the foundation for smoother developer workflows, new possibilities in automation, and AI integrations.
#### What does the Abilities API offer developers?
The Abilities API provides a central registry where WordPress capabilities (or abilities) are registered in a format that’s both machine-readable and human-friendly. This means that abilities are not only discoverable by developers but also accessible programmatically by automation tools across different platforms, including [AI agents](https://github.com/resources/articles/what-are-ai-agents).
The key goals of this project are:
* **Discoverability:** List and inspect all available abilities through a standard interface.
* **Interoperability:** A uniform schema enables unrelated components to compose [workflows](https://github.com/WordPress/gutenberg/issues/70710).
* **Security-first:** Explicit permission control on who or what can invoke abilities.
* **Gradual Adoption:** Starting as a [Composer](https://getcomposer.org/) package plugin with plans to smoothly migrate to WordPress core.
Think of it as a one-stop shop for what WordPress or any plugin/theme can do, registered in a way everyone (and everything) can understand.
Now, I don’t know about you, but I find it easier to understand how something works when I can see it in action. To do that, I’m going to implement Abilities in a plugin I maintain, List All URLs.
#### List All URLs
The plugin itself is minimalist. It registers a **Tools** sub-menu item that opens an admin page where you can select to list the URLs of all Posts, Pages, or Custom Post Types on your WordPress site.
List All URLs admin page
List All URLs admin page rendering the list of URLs
There is one primary function that powers the fetching and display of the data, the `list_all_urls_generate_url_list()` [function](https://github.com/wptrainingteam/list-all-urls/blob/c61ef4a4ea36acf279a8a2ff9038737db1bdbd25/list-all-urls.php#L49). This function receives two arguments and generates the list of URLs to display on the page.
| | |
| --- | --- |
| 123456789101112131415161718192021222324252627282930 | `/**``* Generate a list of URLs based on the provided arguments``* Optionally make them clickable links``*``* @param array $arguments Arguments to customize the URL generation.``* @param bool $makelinks Whether to return clickable links or plain URLs (escaped).``*``* @return array List of generated URLs.``*/``function` `list_all_urls_generate_url_list(` `array` `$arguments` `=` `array``(), bool` `$makelinks` `= false ):` `array` `{``$default_args` `=` `array``(``'post_type'` `=>` `'post'``,``'posts_per_page'` `=> - 1,``'post_status'` `=>` `'publish'``,``);``$args` `= wp_parse_args(` `$arguments``,` `$default_args` `);``$posts` `= get_posts(` `$args` `);``$links` `=` `array``();``foreach` `(` `$posts` `as` `$post` `) {``$permalink` `= get_permalink(` `$post` `);``if` `(` `$makelinks` `) {``$links``[] =` `'<a href="'` `. esc_url(` `$permalink` `) .` `'">'` `. esc_html(` `$permalink` `) .` `'</a>'``;``}` `else` `{``$links``[] = esc_html(` `$permalink` `);``}``}``return` `$links``;``}` |
Internally, this function calls the WordPress `get_posts()` function to retrieve the actual data and then formats it based on whether it should be displayed as a list of clickable links or not.
There are a couple of smaller quality-of-life improvements I’d like to add, such as the option to limit results for sites with a large number of URLs, the ability to filter URLs by category or date range, and the option to export the returned URLs.
However, two of the larger features that might be useful to add are:
1. A way to access the URL list outside of WordPress (ie, a REST API endpoint) to be able to hook the data into external services.
2. A block that would allow anyone to add the list of URLs to any implementation of the Block Editor (ie, on any post or page, or even in a template).
To implement this, you need to set up a few things.
If you prefer browsing the code for this solution, you can see the full implementation in action by checking out the `rest-blocks` [branch](https://github.com/wptrainingteam/list-all-urls/tree/rest-blocks) of the GitHub repository.
You’ll need to register a custom REST API route and associated GET endpoint using `register_rest_route()`, which should get the posts for the route.
That would need a callback function to fetch the data, which in turn could call `list_all_urls_generate_url_list` without the `$makelinks` parameter to return the data.
For the custom block, you could scaffold the block structure using [create-block](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/), and then utilize the custom REST API endpoint and the [api-fetch](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-api-fetch/) package to fetch the data for the block’s `Edit` component and render it in the Editor.
It would make sense to make it a dynamic block that calls the `list_all_urls_generate_url_list` function in a `render.php` file, which is configured to render on the front end.
To give users the ability to select the post types to generate the list of URLs, and to add clickable links to the list you would need to update the block to support additional attributes.
However, this is already a significant amount of code just to make it possible to access the list URLs functionality via the REST API and the Block Editor, while maintaining the existing admin page.
This is the perfect example of custom Ability. You can wrangle most of that by registering it all in one place, and then accessing and executing it everywhere you need to.
#### Installing the Abilities API
To get started, make sure you are working with the latest version of the API.
Currently, there are three ways you can install and test the Abilities API:
* You can clone the [GitHub repository](https://github.com/WordPress/abilities-api/) into your `wp-content/plugins` directory, install any dependencies, run the build steps, and activate the plugin.
* You can download the latest version from the [releases](https://github.com/WordPress/abilities-api/releases) page of the GitHub repository, and then upload and install the plugin zip file.
* You can require the [Composer package](https://packagist.org/packages/wordpress/abilities-api) as a dependency of your plugin or theme.
One method for testing the Abilities API is to clone the GitHub repository, as this ensures you have the latest version of the code on the `trunk` branch.
Another option is to install the composer package. This allows your plugin to access the latest stable version of the Abilities API and any new features it might offer, while being compatible with what is available in Core.
#### Abilities to the rescue
In this case, a custom Ability can handle most, if not all, of the required core functionality. The [PHP docs](https://github.com/WordPress/abilities-api/blob/trunk/docs/php-api.md) contain everything you need on how to register and use Abilities in PHP, but let’s take a look at what I need for List All URLs.
Registering an Ability in PHP is possibly using the `wp_register_ability()` function. To ensure the Ability is registered correctly, this function should always be called inside a callback hooked into the `wp_abilities_api_init` action hook.
#### Diving into Ability registration
Registering an Ability requires a unique identifier (`list-all-urls/urls`) and an array of arguments. Most of the arguments are optional, but the required ones are:
* `label`: A human-readable name for the Ability.
* `description`: A brief description of what the Ability does.
* `category`: The category under which the Ability is grouped. You can register your own Ability Categories, but in this case, I’m using the available `site` category
* `output_schema`: The schema that defines the structure of the data returned by the Ability.
* `execute_callback`: The function that will be called when the Ability is executed.
* `permission_callback`: A function that determines whether the current user has permission to execute the Ability.
The `input_schema` argument is optional, but highly recommended if your Ability requires input parameters. In my case, I’d like to pass in the same arguments that `list_all_urls_generate_url_list()` accepts.
Setting these schemas not only lets the Ability know what data it can expect and return, but also enables automatic validation of that data. For example, if I tried passing a `posts_per_page` value in the input that didn’t evaluate to an integer, it would automatically cause a validation error and stop the Ability from executing.
You’ll notice that I’m using the original `list_all_urls_generate_url_list()` function as the Ability `execute_callback`, so nothing has to change there.
##### Getting and using an ability in PHP
The next step is to update the internal admin page to fetch and execute the ability.
All that really needs to change here is setting up the `$input` array, fetching the ability, and executing it.
Now, what I really like about this implementation is how extendable this is. If I want to allow other plugin or theme developers to utilize this functionality, all I have to do is document the ability identifier, the input schema, and the output schema.
There are also functions available to both check which abilities are available — [`wp_get_abilities()`](https://github.com/WordPress/abilities-api/blob/trunk/docs/php-api.md#getting-all-registered-abilities-wp_get_abilities) — and whether a specific Ability is available or not — [`wp_has_ability()`](https://github.com/WordPress/abilities-api/blob/trunk/docs/php-api.md#checking-if-an-ability-is-registered).
During development, you could use these functions using the [WP-CLI](https://wp-cli.org/) [`shell`](https://developer.wordpress.org/cli/commands/shell/) command to fetch and inspect individual abilities.
For example, checking which abilities are currently available:
Checking if a specific Ability is available:
Fetching a single Ability:
When fetching all Abilities and individual Abilities, the whole Ability object is returned, so you’re able to see what function the Ability performs, and what the expected inputs and outputs are.
##### REST API support out of the box
Another really cool thing about Abilities is that you can enable their REST API endpoints in a similar way as when creating a Custom Post Type. If you enable the `meta.show_in_rest` argument on the Ability it will support the [Abilities REST API endpoints](https://github.com/WordPress/abilities-api/blob/trunk/docs/rest-api.md) by default.
The [Abilities REST API endpoints](https://github.com/WordPress/abilities-api/blob/trunk/docs/rest-api.md) are available under the `wp-json/wp-abilities/v1` namespace. Access to the Abilities REST API endpoints requires an authenticated user, which supports all the same authentication methods as the WordPress REST API.
As with the PHP functions, you can perform a series of default Ability actions using this namespace, including:
* listing all Abilities by sending a *GET* request to `/wp-json/wp-abilities/v1/abilities`
* retrieving a single Ability by sending a *GET* request to `wp-json//wp-abilities/v1/{namespace/ability}`, where `{namespace/ability}` is the unique ID of your registered Ability (eg `list-all-urls/urls`)
* executing an Ability by sending either a *GET* or *POST* request (depending on the Ability’s `readonly` setting) to `/wp-json/wp-abilities/v1/{namespace/ability}/run`.
What’s more, the `permission_callback` set during Ability registration is also respected when executing Abilities via the REST API. This ensures that only authenticated users with the correct permissions can execute any given Ability via the REST API.
All that, just by enabling a single argument on registration!
#### Abilities are coming to Core!
The server-side (PHP) Abilities registration, retrieval, and execution, including REST API support, was recently [approved to be merged in WordPress 6.9](https://core.trac.wordpress.org/ticket/64098). I[n early December](https://make.wordpress.org/core/6-9/), you’ll have access to all this functionality out of the box in WordPress. You can even help test it right now by following the instructions in the [Help Test WordPress 6.9](https://make.wordpress.org/test/2025/10/21/help-test-wordpress-6-9/#introducing-the-abilities-api) post.
#### Using an ability in JavaScript
Now, you might be thinking, “Ok, but I still have to use something like the `fetch-api` to fetch the data from the REST API endpoints?” Well, dear reader, that’s the fun part.
The Abilities API also includes a [JavaScript client](https://github.com/WordPress/abilities-api/blob/trunk/docs/javascript-client.md), which provides built-in support for fetching and executing custom Abilities in the browser.
Currently, the JavaScript client is only available in the GitHub repository; however, the goal is to eventually ship it as a Gutenberg package. As a result, there’s a good chance it will be included in WordPress core by version 7.0 (or possibly earlier via the Gutenberg plugin).
If you require the Abilities API as a composer package, you can start using it in your plugins or themes without causing any conflicts with the Core Abilities API.
The JavaScript client also ships with functions to list, fetch and execute custom Abilities, as well as a function to create custom Abilities in JavaScript. In the case of List all URLs, you only need the function to execute its Ability.
And then you can execute the Ability, passing it a JSON object for the required input, and use the data it returns in my block.
#### Where to learn more and get involved
* Follow the development of the Abilities API in the official [GitHub repository](https://github.com/WordPress/abilities-api) and be sure to [read the docs](https://github.com/WordPress/abilities-api/tree/trunk/docs).
* Join discussions on the WordPress Slack [#core-ai channel](https://wordpress.slack.com/archives/C08TJ8BPULS/p1747960962509329) or follow the [Core AI team blog](https://make.wordpress.org/ai/) for updates and news.
#### This is just the beginning..
Think of all the different ways in which WordPress core, plugins, and themes make their capabilities available to developers. WordPress doesn’t currently force a standard method to build and communicate a public API, so the available options could be anything from a series of action and filter hooks, to publicly accessible global functions, to a series of REST API endpoints, to objects that are meant to be extended, or any combination of all these.
Abilities solves this problem by allowing WordPress developers to define a standardized and discoverable method to expose common functionalities. These Abilities can then be accessed and executed from the application (PHP) and presentation (JavaScript) layers of WordPress, as well as by external applications (REST API), all in the same standardized way.
Not only that, but if you combine Abilities with something like the [WordPress MCP adapter](https://github.com/WordPress/mcp-adapter), you can even allow any AI agents to interact with your Abilities, unlocking even more possibilities. (Now that’s a blog post for another day!)
The Abilities API is an exciting new way to develop with WordPress, and I look forward to seeing what it enables for the future of WordPress.
*Props to [@juanmaguitar](https://profiles.wordpress.org/juanmaguitar/), [@bph](https://profiles.wordpress.org/bph/) and [@areziaal](https://profiles.wordpress.org/areziaal/) for feedback and review on this article.*, Introducing the WordPress Abilities API, WordPress Developer Blog, 01ka16sqgnt0bzwgfpxftmjz0g.