How to Register and Expose Custom Plugin Abilities Using the WordPress Abilities API with REST and Permission Callbacks
How to Register and Expose Custom Plugin Abilities Using the WordPress Abilities API with REST and Permission Callbacks is a skill every serious WordPress developer needs. When you build custom plugins, you often need to control what users can do. You also need to expose those controls safely through the REST API. WordPress gives you the tools to do this cleanly. This tutorial walks you through the full process. You’ll define custom capabilities, register REST API routes, and lock them down with permission callbacks. By the end, you’ll have a working plugin that controls access at both the PHP and REST API level. This approach keeps your plugin secure. It also makes it easy to integrate with JavaScript frontends or external services.
Prerequisites and Requirements for Registering Custom Plugin Abilities
Before you start, make sure you have the following in place.
Required software and access:
- A WordPress installation (version 5.6 or higher)
- Admin access to your WordPress dashboard
- SSH or FTP access to your server
- A code editor (VS Code, Sublime Text, or similar)
- Basic knowledge of PHP and WordPress plugin development
- Familiarity with the WordPress REST API
Assumed knowledge level: Intermediate. You should know how WordPress hooks work. You should also understand how to create a basic plugin file.
Estimated time to complete: 30–45 minutes.
You don’t need a local development environment, but it’s strongly recommended. Tools like LocalWP or DevKinsta make testing safe and fast. Never test capability changes directly on a live production site. A mistake with user roles can lock you out of the admin dashboard.
Step-by-Step Guide to Exposing Custom Plugin Abilities via REST
Related tutorial: How to Use Ansible Dynamic Inventory with the AWS Ec2 Plugin to Automatically Target Cloud Instances by Tag on Ubuntu Server
Follow these steps carefully. Each one builds on the last.
Step 1: Create your plugin file.
Navigate to your WordPress plugins directory. Create a new folder and file.
/wp-content/plugins/my-abilities-plugin/my-abilities-plugin.php
Add the plugin header at the top of the file.
<?php
/
Plugin Name: My Abilities Plugin
Description: Registers custom capabilities and exposes them via REST API.
Version: 1.0.0
Author: Your Name
/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
This header tells WordPress to recognize the file as a plugin. The ABSPATH check prevents direct file access.
Step 2: Register a custom capability.
Custom capabilities let you control access beyond default WordPress roles. Add this function to your plugin file.
function mab_add_custom_capabilities() {
$role = get_role( 'administrator' );
if ( $role ) {
$role->add_cap( 'manage_my_plugin_data' );
}
}
add_action( 'admin_init', 'mab_add_custom_capabilities' );
This adds a manage_my_plugin_data capability to the administrator role. You can also assign it to other roles as needed. Run this on admin_init so it fires reliably after WordPress loads roles.
Step 3: Register a custom REST API route.
Now you’ll create a REST endpoint that only users with your custom capability can access. See the WordPress REST API documentation for full reference.
function mab_register_rest_routes() {
register_rest_route(
'my-abilities-plugin/v1',
'/data',
array(
'methods' => 'GET',
'callback' => 'mab_get_plugin_data',
'permission_callback' => 'mab_check_permission',
)
);
}
add_action( 'rest_api_init', 'mab_register_rest_routes' );
The permission_callback is critical. It runs before the main callback. If it returns false, WordPress blocks the request automatically.
Step 4: Write the permission callback.
This function checks whether the current user has the right capability.
function mab_check_permission( WP_REST_Request $request ) {
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_forbidden',
__( 'You must be logged in.', 'my-abilities-plugin' ),
array( 'status' => 401 )
);
}
if ( ! current_user_can( 'manage_my_plugin_data' ) ) {
return new WP_Error(
'rest_forbidden',
__( 'You do not have permission.', 'my-abilities-plugin' ),
array( 'status' => 403 )
);
}
return true;
}
Always return a WP_Error object with the correct HTTP status code. Return true only when access is granted. Never use __return_true on public-facing routes.
Step 5: Write the main route callback.
This function handles the actual response when a permitted user hits your endpoint.
function mab_get_plugin_data( WP_REST_Request $request ) {
$data = array(
'status' => 'success',
'message' => 'You have access to plugin data.',
'user' => wp_get_current_user()->user_login,
);
return rest_ensure_response( $data );
}
Always use rest_ensure_response(). It wraps your data in a proper WP_REST_Response object.
Step 6: Activate the plugin and test the endpoint.
Go to your WordPress dashboard. Navigate to Plugins and activate My Abilities Plugin. Then test your endpoint using curl from the command line.
curl -X GET https://yourdomain.com/wp-json/my-abilities-plugin/v1/data
--user admin:your_application_password
Use an Application Password instead of your real password. You can generate one under Users → Profile in the WordPress admin. A successful response returns your JSON data. An unauthorized request returns a 401 or 403 error.
Step 7: Clean up capabilities on plugin deactivation.
Always remove custom capabilities when your plugin is deactivated. This keeps the database clean.
function mab_remove_custom_capabilities() {
$role = get_role( 'administrator' );
if ( $role ) {
$role->remove_cap( 'manage_my_plugin_data' );
}
}
register_deactivation_hook( __FILE__, 'mab_remove_custom_capabilities' );
Troubleshooting Common Issues with REST and Permission Callbacks
Even with clean code, things can go wrong. Here are the most common problems.
Problem: REST route returns 404.
Your permalink structure may be causing this. Go to Settings → Permalinks and click Save. This flushes the rewrite rules without changing anything.
Problem: Permission callback always returns 403.
Check that you activated the plugin before testing. The capability only gets added on admin_init. If you’re testing with a non-admin user, make sure you assigned the capability to their role explicitly.
Problem: Capability doesn’t persist after plugin reactivation.
WordPress stores capabilities in the database. If you deactivated the plugin first, the capability was removed. Reactivate the plugin and visit any admin page to trigger admin_init again.
Problem: curl returns SSL error.
Add the -k flag to skip SSL verification in local environments only. Never skip SSL on production servers.
Warning: Don’t set permission_callback to '__return_true' on any route that returns sensitive data. This makes the endpoint publicly accessible to anyone.
Conclusion
You now know how to register and expose custom plugin abilities using the WordPress Abilities API with REST and permission callbacks. You created a plugin that adds a custom capability, registers a secure REST route, and enforces access control at every level. This pattern scales well. You can add more routes, more capabilities, and more roles as your plugin grows. Your next steps could include adding POST and DELETE methods to your REST routes. You might also explore nonce verification for cookie-based authentication. Keep your permission callbacks strict. That’s what keeps your plugin safe as it grows in complexity.
