> For the complete documentation index, see [llms.txt](https://docs.acpt.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.acpt.io/integrations/abilities-api/extending.md).

# Extending

Filters for overriding capabilities, adding or removing providers, and building your own abilities on ACPT's infrastructure

Two WordPress filters let you customize the Abilities API integration from your own plugin or theme's `functions.php`, without touching ACPT's code.

## Overriding the required capability

By default, every content-model ability requires `manage_options` and every content-data (field value) ability requires `edit_posts` — see [How it works → Permissions](/integrations/abilities-api/how-it-works.md#permissions). Override this per ability, per user, or based on the input being sent with the `acpt/abilities-api/capability` filter:

```php
/**
 * @param string $capability   The default capability for this ability.
 * @param string $abilityName  The full ability name, e.g. "acpt/save-post-type".
 * @param mixed  $input        The input passed to the ability (or null).
 */
add_filter('acpt/abilities-api/capability', function ($capability, $abilityName, $input) {
    // Let editors manage forms, but keep everything else at manage_options.
    if (strpos($abilityName, 'acpt/') === 0 && strpos($abilityName, '-form') !== false) {
        return 'edit_others_posts';
    }

    return $capability;
}, 10, 3);
```

The returned value is passed straight to `current_user_can()`, so it can be any valid WordPress capability string.

## Adding or removing providers

Every ability is registered by a small "provider" class (`CustomPostTypeAbilities`, `TaxonomyAbilities`, …), one per content domain. The full list ACPT registers by default is filterable with `acpt/abilities-api/providers`:

```php
/**
 * @param array<class-string> $providers Class names, each extending
 *                                        ACPT\Integrations\AbilitiesAPI\Providers\AbstractAbilityProvider.
 */
add_filter('acpt/abilities-api/providers', function (array $providers) {
    // Don't expose dynamic block abilities to AI agents on this site.
    return array_filter($providers, function ($providerClass) {
        return $providerClass !== \ACPT\Integrations\AbilitiesAPI\Providers\DynamicBlockAbilities::class;
    });
});
```

Or add your own provider to register additional, site-specific abilities alongside ACPT's:

```php
add_filter('acpt/abilities-api/providers', function (array $providers) {
    $providers[] = My_Plugin\MyCustomAbilities::class;

    return $providers;
});
```

Any class that isn't a subclass of `AbstractAbilityProvider` is silently skipped, so a bad value here can't fatal the site.

## Building your own ability provider

You don't have to reinvent `wp_register_ability()` boilerplate — ACPT's own `AbilityRegistrar` and `AbilityOutputSchema` classes are safe to reuse from your own provider:

```php
namespace My_Plugin;

use ACPT\Integrations\AbilitiesAPI\Providers\AbstractAbilityProvider;
use ACPT\Integrations\AbilitiesAPI\Support\AbilityOutputSchema;

class MyCustomAbilities extends AbstractAbilityProvider
{
    protected function config(): array
    {
        return [
            'namespace'  => 'my-plugin',
            'category'   => 'acpt-content-model', // or your own, registered separately
            'capability' => 'manage_options',
            'operations' => [
                'get' => [
                    'name'          => 'get-something',
                    'label'         => 'Get something',
                    'description'   => 'Returns something specific to my plugin.',
                    'callback'      => function (array $input) {
                        return ['id' => $input['id'] ?? null, 'value' => 'hello'];
                    },
                    'input_schema'  => [
                        'type'       => 'object',
                        'properties' => ['id' => ['type' => 'string']],
                        'required'   => ['id'],
                    ],
                    'output_schema' => AbilityOutputSchema::single([
                        'type'       => 'object',
                        'properties' => [
                            'id'    => ['type' => 'string'],
                            'value' => ['type' => 'string'],
                        ],
                    ]),
                ],
            ],
        ];
    }
}
```

Then register it with the `acpt/abilities-api/providers` filter shown above. `AbilityRegistrar` takes care of building the `execute_callback`/`permission_callback` wrappers, the `{success, message, data}` response envelope, and the `readonly`/`destructive`/`idempotent` metadata the same way it does for every built-in ACPT ability — see [How it works](/integrations/abilities-api/how-it-works.md) for exactly what that means.

{% hint style="warning" %}
Registering a category (e.g. a custom one instead of reusing `acpt-content-model`) is a separate step — call `wp_register_ability_category()` yourself on WordPress's `wp_abilities_api_categories_init` hook, the same hook ACPT uses for its own two categories.
{% endhint %}
