Send Every Test Event to a Webhook (log_experiment_activity)

Teams Included on the Teams plan. Not available on the Lite version. Upgrade →

The built-in Webhooks send one POST when a test finishes. If you want every visit, conversion or goal sent to another system as it happens (Zapier, Make, n8n, your CRM, a data warehouse), use the log_experiment_activity action.

The hook

AB Split Test runs log_experiment_activity every time it accepts a tracking event, right after the event is queued. It passes four arguments:

  • $eid: the test ID
  • $variation: the variation key the visitor saw
  • $type: visit, conversion, or a goal number (1, 2, 3…)
  • $location: where the event came from, when known

Events the plugin rejects (for example, one with no variation) never reach the hook.

Example: send each event to a webhook

Add this to your child theme’s functions.php or a code snippets plugin, then change the endpoint URL.

add_action( 'log_experiment_activity', 'my_abst_event_webhook', 10, 4 );

function my_abst_event_webhook( $eid, $variation, $type, $location ) {

    // Extra visitor detail when the event came from the browser beacon.
    $size = $audience = $visitor = $order_value = '';
    if ( isset( $_GET['method'] ) && 'beacon' === $_GET['method'] ) {
        $beacon      = json_decode( (string) file_get_contents( 'php://input' ), true );
        $size        = isset( $beacon['size'] ) ? sanitize_text_field( $beacon['size'] ) : '';   // mobile | tablet | desktop
        $audience    = isset( $beacon['aud'] ) ? sanitize_text_field( $beacon['aud'] ) : '';     // audience ids, comma separated
        $visitor     = isset( $beacon['uuid'] ) ? sanitize_text_field( $beacon['uuid'] ) : '';   // visitor id
        $order_value = isset( $beacon['orderValue'] ) ? floatval( $beacon['orderValue'] ) : 0;   // order value on conversions
    }

    // ----- Logic gates: uncomment the ONE you want -----
    // Only visits:
    // if ( 'visit' !== $type ) { return; }
    // Only conversions:
    // if ( 'conversion' !== $type ) { return; }
    // Only goal 1 (goals arrive as a number - 1, 2, 3...):
    // if ( '1' !== (string) $type ) { return; }
    // ---------------------------------------------------

    wp_remote_post( 'https://your-endpoint.example.com/hook', array( // set yours
        'timeout'  => 2,
        'blocking' => false, // fire and forget, doesn't slow the page down
        'body'     => wp_json_encode( array(
            'event'      => $type,       // visit, conversion, or goal number
            'testId'     => (int) $eid,
            'variation'  => $variation,
            'url'        => home_url( $_SERVER['REQUEST_URI'] ),
            'screenSize' => $size,       // mobile | tablet | desktop
            'audiences'  => $audience,   // audience ids, comma separated
            'visitorId'  => $visitor,
            'orderValue' => $order_value,
        ) ),
    ) );
}

Choosing which events to send

By default the snippet sends everything. To send only one kind of event, uncomment one of the lines in the “Logic gates” block. Goals arrive as a number, so goal 1 is '1'.

What gets sent

{
  "event": "conversion",
  "testId": 4182,
  "variation": "variation2",
  "url": "https://example.com/wp-admin/admin-ajax.php?action=abst_event&method=beacon",
  "screenSize": "mobile",
  "audiences": "3,7",
  "visitorId": "a1b2c3d4",
  "orderValue": 49
}

A few things to know about the fields:

  • screenSize, audiences, visitorId and orderValue are only filled in when the event came from the browser beacon, which is how most visits and conversions are sent. Events from other sources (server-side order tracking, the REST API) still fire the hook, but these four fields are blank.
  • url is the tracking request, not the page the visitor was on. The hook runs during the tracking call, so if you need the page itself, send it from your own tracking instead.

It won’t slow your site down

'blocking' => false sends the request without waiting for a reply, and 'timeout' => 2 caps it anyway. The trade-off is that failed deliveries aren’t reported, so check your endpoint receives events before you rely on it.

This fires once per event, so a busy test sends a lot of requests. If your endpoint charges per task (Zapier, Make), use a logic gate to send only conversions.

Leave a Comment

You must be logged in to post a comment.