Docs / Hooks and filters

Hooks and filters

The assistant is built to be extended without forking it. A handful of WordPress filters let an add-on register new tools, supply a wallet or image-search provider, shape the system prompt, route outbound WhatsApp, and tune the per-client rate limit. Each seam is decoupled on purpose: with nothing hooked, the capability simply stays dormant.

Register tools with a name, schema and callback

Tools are how the model reads and acts on real store data. The plugin ships its built-in WooCommerce tools, then exposes the full list to add-ons through the fahad_ai_register_tools filter. Append an entry with a unique name, a description shown to the model, a JSON Schema parameters block, and a callback with the signature fn( array $input ): array.

add_filter( 'fahad_ai_register_tools', function ( array $tools ) {
    $tools[] = [
        'name'        => 'loyalty_points',
        'description' => 'Get the logged-in customer loyalty points balance.',
        'parameters'  => [ 'type' => 'object', 'properties' => new stdClass() ],
        'callback'    => fn( array $input ) => [ 'points' => my_points_lookup() ],
    ];
    return $tools;
} );

Invalid entries are skipped, and a callback that throws is isolated, so a misbehaving add-on cannot fatal a request. The callback return value is sent back to the model as the tool result, so keep it to plain, grounded data.

First-party feature packs that ship inside the plugin use a separate, filter-free path, Fahad_AI_Tool_Registry::register_pack(), where a pack drops into includes/tools/ as one new file with no bootstrap edits. The fahad_ai_register_tools filter is the seam for your own add-ons, and it runs after the packs so you can still override a name.

Supply a wallet provider

Store credit is the differentiator, and it is also money-sensitive, so the assistant core never depends on any wallet plugin. The wallet tools resolve a provider adapter at runtime through fahad_ai_wallet_provider. Return an object (or an array of callables keyed by the same operation names).

add_filter( 'fahad_ai_wallet_provider', fn() => new My_WalletPro_Adapter() );

The adapter exposes these operations. All amounts are floats in the store currency, and any operation you cannot service should return an [ 'error' => string ] array rather than throw or report a success it did not perform.

The ledger and its atomicity belong to your provider. The tool layer only guarantees it never creates an unsafe condition: it validates amounts before touching you, checks sufficient balance before a debit is even attempted, never fabricates a balance, and acts only on the current logged-in user. See the wallet integration page for the full contract.

Filter the system prompt

The fahad_ai_system_prompt filter receives the base prompt (the default body or the merchant custom prompt) so you can inject context, for example a returning customer's stored preferences. Return a string.

add_filter( 'fahad_ai_system_prompt', function ( $prompt ) {
    return $prompt . "\n\nThis customer prefers vegan products.";
} );
The trust guardrails are appended after this filter returns, not passed through it. Neither this hook, a merchant custom prompt, nor any config field can drop or override the anti dark-pattern policy. You can add to the prompt, you cannot remove the guardrails.

Tune the rate limit

Each client is held to a request count inside a time window. Both are overridable: fahad_ai_rate_limit sets the number of requests (default 20), and fahad_ai_rate_window sets the window in seconds (default one minute).

add_filter( 'fahad_ai_rate_limit',  fn() => 40 );                   // 40 requests
add_filter( 'fahad_ai_rate_window', fn() => 2 * MINUTE_IN_SECONDS ); // per 2 minutes

Raising these costs more provider tokens per visitor, so move them deliberately. Lowering them tightens abuse protection at the cost of stricter limits for genuine shoppers.

Plug in an image-search provider

Visual search stays dormant until a provider is hooked. The fahad_ai_visual_retriever filter receives a validated image descriptor and structured filters, and returns ranked product IDs, best first. Two shapes are accepted: return the IDs directly, or return a callable retriever to register once.

add_filter( 'fahad_ai_visual_retriever', function ( $ids, $image, $filters ) {
    return My_Vision_Backend::rank( $image, $filters ); // int[] best first
}, 10, 3 );

The plugin validates every upload before the seam is consulted, so the provider only ever sees a size-bounded payload with an allowed image type. The retriever returns IDs only, never price or stock, and $filters carries category, min_price, max_price and limit so you can pre-filter the scan.

Send outbound WhatsApp

The core makes no live Meta call. The fahad_ai_whatsapp_send filter is the outbound seam: with no provider hooked it returns null and nothing is sent. A provider posts to the Graph API and returns a result array.

add_filter( 'fahad_ai_whatsapp_send', function ( $result, $to, $text, $ctx ) {
    // POST to the WhatsApp Cloud API with your phone number id + token.
    // return [ 'sent' => true, 'id' => $wamid ] on success.
    return $result;
}, 10, 4 );

Keeping the HTTP call and the access token in the provider means this scaffolding ships and is fully testable without any Meta credentials, and it mirrors the same decoupling as the wallet seam.

A note on the pattern

Every seam here follows one rule: the assistant ships the behaviour, your add-on ships the provider, and either side can change independently. Nothing fires until you hook it, so installing the plugin is always a calm, predictable starting point. If you build something, please return data the model can trust, plain and grounded, and let the guardrails do the rest.