Developer Portal
Plugin Development

AJAX & Webhook Handlers

Register lightweight, secure HTTP entry points for dashboard AJAX requests and external webhooks without creating custom routes or controllers.

Why This Exists

Plugins frequently need to handle asynchronous requests — such as fetching dashboard widgets, saving background preferences, or receiving external callbacks from payment gateways and SMS services. Allowing every plugin to register arbitrary HTTP routes, controllers, or middleware introduces severe security risks and potential route collisions across third-party extensions. AIPostify solves this by routing all plugin AJAX and webhook traffic through a single, strictly controlled entry point: /aipostify/plugin-ajax/{plugin-slug}/{action}.

Registering a Handler

Handlers are registered inside your plugin's boot() method using Hook::listen() with the action hook pattern plugin_ajax_{action}:

Hook::listen(
    string $name,
    callable $callback,
    int $priority = 10,
    bool $requiresAuth = true,
    ?string $rateLimit = null
): void

Here is the authenticated dashboard AJAX handler from the postify-announcements reference plugin shown verbatim:

// 7. Scoped AJAX Action (requiresAuth: true — Admin Dashboard AJAX)
Hook::listen('plugin_ajax_get_recent_announcements', function (\Illuminate\Http\Request $request) {
    return [
        'status'     => 'success',
        'banner'     => Setting::get('postify-announcements', 'banner_message', 'Default banner'),
        'color'      => Setting::get('postify-announcements', 'badge_color', 'info'),
        'fetched_at' => now()->toIso8601String(),
    ];
}, priority: 10, requiresAuth: true);

Naming & Collision Safety

You do not need to worry about action name collisions with other plugins. When a plugin registers a plugin_ajax_{action} listener inside its boot() method, AIPostify automatically namespaces the hook key internally with your plugin's own slug (e.g. plugin_ajax_postify-announcements_get_recent_announcements). You can pick simple, readable action names like get_data or ping without conflicting with any other installed plugin.

Authenticated vs Public Actions

The requiresAuth parameter dictates how AIPostify secures incoming requests before calling your handler:

  • requiresAuth: true (Default): Designed for dashboard AJAX calls. Requires an active authenticated user session and enforces CSRF token verification on state-changing requests (POST, PUT, PATCH, DELETE). Unauthenticated calls receive an automatic HTTP 401 response.
  • requiresAuth: false: Designed for public webhooks and external service callbacks (such as payment gateway notifications or SMS delivery receipts). Bypasses user authentication and CSRF checks.

🔒 Security Requirement for Public Webhooks

Because requiresAuth: false actions accept requests from anyone on the internet, your callback MUST verify the request's authenticity itself — typically via a signature, HMAC, or shared secret supplied by the external service you're integrating with. AIPostify does not and cannot verify this for you.

Here is the mock webhook action from postify-announcements demonstrating this pattern. Note: This is a demonstration of the structural pattern, not a real secure webhook implementation to copy verbatim:

// 8. Scoped Webhook Action (requiresAuth: false — Public Webhook Challenge/Ingress)
Hook::listen('plugin_ajax_external_ping', function (\Illuminate\Http\Request $request) {
    // TODO: verify your own signature here in a real webhook (e.g., hash_equals($signature, hash_hmac(...)))

    return [
        'received'  => true,
        'action'    => 'external_ping',
        'timestamp' => time(),
        'payload'   => $request->all(),
    ];
}, priority: 10, requiresAuth: false);

Building the Action URL

Inside your main plugin class (which extends BasePlugin), use the $this->ajaxUrl($action) helper to generate the fully qualified endpoint URL for your JavaScript assets or external Webhook configuration:

// Inside your plugin class:
$endpoint = $this->ajaxUrl('get_recent_announcements');
// Outputs: https://example.com/aipostify/plugin-ajax/postify-announcements/get_recent_announcements

You can pass this URL to your JavaScript front-end via Blade templates or localized script data.

Rate Limiting

AIPostify enforces a default global floor rate limit of 120 requests per minute per IP on all plugin AJAX and webhook actions to protect your site against denial-of-service attempts.

If your endpoint is resource-intensive (e.g., triggering heavy processing or sending emails), you can pass a custom rateLimit string parameter formatted as 'N,M' (where N is maximum requests and M is decay time in minutes):

// Allow maximum 10 requests every 1 minute for this sensitive endpoint
Hook::listen('plugin_ajax_submit_form', function ($request) {
    // Handler logic...
}, priority: 10, requiresAuth: true, rateLimit: '10,1');

💡 Note: The global baseline rate limit floor (120 reqs/min) always applies as a maximum upper ceiling regardless of custom configuration.

What This Does NOT Support

To maintain strict core security boundaries and enable global auditability, the AJAX & Webhook Handler system intentionally does not support the following features:

  • Plugins cannot register custom URL paths or arbitrary routing slugs of their own choosing.
  • Plugins cannot attach custom Laravel middleware classes or route groups.
  • Plugins cannot bypass the standardized /aipostify/plugin-ajax/{slug}/{action} URL structure.

This is an intentional architectural boundary: providing a single, predictable dispatcher URL prevents rogue plugins from intercepting core URLs, tampering with system routes, or bypassing global security logging.

See It in Action

Explore the reference plugin postify-announcements inside the AIPostify core repository (at storage/plugins/postify-announcements/Plugin.php or via the Marketplace reference plugin download) to see working examples of both authenticated AJAX requests and unauthenticated webhook callbacks operating in a production environment.

Last verified against AIPostify core. © 2026 AIPostify Marketplace Developer Documentation.