How to send WordPress security and admin events to an external webhook (Rapid7 integration)

Integrating your WordPress site with external monitoring tools like Rapid7 can significantly improve visibility into security events such as login attempts, plugin changes, and user activity.

However, implementing a reliable webhook system requires more than just sending HTTP requests—it involves structured payloads, retry logic, and asynchronous processing.

In this case, the solution involved building a custom WordPress plugin to capture key events and deliver them to a Rapid7 webhook endpoint using a queued, fault-tolerant system.

Issue Background

The goal was to send WordPress activity events to an external endpoint using:

  • HTTPS POST requests
  • JSON-formatted payloads
  • A webhook ingestion URL (Rapid7)

The integration needed to support events such as:

  • login_success
  • login_failed
  • user_created
  • privilege_changed
  • plugin_installed / plugin_updated
  • theme_changed
  • file_modified

Additional requirements included reliable delivery with retry handling and non-blocking performance.

Diagnosis

Direct webhook calls are not reliable

Sending events directly during WordPress actions can slow down the site and fail silently if the endpoint is unavailable.

Webhook responses vary

Endpoints may return 2xx, 4xx, 5xx, or rate-limited responses, requiring proper retry handling.

WP-Cron limitations

WP-Cron only runs when the site receives traffic, which can delay event processing.

Resolution Steps

Step 1: Build a custom plugin

A custom plugin (e.g., Freshy Rapid7 Webhook Sender) was created to manage event capture and delivery.

Step 2: Capture WordPress events

Hook into authentication, user, and system events such as login attempts, role changes, and plugin updates.

Step 3: Structure JSON payloads

{
  "timestamp": "2026-01-27T19:42:11Z",
  "event_type": "login_success",
  "username": "adminuser",
  "source_ip": "203.0.113.24",
  "site": "https://example.com",
  "action": "authenticate",
  "outcome": "success",
  "object": "wp_login",
  "severity": "low"
}

Step 4: Queue events in a database

Store events in a custom table (e.g., wp_freshy_rapid7_queue) to ensure reliable delivery.

Step 5: Send asynchronously with WP-Cron

Process the queue using WP-Cron to avoid blocking frontend or admin actions.

Step 6: Implement retry logic

Retry failed requests using exponential backoff and move persistent failures to a dead-letter queue.

Step 7: Configure webhook endpoint

Use separate endpoints for staging and production or include an environment field in payloads.

Step 8: Set headers

Ensure requests include:

Content-Type: application/json

Step 9: Test delivery

Send test events and validate responses using logs or tools like Postman.

Step 10: Add logging

Implement logging for successful and failed deliveries with visibility in the admin panel.

Final Outcome

After implementing the system:

  • WordPress events are reliably captured and sent
  • Delivery is asynchronous and performant
  • Retry logic prevents data loss
  • Logging provides visibility into system behavior

If you need help integrating WordPress with external APIs or webhook systems, contact Freshy for expert support.