Agent and ability hooks

These hooks sit around the AI-agent surface described in AI agents and the Abilities API. All of them are inert while the operator switch is off, because nothing is registered at all in that state.

One rule applies to the whole page: the operator gets a single on/off switch by design. There is no per-ability toggle screen, and happenboard_registered_abilities is the deliberate escape hatch for a developer who genuinely needs finer control.

happenboard_register_abilities

Action Free Since 1.0 src/Service/Abilities/Bootstrap.php

Fires after HappenBoard registers its own abilities. Register your own here rather than on wp_abilities_api_init directly, so yours inherit the same gating: when the operator switch is off this never fires and nothing downstream registers either.

Signature

do_action( 'happenboard_register_abilities' )

Return value

void

Example

add_action(
    'happenboard_register_abilities',
    function () {
        wp_register_ability(
            'acme/list-sponsors',
            array(
                'label'               => 'List sponsors',
                'description'         => 'Returns the sponsors attached to an event.',
                'category'            => 'happenboard-events',
                'permission_callback' => fn () => current_user_can( 'happenboard_manage_events' ),
                'execute_callback'    => fn ( $input = array() ) => acme_sponsors_for( (int) ( $input['event_id'] ?? 0 ) ),
            )
        );
    }
);

Notes

  • Runs on wp_abilities_api_init, so wp_register_ability() is available.
  • HappenBoard Pro fires its own happenboard_pro_register_abilities after registering its abilities, for add-ons that want to sit behind Pro instead.

happenboard_registered_abilities

Filter Free Since 1.0 src/Service/Abilities/Registry.php

Filters each ability HappenBoard is about to register, by full name. Remove a name from the array and that ability is never registered, so an agent does not see it at all rather than being refused when it calls.

Signature

apply_filters( 'happenboard_registered_abilities', array $names )

Parameters

$names array<int,string>
Candidate ability names, always a single-element array holding the one being registered, e.g. happenboard/delete-event.

Return value

array<int,string> The names to keep. Return an empty array to drop the ability.

Example

add_filter(
    'happenboard_registered_abilities',
    fn ( $names ) => array_diff(
        $names,
        array( 'happenboard/delete-event', 'happenboard/delete-booking-type' )
    )
);

Notes

  • The filter runs once per ability, not once with the whole set, so compare against the names you want to remove rather than expecting a full list.
  • Removing an ability here is not a permission check. Permissions are already enforced per ability against your HappenBoard capabilities; this is for narrowing the surface itself.

happenboard_ability_executed

Action Free Since 1.0 src/Service/Abilities/Registry.php

Fires after a write ability runs, whatever the outcome. This is the seam the built-in agent audit trail listens on, and the place to hang your own alerting or SIEM forwarding.

Signature

do_action( 'happenboard_ability_executed', string $name, array $input, mixed $result, string $status )

Parameters

$name string
Full ability name, e.g. happenboard/create-event.
$input array<string,mixed>
The validated input. May contain credentials. A listener that persists it must redact first, as the built-in audit log does.
$result mixed
Whatever the ability returned, or the WP_Error it was refused with.
$status string
One of ok, error, failed, rate_limited, unconfirmed.

Return value

void

Example

add_action(
    'happenboard_ability_executed',
    function ( $name, $input, $result, $status ) {
        if ( 'rate_limited' !== $status ) {
            return;
        }

        error_log( sprintf( 'Agent hit the budget on %s (user %d)', $name, get_current_user_id() ) );
    },
    10,
    4
);

Notes

  • Four arguments: register with add_action( ..., 10, 4 ) or the later ones never arrive.
  • Reads are not announced here. They are budgeted at 300 a minute per actor, and logging them would put the audit trail on the hot path.
  • Every listener is wrapped so a throw cannot turn a completed write into a 500 for the caller. Do not rely on throwing here to block anything: the write has already happened.

happenboard_agent_log_retention_days

Filter Free Since 1.0 src/Service/Abilities/AuditLog.php

Filters how long agent audit rows are kept before the cleanup pass removes them.

Signature

apply_filters( 'happenboard_agent_log_retention_days', int $days )

Parameters

$days int
Retention in days. Defaults to 90.

Return value

int Days to keep. Return 0 or less to disable the cleanup and keep rows forever.

Example

// Keep a full year for a compliance requirement.
add_filter( 'happenboard_agent_log_retention_days', fn () => 365 );

Notes

  • The value is also reported back to the settings screen, so what the log panel says matches what actually happens.

happenboard_abuse_guard_limits

Filter Free Since 1.0 src/Service/Security/AbuseGuard.php

Filters the per-actor rate budgets applied to every ability call. Raising these is a deliberate act: they exist so a looping agent, or a leaked application password, cannot exhaust the site.

Signature

apply_filters( 'happenboard_abuse_guard_limits', array $limits )

Parameters

$limits array<string,int>
Keys read_per_minute (300), write_per_minute (60) and write_per_hour (600).

Return value

array<string,int> The budgets to apply. Missing keys fall back to the defaults, and any value is floored at 1.

Example

add_filter(
    'happenboard_abuse_guard_limits',
    function ( $limits ) {
        // A nightly sync job needs more write headroom than a chat agent.
        $limits['write_per_hour'] = 2000;

        return $limits;
    }
);

Notes

  • Budgets are counted per acting user, not per site, so one busy integration does not throttle everyone else.
  • This does not touch the public RSVP and booking forms. Those have their own limits, documented in the sign-up hooks.

happenboard_abuse_guard_denial_threshold

Action Free Since 1.0 src/Service/Security/AbuseGuard.php

Fires the first time an actor crosses the denial threshold inside one window: ten refused calls in five minutes. A credential probing the surface produces refusals rather than completions, so this is the signal worth alerting on.

Signature

do_action( 'happenboard_abuse_guard_denial_threshold', int $actor_id, int $count )

Parameters

$actor_id int
Acting user ID, or 0 when logged out.
$count int
Denials recorded for that actor in the current window.

Return value

void

Example

add_action(
    'happenboard_abuse_guard_denial_threshold',
    function ( $actor_id, $count ) {
        wp_mail(
            get_option( 'admin_email' ),
            'HappenBoard: repeated agent refusals',
            sprintf( 'User %d was refused %d times in five minutes.', $actor_id, $count )
        );
    },
    10,
    2
);

Notes

  • Two arguments: register with add_action( ..., 10, 2 ).
  • It fires once per window when the threshold is crossed, not on every subsequent denial, so an alert here does not become its own flood.