Configure and implement widget webhooks

This guide walks you through configuring a widget webhook preset in Bloomreach and implementing the postMessage communication protocol in your iFrame widget.

Before you begin, read Widget webhooks to understand how the communication process works and what prerequisites apply.

Overview

Setting up a widget webhook involves two stages:

  1. Configure the webhook preset in Bloomreach: Set up the widget URL, icon, description, and tracked parameters.
  2. Implement the communication protocol: Build the JavaScript postMessage communication between the iFrame widget and Bloomreach to exchange configuration data.

Configure the webhook preset in Bloomreach

Access the webhook preset wizard

  1. Go to Data & Assets > Integrations.

  2. Click + Add new integration to open the wizard.

    Add new integration button highlighted on the Integrations page in Bloomreach.

    Click Add new integration on the Integrations page to open the webhook preset wizard.

  3. Use the Webhooks filter to narrow down the integrations list.

  4. Click + Add integration next to Webhook preset.

    Webhooks filter applied with Add integration button next to Webhook preset.

    Use the Webhooks filter to locate the Webhook preset, then click Add integration.

Configure the webhook settings

In the webhook preset wizard, fill in the following fields:

  • Icon (optional): Upload an icon to help users identify this webhook in scenario action nodes. Maximum dimensions: 72×72px. Supported formats: PNG, JPEG.
  • Description: Add a description that explains what this webhook does and when to use it.
  • Preset type: Set to Widget Webhook to enable widget-specific configuration fields.
  • Widget URL: Enter the URL where your widget interface is hosted. Bloomreach loads this URL into an iFrame when a user opens the webhook in a scenario.
  • Tracked parameters (optional): Specify values you want to track, such as event_time or order_id. These are added as extra event properties in campaign events.
📘

Note

Enable authentication if your third-party API requires it by selecting a supported method. Bloomreach securely stores your credentials and automatically includes them in webhook requests.

Webhook preset wizard showing Widget Webhook preset type and widget URL field.

Set the preset type to Widget Webhook to reveal the widget-specific configuration fields.

After saving the preset, connect it to your iFrame widget by implementing the communication protocol.

Implement the widget communication protocol

The iFrame widget and Bloomreach follow a specific protocol to exchange messages.

📘

Note

To verify the connection during setup, you can load the widget in a scenario while you implement. See Widget webhooks in scenarios for details.

Set up postMessage communication

The widget must listen for messages from Bloomreach and send messages back to the application origin.

Define the application origin in your widget code:

var appOrigin = "https://app.exponea.com"; // the origin where the app runs

Set up a message listener to receive messages from the application:

window.addEventListener('message', function(event) {
  if (event.origin !== appOrigin) return;
  
  // Handle different message types
  var message = event.data;
  switch(message.message_type) {
    case 'app_hello':
      // Handle initialization response
      break;
    case 'app_reject':
      // Handle unsupported version
      break;
    case 'app_request_state':
      // Handle state request
      break;
    case 'errors':
      // Handle validation errors
      break;
  }
});

Initialize the widget

The initialization process verifies that the widget and Bloomreach use the same API version and establishes communication between them.

Send the initialization message

When the iFrame loads, send an initialization message to Bloomreach:

var appOrigin = 'https://app.exponea.com'; // the origin where the app runs 
window.parent.postMessage(
    {
        min_supported_version: 1, // will be used in the future for compatibility, but still must be present
        max_supported_version: 1, // will be used in the future for compatibility, but still must be present
        message_type: 'widget_hello'
    },
    appOrigin
);
📘

Note

Always include both min_supported_version and max_supported_version in your initialization message. These version numbers enable future compatibility.

Handle the application response

Bloomreach responds with either app_hello or app_reject.

If the API version is supported, Bloomreach sends an app_hello message with the following payload:

{
  message_type: 'app_hello',
  version: 1,
  webhook: null, // null for new webhooks
  webhook: { // object for edited webhooks with previous configuration
    url: 'https://remote-server', // remote url to send the webhook to
    method: 'POST', // HTTP method to use ('GET' or 'POST')
    response_handling: 'discard', // how to make the response available to subsequent campaign nodes
      // 'discard' - ignore the response
      // 'text' - store response as plain text
      // 'json' - parse response as json and store the parsed version
    auth: null, // if no authentication was saved
    auth: { // if authentication was previously saved
      type: 'basic',
      username: 'myuser', // password won't be present here, there is no way to retrieve it after it's saved
    },
    headers: [ // list of HTTP headers to send in the request
      {
        name: 'Content-Type', // name of the HTTP header, case insensitive
        value: 'application/json', // public value of the header, this field isn't present for secret headers
        type: 'public',
      },
      {
        name: 'X-Secret', // name of the HTTP header, case insensitive
        type: 'secret',
      }
    ],
    body: 'jinja2 template', // jinja2 template that renders webhook body
    event_properties: { // extra event properties that are tracked in campaign events
      name1: 'jinja2 template', // the values of the properties are rendered using jinja2, the keys are not
      name2: 'jinja2 template',
    },
    frequency_policy: 'policy_name', // string that matches an existing frequency policy in exponea
    consent_category: 'category', // string that matches an existing category in exponea
    general_consent: true, // in case that consent_category is null or undefined
  },
  widget_state: null, // state of the widget as provided on last save, null for new webhooks
}

Use this payload to initialize the widget instance. For edited webhooks, the webhook object contains the previous configuration.

If the API version isn't supported, Bloomreach sends:

{
  message_type: 'app_reject',
  version: 1,
}
📘

Note

If you receive app_reject, display a message indicating the widget isn't supported and stop sending messages.

Send the initialization complete message

After initializing the widget instance, notify Bloomreach that initialization was successful:

window.parent.postMessage(
  {
    message_type: 'widget_initialized'
  },
  appOrigin
);
🚧

Important

Bloomreach uses this message to enable UI elements like the Save and Test buttons. Users can't save the webhook configuration until the widget sends this message.

Edit the widget parameters

After connecting successfully and receiving the app_hello response, you can load the widget interface and edit it in Bloomreach. See Widget webhooks in scenarios for details.

📘

Note

Use the widget webhook interface editor in Bloomreach every time you need to edit parameters or messages within the widget application.

Handle state requests

When a user interacts with the webhook configuration window, Bloomreach sends an app_request_state message to retrieve the widget's current settings. This happens during the following actions:

  • Test: The user clicks the test button. Bloomreach retrieves the widget's configuration, then the user clicks another button to send the test request.
  • Save: The user clicks Done. Bloomreach retrieves the widget's state and validates consents, frequency policies, and Jinja syntax. Bloomreach doesn't validate the correctness of JSON or XML payloads.
  • Cancel: The user clicks Cancel to close the webhook configuration. Bloomreach doesn't perform validation or saving.

Receive the state request

When the user clicks Save or Test, Bloomreach sends:

{
  message_type: 'app_request_state',
}

Send the widget state response

Respond with the widget's current configuration:

window.parent.postMessage(
{
    message_type: 'widget_state', // webhook data as specified in initialization, except that authentication password and secret header values must be specified (if applicable)
    webhook: {
      url: 'https://remote-server', // remote url to send webhook to
      method: 'POST', // HTTP method to use ('GET' or 'POST')
      response_handling: 'discard', // how to make the response available to subsequent campaign nodes
          // 'discard' - ignore the response
          // 'text' - store response as plain text
          // 'json' - parse response as json and store the parsed version
      auth: null, // if no authentication was saved
      auth: { // if authentication was saved
          type: 'basic',
          username: 'myuser',
          password: 'I am secret',
      },
      headers: [{ // list of HTTP headers to send in the request
          name: 'Content-Type', // name of the HTTP header, case insensitive
          value: 'application/json', // public value of the header, this field isn't present for secret headers
          type: 'public',
      }, {
          name: 'X-Secret', // name of the HTTP header, case insensitive
          type: 'secret',
          value: 'I am secret',
      }],
      body: 'jinja2 template', // jinja2 template that renders webhook body
      event_properties: { // extra event properties that are tracked in campaign events
          name1: 'jinja2 template', // the values of the properties are rendered using jinja2, the keys aren't
          name2: 'jinja2 template',
      },
      frequency_policy: 'policy_name', // string that matches an existing frequency policy in exponea
      consent_category: 'category', // string that matches an existing category in exponea
      general_consent: true // in case that consent_category is null or undefined
    },
    widget_state: null, 
}
);
📘

Note

The widget_state field stores any JSON-serializable JavaScript object that preserves the widget's state for future initialization. Don't include secrets in this data.

Important notes on state responses

  • If the webhook uses the old authentication approach — saving credentials directly at the node level — provide authentication passwords and secret header values in the state response. These values are omitted from the initialization payload for security. Use authentication integrations instead, as they let users update credentials across all scenarios at once.
  • The widget_state field stores any JSON-serializable object up to 4KB. Use this to preserve widget-specific state that doesn't belong in the webhook configuration, such as UI state like filters for dropdowns or flags to collapse panels.

Manage validation errors

If Bloomreach encounters validation errors while processing the widget's state, it displays an error notification to the user and sends error details to the widget.

Bloomreach sends:

{
    message_type: 'errors',
    errors: {
        webhook: {
            // a dictionary of the same structure as the webhook object
            // but containing a list of errors instead of values in the leafs
            // for example it could contain the below data:
            url: ['Use either http or https scheme'],
            // lists use object keys with the index
            headers: {
                0: {type: ['invalid type']} // error for the first header
            }
        }
    }
}

The errors object mirrors the webhook structure, with lists of error messages for each invalid field. Object keys use indices for array elements.

You can use these errors to display detailed validation messages in the widget interface. You can also ignore this message entirely. Users already see the error notification in Bloomreach.


© Bloomreach, Inc. All rights reserved.