WordPress 6.9 introduces a feature that fundamentally changes how we think about extending WordPress functionality: the Abilities API. This new system allows developers to register discrete, self-documenting capabilities that can be discovered and executed by AI assistants, external tools, or programmatically within your own code.
In this comprehensive guide, I’ll walk you through everything you need to know about the Abilities API, from basic registration to advanced usage patterns, complete with real-world code examples.
What is the WordPress Abilities API?
The Abilities API is a standardized way to register custom functions (called “abilities”) in WordPress. Each ability is a self-contained unit of functionality with:
- A unique identifier (namespaced slug)
- Input schema defining what parameters it accepts
- Output schema describing what it returns
- Execute callback containing the actual logic
- Permission callback for security control
- Metadata for REST API exposure and discoverability
Think of it as creating a well-documented menu of actions your WordPress site can perform. Unlike traditional custom functions scattered throughout your codebase, abilities are discoverable, validated, and accessible through multiple channels.
Why the Abilities API Matters
AI Integration
The most compelling reason to adopt the Abilities API is AI integration. As AI assistants become more prevalent, having standardized, discoverable endpoints means your WordPress site can interact with AI tools without custom integration work.
Standardization
Every ability follows the same pattern with JSON Schema validation. This means predictable inputs, outputs, and error handling across your entire codebase.
Security
Built-in permission callbacks ensure abilities are only executed by authorized users. No more scattered capability checks throughout your code.
Discoverability
Abilities can be exposed via the REST API, making them accessible to external tools, mobile apps, or any HTTP client.
Getting Started: Registering Your First Ability
Let’s start with a practical example. We’ll create an ability that counts published posts for any post type.
Step 1: Register an Ability Category (Optional)
Categories help organize related abilities. Register them using the wp_abilities_api_categories_init hook:
function mytheme_register_ability_categories() {
wp_register_ability_category(
'site-utilities',
array(
'label' => __( 'Site Utilities', 'mytheme' ),
'description' => __( 'Administrative and content management tools', 'mytheme' ),
)
);
}
add_action( 'wp_abilities_api_categories_init', 'mytheme_register_ability_categories' );Step 2: Register the Ability
Use the wp_abilities_api_init hook to register abilities:
function mytheme_register_abilities() {
// Ensure the Abilities API exists (WP 6.9+)
if ( ! function_exists( 'wp_register_ability' ) ) {
return;
}
wp_register_ability(
'mytheme/get-post-count',
array(
'label' => __( 'Get Post Count', 'mytheme' ),
'description' => __( 'Returns the total number of published posts for a given post type.', 'mytheme' ),
'category' => 'site-utilities',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_type' => array(
'type' => 'string',
'description' => __( 'Post type slug (e.g., post, page, product)', 'mytheme' ),
),
),
'required' => array( 'post_type' ),
),
'output_schema' => array(
'type' => 'object',
'properties' => array(
'post_type' => array(
'type' => 'string',
'description' => __( 'Post type slug', 'mytheme' ),
),
'count' => array(
'type' => 'integer',
'description' => __( 'Number of published posts', 'mytheme' ),
),
),
),
'execute_callback' => 'mytheme_ability_get_post_count',
'permission_callback' => 'mytheme_ability_permission_check',
'meta' => array(
'show_in_rest' => true,
),
)
);
}
add_action( 'wp_abilities_api_init', 'mytheme_register_abilities' );Step 3: Create the Execute Callback
This function contains your actual logic:
function mytheme_ability_get_post_count( $args ) {
$post_type = sanitize_text_field( $args['post_type'] );
// Validate post type exists
if ( ! post_type_exists( $post_type ) ) {
return array(
'post_type' => $post_type,
'count' => 0,
);
}
$counts = wp_count_posts( $post_type );
return array(
'post_type' => $post_type,
'count' => isset( $counts->publish ) ? (int) $counts->publish : 0,
);
}Step 4: Create the Permission Callback
Control who can execute the ability:
function mytheme_ability_permission_check() {
// Require admin capabilities for this ability
return current_user_can( 'manage_options' );
}Three Ways to Execute Abilities
One of the most powerful aspects of the Abilities API is the flexibility in how abilities can be called. Let’s explore all three methods.
Method 1: PHP Code (Internal Execution)
Use this when calling abilities from within your theme or plugin code:
add_action( 'admin_init', function() {
// Check if Abilities API exists
if ( ! function_exists( 'wp_get_ability' ) ) {
return;
}
// Get the ability instance
$ability = wp_get_ability( 'mytheme/get-post-count' );
if ( $ability ) {
// Execute with parameters
$result = $ability->execute( array(
'post_type' => 'post',
) );
// Use the result
error_log( 'Post count: ' . $result['count'] );
}
} );This method is ideal for:
- Cron jobs that need to perform ability actions
- Admin dashboard widgets
- Internal automation workflows
- Testing abilities during development
Method 2: REST API (External HTTP Calls)
When show_in_rest is enabled in the ability meta, it becomes accessible via the WordPress REST API:
curl -X POST \
-u "username:application_password" \
-H "Content-Type: application/json" \
-d '{
"input": {
"post_type": "post"
}
}' \
https://yoursite.com/wp-json/wp-abilities/v1/abilities/mytheme/get-post-count/runResponse:
{
"post_type": "post",
"count": 42
}This method is perfect for:
- External applications integrating with WordPress
- Mobile apps
- Third-party services and webhooks
- Automation tools like Zapier or Make
- Command-line scripts
Authentication Options:
- Application passwords (recommended)
- OAuth
- JWT tokens (with appropriate plugin)
- Cookie authentication (for logged-in users)
Method 3: AI Natural Language (AI Assistant Integration)
This is where the Abilities API truly shines. AI assistants that understand the abilities schema can execute them through natural language:
Example Prompt:
Using the available ability, tell me how many pages are published on the site.
API credentials: username:application_passwordThe AI assistant will:
- Discover the available ability
- Understand its input requirements
- Make the appropriate API call
- Return human-readable results
This method enables:
- Voice-controlled WordPress management
- Chat-based content administration
- AI-powered site analytics
- Natural language automation
Advanced Example: Empty Trash Ability
Let’s look at a more complex ability that performs a destructive action—emptying the trash for a specific post type:
wp_register_ability(
'mytheme/empty-trash',
array(
'label' => __( 'Empty Trash', 'mytheme' ),
'description' => __( 'Permanently deletes all trashed posts for a given post type.', 'mytheme' ),
'category' => 'site-utilities',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_type' => array(
'type' => 'string',
'description' => __( 'Post type slug', 'mytheme' ),
),
),
'required' => array( 'post_type' ),
),
'output_schema' => array(
'type' => 'string',
'description' => __( 'Success or error message', 'mytheme' ),
),
'execute_callback' => 'mytheme_ability_empty_trash',
'permission_callback' => function() {
// Require delete capabilities
return current_user_can( 'delete_others_posts' );
},
'meta' => array(
'show_in_rest' => true,
),
)
);
function mytheme_ability_empty_trash( $args ) {
$post_type = sanitize_text_field( $args['post_type'] );
if ( ! post_type_exists( $post_type ) ) {
return __( 'Post type does not exist.', 'mytheme' );
}
$trashed_posts = get_posts( array(
'post_type' => $post_type,
'post_status' => 'trash',
'posts_per_page' => -1,
'fields' => 'ids',
) );
foreach ( $trashed_posts as $post_id ) {
wp_delete_post( $post_id, true );
}
return sprintf(
__( 'Successfully deleted %d trashed %s.', 'mytheme' ),
count( $trashed_posts ),
$post_type
);
}Best Practices
1. Always Use Permission Callbacks
Never set permission callbacks to return true unconditionally in production:
// DON'T do this in production
'permission_callback' => '__return_true',
// DO this instead
'permission_callback' => function() {
return current_user_can( 'appropriate_capability' );
},2. Validate and Sanitize Inputs
Even though the API validates against your schema, always sanitize within your callback:
function my_callback( $args ) {
$post_type = sanitize_text_field( $args['post_type'] );
$limit = absint( $args['limit'] ?? 10 );
// ...
}3. Use Descriptive Schemas
Your input and output schemas serve as documentation. Make them detailed:
'input_schema' => array(
'type' => 'object',
'properties' => array(
'post_type' => array(
'type' => 'string',
'description' => 'The post type slug to query. Examples: post, page, product, event.',
'enum' => array( 'post', 'page' ), // Optional: restrict values
),
),
),4. Handle Errors Gracefully
Return meaningful error messages:
function my_callback( $args ) {
if ( ! post_type_exists( $args['post_type'] ) ) {
return new WP_Error(
'invalid_post_type',
__( 'The specified post type does not exist.', 'mytheme' ),
array( 'status' => 400 )
);
}
// ...
}5. Namespace Your Abilities
Always use a namespace prefix to avoid conflicts:
// Good
'mytheme/get-post-count'
'myplugin/send-notification'
// Bad
'get-post-count'
'send-notification'Debugging and Testing
Check if Abilities API is Active
add_action( 'wp_abilities_api_init', function() {
error_log( 'Abilities API initialized successfully' );
} );List All Registered Abilities
add_action( 'admin_init', function() {
if ( function_exists( 'wp_get_abilities' ) ) {
$abilities = wp_get_abilities();
error_log( print_r( $abilities, true ) );
}
} );Test via REST API
Use tools like Postman, Insomnia, or curl to test your abilities before integrating them into applications.
Conclusion
The WordPress Abilities API represents a significant step forward in how we build and extend WordPress. By providing a standardized way to register discoverable, secure, and self-documenting functions, it opens the door to seamless AI integration and external tool connectivity.
Whether you’re building themes, plugins, or custom WordPress applications, adopting the Abilities API now will future-proof your code for the AI-native web.
Start simple with read-only abilities like post counts or site statistics, then gradually add more complex functionality as you become comfortable with the patterns.
The sites and plugins that embrace this API early will have a significant advantage as AI assistants become the primary way users interact with their WordPress installations.