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.