Abilities API
Abilities API
Section titled “Abilities API”WordPress 7.0 ships the Abilities API - a registry that lets a plugin declare
discrete, machine-callable actions (wp_register_ability()), each with a JSON
schema, a permission callback, and annotations describing whether it reads or
writes. Registered abilities are exposed over /wp-abilities/v1 and can be
surfaced to MCP clients and AI agents.
Current State: Eventonomy Registers None
Section titled “Current State: Eventonomy Registers None”As of 1.5.0, Eventonomy and Eventonomy Pro register zero abilities. There is no
wp_register_ability() call in either repository. Verified on a WordPress 7.0
install: wp_get_abilities() returns 10, of which 3 are core and the rest belong to
WooCommerce - none are ours.
This page documents that honestly rather than describing a surface that does not
exist. If you are looking for a machine-callable interface to Eventonomy today,
use the REST API (reference) - it is complete, versioned, and
covers 70 routes / 111 operations with both plugins active (the count the generated
docs/api/openapi.json reports).
Why this matters. Eventonomy is REST-first with no AJAX and a documented envelope, so it is unusually well-placed to expose abilities - the hard part (a clean, permission-checked action surface) is already built. WooCommerce has already adopted the API. Treat this as a known gap, not a design decision.
Registering Abilities Against Eventonomy
Section titled “Registering Abilities Against Eventonomy”Nothing stops you registering your own today. The plugin’s service layer and capability helpers are the right things to call.
add_action( 'wp_abilities_api_init', function () { if ( ! function_exists( 'wp_register_ability' ) ) { return; // WordPress < 7.0 }
wp_register_ability( 'my-addon/find-events', array( 'label' => __( 'Find events', 'my-addon' ), 'description' => __( 'Search upcoming events by text, city or date range.', 'my-addon' ), 'category' => 'my-addon',
'input_schema' => array( 'type' => 'object', 'properties' => array( 'search' => array( 'type' => 'string' ), 'city' => array( 'type' => 'string' ), 'from' => array( 'type' => 'string', 'format' => 'date-time' ), ), ), 'output_schema' => array( 'type' => 'object', 'properties' => array( 'items' => array( 'type' => 'array' ), 'total' => array( 'type' => 'integer' ), ), ),
// Go through the contract, never raw SQL. 'execute_callback' => function ( array $input ) { $events = evnm( \Eventonomy\Contracts\EventRepositoryInterface::class ); return $events->query( array( 'search' => $input['search'] ?? '', 'city' => $input['city'] ?? '', 'status' => 'published', 'per_page' => 20, ) ); },
// Reuse Eventonomy's resolution, do not re-implement it. 'permission_callback' => function (): bool { return true; // published events are public },
'meta' => array( 'annotations' => array( 'readonly' => true, 'destructive'=> false, 'idempotent' => true, ), 'show_in_rest' => true, 'mcp' => array( 'public' => true, 'type' => 'tool' ), ), ) );} );Register a category once, guarding for older WordPress:
if ( function_exists( 'wp_register_ability_category' ) && ! wp_has_ability_category( 'my-addon' ) ) { wp_register_ability_category( 'my-addon', array( 'label' => __( 'My Add-on', 'my-addon' ), 'description' => __( 'Event operations provided by My Add-on.', 'my-addon' ), ) );}The Rules That Matter Most
Section titled “The Rules That Matter Most”1. The permission callback is the whole security boundary. An ability is
callable by an agent. Do not write a bare current_user_can( 'read' ) for anything
event-scoped - use the resolved helpers, which handle ownership:
'permission_callback' => function ( array $input ): bool { $events = evnm( \Eventonomy\Contracts\EventRepositoryInterface::class ); $event = $events->get( (int) ( $input['event_id'] ?? 0 ) ); return is_array( $event ) && evnm_user_can_manage_event( get_current_user_id(), $event, 'manage_rsvps' );},See Capabilities. The ownership branch is why a member organizer can manage their own event without site-manager rights - re-implementing the check will get that wrong.
2. Annotate honestly. readonly, destructive and idempotent are how a
client decides whether it may call something unattended, retry it, or must ask a
human first. Marking a refund idempotent: true because “the service is
idempotent” invites an agent to retry a money operation. If in doubt, mark it
destructive: true and let the client ask.
3. Never expose a secret through an ability. The same fields withheld from REST
are withheld here: magic_token, checkin_token, gateway_txn_id, and the order
meta blob. An ability is not a privileged back door.
4. Money and attendance are not read operations. Anything that creates an RSVP,
mints a ticket, refunds an order or records a payout must go through the service
layer (RsvpService, OrderService, Pro’s RefundService / PayoutService) so
the hooks, ledgers and reversals fire. Writing to a repository directly from an
ability skips all of that.
If Eventonomy Adopts This
Section titled “If Eventonomy Adopts This”The natural first set, in the order the existing REST surface makes them cheap - recorded here so the shape is agreed before anyone builds it:
| Ability | Annotations | Backed by |
|---|---|---|
eventonomy/find-events |
readonly, idempotent | EventRepositoryInterface::query() |
eventonomy/get-event |
readonly, idempotent | EventRepositoryInterface::get() |
eventonomy/create-event |
write, not idempotent | EventService::create() |
eventonomy/list-attendees |
readonly, idempotent | RsvpRepositoryInterface::query() |
eventonomy/check-in-attendee |
write, idempotent | Pro CheckinService (already idempotent + void-gated) |
eventonomy/refund-order |
write, destructive | Pro RefundService - full order only |
Reads should come first: they are safe, immediately useful to an assistant answering “what is on next week”, and they exercise the registration plumbing before anything can move money.
What’s Next?
Section titled “What’s Next?”- REST API - the complete machine interface that exists today
- Capabilities - the permission model an ability must reuse
- Extending - the service and repository contracts to call

