Abilities API in WordPress 6.9
WordPress 6.9 adds the Abilities API - this new system allows plugins, themes and WordPress core to describe their features in a single, machine-readable format.
Now any site capability can be registered in a way that it can be found, verified and invoked from various places: PHP, via REST API and in future AI tools.
This API is part of the “AI Building Blocks for WordPress” initiative and lays the groundwork for AI agents and automation. Thanks to it, external systems and developers will be able to precisely understand what functions are available on the site, how exactly they work.
What is the Abilities API?
The Abilities API in WordPress provides a standardized way to register separate units of functionality within the site. These units are concrete actions or capabilities that components can perform, with clearly defined inputs, outputs and permissions.
The API acts as a central registry, simplifying interaction between different parts of WordPress, third-party plugins, themes and external systems (for example, AI agents), helping them understand the capabilities available on the site.
Ability - an autonomous unit of functionality with clearly defined inputs and outputs, permissions, and execution logic.
Goals and benefits
- Standardization: A single way to describe site functionality.
- Validation: Built-in validation of inputs and outputs via JSON Schema.
- Security: Flexible access control through permission callbacks.
- Extensibility: Simple registration of abilities in any plugin or theme.
- Discovery: Functionality easily open to AI and automation.
- AI-oriented: Machine-readable format for integration with AI agents.
Registering abilities through the Abilities API enables developers to:
- Create functionality with standardized interfaces.
- Define access checks and execution callbacks.
- Organize abilities by logical categories.
- Validate inputs and outputs.
- Automatically publish abilities through REST API endpoints.
Use cases
- AI integrations: Allow agents to open and perform actions on the site.
- Plugin interactions: Plugins can discover and use each other’s functionality.
- Automation tools: Programmatic access to site capabilities.
- API documentation: Self-documenting functionality with schema validation.
- Developer tools: Standardized way to expose plugin functions.
API components:
Core concepts
-
Ability: A separate functional unit with a unique name in the format namespace/ability-name. Each ability has a human-readable name, description, input and output definitions (via JSON Schema), a category, optional permissions and a handler function. Each registered ability is an instance of WP_Ability.
-
Category: Ability to group related abilities. Each ability must belong to exactly one category. Categories have a slug, label and description. Each category is an instance of WP_Ability_Category.
-
Registry: Central singleton WP_Abilities_Registry, storing all registered abilities. It provides methods to register, delete, search and fetch abilities. Similarly, WP_Abilities_Category_Registry manages categories.
-
Callback: PHP function or method that executes when the ability is invoked via WP_Ability::execute().
-
Schema: JSON Schema for input data (input_schema) and output (output_schema). This provides validation and helps agents understand how to use the ability.
-
Permission Callback: Optional function that defines whether the current user can execute the ability.
- Namespace: The first part of the ability name (before the slash). Usually matches the plugin or component that registers the ability.
Using abilities
Abilities must be registered in the hook wp_abilities_api_init. Attempting to register abilities outside this hook will trigger a notification _doing_it_wrong(), and the ability registration will fail.
Basic usage example
The example below is intended for implementation in a plugin, but it can also be adapted for a theme’s functions.php file:
// 1. Define the callback function for the ability
function my_plugin_get_site_title( array $input = array() ): string {
return get_bloginfo( 'name' );
}
// 2. Register the ability
add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
function my_plugin_register_abilities() {
wp_register_ability( 'my-plugin/get-site-title', array(
'label' => __( 'Get Site Title', 'my-plugin' ),
'description' => __( 'Retrieves the title of the current WordPress site.', 'my-plugin' ),
'category' => 'site-info',
'input_schema' => [
'type' => 'object',
'properties' => [],
'additionalProperties' => false,
],
'output_schema' => [
'type' => 'string',
'description' => 'The site title.',
],
'execute_callback' => 'my_plugin_get_site_title',
'permission_callback' => '__return_true', // Everyone can access
'meta' => [
'show_in_rest' => true, // enable REST API support
],
) );
}
// 3. Then, you can get and execute the ability
add_action( 'admin_init', 'my_plugin_use_ability' );
function my_plugin_use_ability() {
$ability = wp_get_ability( 'my-plugin/get-site-title' );
if ( ! $ability ) {
return;
}
$site_title = $ability->execute();
if ( is_wp_error( $site_title ) ) {
error_log( 'Execution error: ' . $site_title->get_error_message() );
return;
}
// $site_title now contains the result
echo 'Site Title: ' . esc_html( $site_title );
}
A more complex registration example
Example with more complex input and output schemas, validation and error handling:
add_action( 'wp_abilities_api_init', 'my_register_text_analysis_ability' );
function my_register_text_analysis_ability() {
wp_register_ability(
'my-plugin/analyze-text',
[
'label' => __( 'Analyze Text', 'my-plugin' ),
'description' => __( 'Performs sentiment analysis on provided text.', 'my-plugin' ),
'category' => 'text-processing',
'input_schema' => [
'type' => 'object',
'required' => [ 'text' ],
'properties' => [
'text' => [
'type' => 'string',
'description' => __( 'The text to analyze.', 'my-plugin' ),
'minLength' => 1,
'maxLength' => 5000,
],
'options' => [
'type' => 'object',
'properties' => [
'include_keywords' => [
'type' => 'boolean',
'description' => __( 'Whether to extract keywords.', 'my-plugin' ),
'default' => false,
],
],
],
],
],
'output_schema' => [
'type' => 'object',
'properties' => [
'sentiment' => [
'type' => 'string',
'enum' => [ 'positive', 'neutral', 'negative' ],
'description' => __( 'The detected sentiment.', 'my-plugin' ),
],
'confidence' => [
'type' => 'number',
'minimum' => 0,
'maximum' => 1,
'description' => __( 'Confidence score for the sentiment.', 'my-plugin' ),
],
'keywords' => [
'type' => 'array',
'items' => [
'type' => 'string',
],
'description' => __( 'Extracted keywords (if requested).', 'my-plugin' ),
],
],
],
'execute_callback' => 'my_plugin_analyze_text',
'permission_callback' => fn() => current_user_can( 'edit_posts' ),
]
);
}
/**
* Callback for analyze-text ability.
*/
function my_plugin_analyze_text( array $input ): array {
$text = $input['text'];
$include_keywords = $input['options']['include_keywords'] ?? false;
// Perform analysis (simplified example).
$sentiment = 'neutral';
$confidence = 0.75;
$result = [
'sentiment' => $sentiment,
'confidence' => $confidence,
];
if ( $include_keywords ) {
$result['keywords'] = [ 'example', 'keyword' ];
}
return $result;
}
Categories
Each ability must be tied to a category. Categories improve search usability and help group related abilities. Categories must be registered in advance, via the hook wp_abilities_api_categories_init.
// First register the category
add_action( 'wp_abilities_api_categories_init', 'my_plugin_register_category' );
function my_plugin_register_category() {
wp_register_ability_category( 'site-information', [
'label' => 'Site Information',
'description' => 'Abilities that provide information about the WordPress site.',
] );
}
// Then register the ability in this category
add_action( 'wp_abilities_api_init', 'my_plugin_register_ability' );
function my_plugin_register_ability() {
wp_register_ability( 'my-plugin/site-info', [
'category' => 'site-information',
'label' => 'Site Info',
'description' => 'Returns information about this WordPress site',
'execute_callback' => 'my_plugin_get_siteinfo',
'permission_callback' => fn ( $input ) => current_user_can( 'manage_options' ),
'meta' => [
'show_in_rest' => true,
],
'input_schema' => [],
'output_schema' => [
'type' => 'object',
'properties' => [
'site_name' => [
'type' => 'string',
'description' => 'The name of the WordPress site',
],
'site_url' => [
'type' => 'string',
'description' => 'The URL of the WordPress site',
],
'active_theme' => [
'type' => 'string',
'description' => 'The active theme of the WordPress site',
],
'active_plugins' => [
'type' => 'array',
'description' => 'List of active plugins on the WordPress site',
'items' => [
'type' => 'string',
],
],
],
],
] );
}
PHP Functions and Hooks API
Functions
Manage abilities:
- wp_register_ability() — register a new ability
- wp_unregister_ability() — remove an ability from the registry
- wp_has_ability() — check if an ability is registered
- wp_get_ability() — get a registered ability
- wp_get_abilities() — get all registered abilities
Managing categories of abilities:
- wp_register_ability_category() — register an ability category
- wp_unregister_ability_category() — remove a category from the registry
- wp_has_ability_category() — check if a category is registered
- wp_get_ability_category() — get a registered category
- wp_get_ability_categories() — get all ability categories
Hooks
New hooks for integration with the Abilities API.
Events:
- wp_abilities_api_categories_init - triggered during initialization of the registry for ability categories (categories are registered here)
- wp_abilities_api_init - triggered during initialization of the registry for abilities (abilities are registered here)
- wp_before_execute_ability - triggered before executing an ability
- wp_after_execute_ability - triggered after executing an ability
Filters:
- wp_register_ability_category_args - filters category arguments for abilities before registration
- wp_register_ability_args - filters ability arguments before registration
Naming Convention
Ability Name Convention
It is recommended to follow these naming rules for abilities:
-
Use namespaced names to avoid conflicts, for example
my-plugin/my-ability. -
Use only lowercase Latin letters, digits, dashes and slashes.
- Use descriptive, action-oriented names, for example
process-payment,generate-report. - General format:
namespace/ability-name.
Category Slug Convention
wp_register_ability_category( string $slug, array $args )
Parameter $slug must adhere to the following rules:
-
Format: may contain only
a-z, 0-9(lowercase alphanumeric) and-(dashes). -
Correct examples:
data-retrieval,ecommerce,site-information,user-management,category-123 - Incorrect examples:
- Uppercase letters:
Data-Retrieval,MyCategory - Underscores:
data_retrieval - Special characters:
data.retrieval,data/retrieval,data retrieval - Dash at the start/end:
-data,data- - Double dashes:
data--retrieval
- Uppercase letters:
Checking and retrieving abilities
You can check for the existence of an ability and retrieve it programmatically (see WP_Ability):
<?php
if ( wp_has_ability( 'my-plugin/get-post-count' ) ) {
$ability = wp_get_ability( 'my-plugin/get-post-count' );
echo $ability->get_label();
echo $ability->get_description();
}
// get all abilities
$all_abilities = wp_get_abilities();
foreach( $all_abilities as $ability ){
echo $ability->get_name();
}
Error handling
Abilities should properly handle errors, returning WP_Error objects:
<?php
function my_plugin_delete_post( $input ) {
$post_id = $input['post_id'];
if ( ! get_post( $post_id ) ) {
return new WP_Error(
'post_not_found',
__( 'The specified post does not exist.', 'my-plugin' )
);
}
$result = wp_delete_post( $post_id, true );
if ( ! $result ) {
return new WP_Error(
'deletion_failed',
__( 'Failed to delete the post.', 'my-plugin' )
);
}
return array(
'success' => true,
'post_id' => $post_id,
);
}
Validation via JSON Schema (REST API)
The Abilities API uses JSON Schema to validate inputs and outputs. Schemas perform two tasks:
- Automatic validation of data passed to abilities.
- Self-documenting API contracts for developers.
Defining schemas is mandatory if there are values that must be passed or returned.
Using REST API Endpoints
When the Abilities API is enabled, the registered abilities can be automatically published via REST API in the namespace wp-abilities/v1:
GET /wp-abilities/v1/categories- list of all ability categoriesGET /wp-abilities/v1/categories/{slug}- data for a single categoryGET /wp-abilities/v1/abilities- list of all abilitiesGET /wp-abilities/v1/abilities/{name}- data for a single abilityGET|POST|DELETE /wp-abilities/v1/abilities/{name}/run- run ability
Developers can enable support for standard REST API endpoints for abilities. This is done via the argument show_in_rest = true when registering an ability.
wp_register_ability( 'my-plugin/get-post-count', [ 'label' => __( 'Get Post Count', 'my-plugin' ), 'description' => __( 'Retrieves the total number of published posts.', 'my-plugin' ), 'category' => 'content-management', 'input_schema' => [ 'type' => 'string', 'description' => __( 'The post type to count.', 'my-plugin' ), 'default' => 'post', ], 'output_schema' => [ 'type' => 'integer', 'description' => __( 'The number of published posts.', 'my-plugin' ), ], 'execute_callback' => 'my_plugin_get_post_count', 'permission_callback' => fn() => current_user_can( 'read' ), 'meta' => [ 'show_in_rest' => true, ], ] );
Access to all REST endpoints of the Abilities API requires an authenticated user. The Abilities API supports all WordPress REST API authentication methods:
- Cookie authentication (same-origin requests).
- Application passwords (recommended for external access).
- Custom authentication plugins.
After enabled, you can list, view details and run abilities via REST API.
List all abilities:
curl -u 'USERNAME:APPLICATION_PASSWORD' \ https://example.com/wp-json/wp-abilities/v1/abilities
Get a single ability:
curl -u 'USERNAME:APPLICATION_PASSWORD' \ https://example.com/wp-json/wp-abilities/v1/abilities/my-plugin/get-post-count
Run an ability:
curl -u 'USERNAME:APPLICATION_PASSWORD' \
-X POST https://example.com/wp-json/wp-abilities/v1/abilities/my-plugin/get-post-count/run \
-H "Content-Type: application/json" \
-d '{"input": {"post_type": "page"}}'
The API automatically validates input data against the schema, checks permissions via the permission_callback, executes the ability, validates the result against output_schema and returns a JSON-formatted response.
Backward compatibility
The Abilities API is a new feature in WordPress 6.9 and does not change existing functionality. Plugins and themes can gradually adopt the API without breaking current code.
Developers who want to support both WordPress 6.9+ and older versions should first check for the existence of API functions before using them:
if ( function_exists( 'wp_register_ability' ) ) {
add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
}
// or
if ( class_exists( 'WP_Ability' ) ) {
add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
}
Additional materials
- Source DOC: https://github.com/WordPress/abilities-api/tree/trunk/docs
- GitHub Repo: https://github.com/WordPress/abilities-api/
- Core Trac Ticket #64098
- Abilities API Handbook
- AI Building Blocks Initiative
--
Source: https://make.wordpress.org/core/2025/11/10/abilities-api-in-wordpress-6-9/