I had a client with a simple request. We’d built a small utility plugin for them that listed out all the URLs of a specific post type on a private admin page. Simple enough. But then came the follow-up: “Can we get that same list, but put it in a block on the homepage? Oh, and our marketing team needs to pull those URLs into an external tool via an API.”
My first thought was, “Here we go.” This is classic scope creep that turns a clean little plugin into a tangled mess. The old way meant I’d have to register a new REST route, write its callback function, then build a custom block, and then use apiFetch in JavaScript to hit my own new endpoint. The core logic—getting a list of URLs—would be duplicated or awkwardly abstracted. It’s tedious, and it’s how you end up with spaghetti code. A total nightmare to maintain. But now, the new WordPress Abilities API changes the entire approach.
So, What’s the WordPress Abilities API, Really?
For years, we’ve had no standard way to say “this is what my plugin can do.” We use hooks, public functions, REST endpoints… it’s the wild west. The Abilities API, which I first read about on the official WordPress Developer Blog, aims to fix that. It creates a central, machine-readable registry for what WordPress and its plugins can do. Think of it as a universal remote for WordPress functionality. Instead of building three separate entry points for my client’s URL-listing feature, I can register a single “ability” and let WordPress handle the rest.
My first instinct was to just build the separate REST endpoint and block like I’ve done a hundred times. And yeah, it would have worked. But it felt wrong. It’s not a scalable solution. The moment the client asks for another parameter—like filtering by date—I’d have to update it in multiple places. Not good. The real fix is to centralize the capability itself.
Here’s the kicker. With the Abilities API, I define the function once. Trust me on this, it’s a big deal. I register my URL-listing function as an “ability,” and the API handles the REST API endpoint and makes it available to other tools automatically.
<?php
add_action( 'wp_abilities_api_init', 'my_plugin_register_abilities' );
function my_plugin_register_abilities() {
wp_register_ability(
'my-plugin/get-urls',
[
'label' => __( 'Get All URLs', 'my-plugin' ),
'description' => __( 'Retrieves a list of URLs for a given post type.', 'my-plugin' ),
'execute_callback' => 'my_plugin_generate_url_list',
'permission_callback' => 'is_user_logged_in', // Or something more robust
'input_schema' => [
'type' => 'object',
'properties' => [
'post_type' => [ 'type' => 'string' ],
'makelinks' => [ 'type' => 'boolean' ],
],
],
'output_schema' => [
'type' => 'array',
'items' => [ 'type' => 'string' ],
],
'meta' => [
'show_in_rest' => true,
],
]
);
}
See that 'show_in_rest' => true? That one line automatically creates the REST API endpoint. No more register_rest_route boilerplate. The input_schema handles validation. And the JavaScript side becomes dead simple. Instead of a messy apiFetch call, the JS client for the Abilities API makes it clean.
Why This is a Foundational Shift
This isn’t just about saving a few lines of code. It’s about building a more interoperable WordPress ecosystem. When plugins can programmatically discover what other plugins can *do*, you open the door for complex workflows, automation, and—yes—AI agents that can intelligently interact with a site’s specific capabilities.
- It’s DRY (Don’t Repeat Yourself): You define the core logic once. That’s it.
- It’s Discoverable: Other tools can ask WordPress, “What can you do?” and get a standardized answer.
- It’s Secure: Permissions are baked in from the start with the
permission_callback.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
The new API is a huge step forward. It forces us to think about our plugin features not as isolated pieces of code, but as reusable, well-defined “abilities.” And that was it. The client got their feature, and I got a cleaner, more future-proof codebase. A rare win-win.
Leave a Reply