JS API Integration

FlowHunt JavaScript API Chatbot Integration

The FlowHunt JS API gives you full control over how your chatbot integrates with your website. Using the v2 integration code, you can embed the chatbot, subscribe to lifecycle and interaction events, pass dynamic data via flow variables, fire the Chat Hook trigger mid-conversation with sendHook(), track interactions with URL parameters, and programmatically control the chat window.

This guide covers every aspect of the JS API with code examples you can copy and adapt to your project.

Integration Code (v2)

Copy and paste the snippet below into your HTML just before the closing </body> tag. Replace YOUR_CHATBOT_ID and YOUR_WORKSPACE_ID with the values from your FlowHunt chatbot settings.

<script id="fh-chatbot-script-YOUR_CHATBOT_ID">
  var currentScript = document.currentScript
    || document.getElementById('fh-chatbot-script-YOUR_CHATBOT_ID');

  var script = document.createElement('script');
  script.async = true;
  script.src = 'https://app.flowhunt.io/api/chatbot/YOUR_CHATBOT_ID'
    + '?workspace_id=YOUR_WORKSPACE_ID&v=2';

  script.onload = function () {
    window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
      // The chatbot is ready — use chatbotManager here
    });
  };

  if (currentScript && currentScript.parentNode) {
    currentScript.parentNode.insertBefore(script, currentScript.nextSibling);
  } else {
    document.head.appendChild(script);
  }
</script>

The chatbot ID in the global variable name (window.FHChatbot_YOUR_CHATBOT_ID) uses underscores instead of hyphens.

Overriding Configuration with setConfig()

Before calling init(), you can override the default chatbot settings using setConfig():

<script id="fh-chatbot-script-YOUR_CHATBOT_ID">
  var currentScript = document.currentScript
    || document.getElementById('fh-chatbot-script-YOUR_CHATBOT_ID');

  var script = document.createElement('script');
  script.async = true;
  script.src = 'https://app.flowhunt.io/api/chatbot/YOUR_CHATBOT_ID'
    + '?workspace_id=YOUR_WORKSPACE_ID&v=2';

  script.onload = function () {
    window.FHChatbot_YOUR_CHATBOT_ID.setConfig({
      headerTitle: 'Support Assistant',
      maxWindowWidth: '700px',
      showChatButton: false,
      flowVariables: {
        userId: '12345',
        plan: 'enterprise',
      },
      urlSuffix: '?utm_source=chatbot',
    });

    window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
      // Chatbot initialised with custom config
    });
  };

  if (currentScript && currentScript.parentNode) {
    currentScript.parentNode.insertBefore(script, currentScript.nextSibling);
  } else {
    document.head.appendChild(script);
  }
</script>

Available Configuration Options

OptionTypeDescription
headerTitlestringCustom header title text
maxWindowWidthstringMaximum chat window width (e.g. "700px")
maxWindowHeightstringMaximum chat window height
inputPlaceholderstringPlaceholder text for the message input
showChatButtonbooleanShow or hide the default floating chat button
openChatPanelbooleanAutomatically open the chat panel on page load
flowVariablesobjectKey–value pairs of custom data passed to the flow. Values may be any JSON-serialisable type (string, number, boolean, object, array).
urlSuffixstringQuery string appended to all chatbot-generated URLs
cookieConsentbooleanEnable session persistence via cookies
embeddedstringSet to enable embedded mode (no close button)
themestringTheme mode

Flow Variables: Pass Dynamic Data

flowVariables are merged into every session the chatbot creates. They are typically used as static context (known at page load): the user’s ID, plan, current locale, etc.

window.FHChatbot_YOUR_CHATBOT_ID.setConfig({
  flowVariables: {
    userId: getCurrentUserId(),
    userEmail: getCurrentUserEmail(),
    currentPage: window.location.pathname,
    plan: 'enterprise',
  },
});

If the user navigates after the chat has opened, values passed here become stale. To update them mid-conversation, call chatbotManager.sendHook() with options.flowVariables — see Mid-conversation host → flow communication below.

URL Suffix: Track Chatbot Interactions

The urlSuffix parameter appends a query string to every URL generated by the chatbot. This is useful for tracking chatbot-originated traffic in analytics tools:

window.FHChatbot_YOUR_CHATBOT_ID.setConfig({
  urlSuffix: '?utm_source=chatbot&utm_medium=widget',
});

Use cases:

  • Track conversions from chatbot interactions in Google Analytics.
  • Analyze user behavior after engaging with the chatbot.
  • Attribute campaigns that drive chatbot engagement.

Logo

Ready to grow your business?

Start your free trial today and see results within days.

Mid-conversation host → flow communication

Added in April 2026 as part of the FlowHunt Chat Hook feature.

In single-page applications the chat window typically stays open while the user navigates between screens. Once the chat is running, flowVariables passed via setConfig() become stale, and there is no way to nudge the flow about something that happened on the host page. A single manager method — sendHook(name, payload, options?) — covers both “fire a trigger” and “just update context” use cases:

  • Pass a name and (optionally) a payload to fire the flow’s Chat Hook trigger. The flow author drops one Chat Hook node on the canvas and branches on {ChatHook.hook_name} to decide what to do.
  • Pass options.flowVariables to merge session variables at the same time — the values are persisted before the trigger fires and remain available to the rest of the session.
  • If the flow has no Chat Hook trigger, the call is a silent 200 no-op: options.flowVariables is still merged, but no trigger runs and no credits are charged. This means host pages can call sendHook() optimistically without knowing whether the flow author has wired a trigger yet.

sendHook() is a safe no-op before the session exists (internally buffered and flushed once the session is created) and never throws to the host page — network errors are logged via console.warn only.

Input limits

Server- and client-side bounds that apply to sendHook(). Violating them is not a crash — the backend returns HTTP 422 and the manager logs via console.warn without throwing.

ConstraintLimitWhere enforcedOn violation
sendHook name length1–256 charsBackend (Pydantic)HTTP 422, trigger does not fire
Number of keys in options.flowVariables≤ 64Backend (Pydantic)HTTP 422, nothing persisted
Length of each key in options.flowVariables≤ 128 charsBackend (Pydantic)HTTP 422, nothing persisted
Pre-session calls buffered by the manager50Widget (in browser)Oldest queued call is dropped and a console.warn logs

The pre-session buffer cap only matters on pages where session creation fails indefinitely (e.g. a permanent network error). Under normal conditions the queue is flushed as soon as onFHChatbotSessionCreated fires.

1. Adding a Chat Hook trigger (flow author side)

  1. Open the flow associated with your chatbot in the FlowHunt editor.
  2. Drag a Chat Hook trigger node onto the canvas. The editor enforces a single trigger per flow — multiple are not allowed.
  3. (Optional) Turn on Validate Payload Schema and define a JSON Schema the incoming payload must match. Validation runs inside the step — a mismatch surfaces as a flow error event (the endpoint still returns 200; the error is visible via onFHError).
  4. Wire the trigger’s outputs (hook_name, payload, flow_variables) into whatever downstream steps you want to fire (Generator, Chat Output, Tool Calls, conditional branches on hook_name, etc.).
  5. Publish the flow.

2. Branching on different hook names inside the flow

The name the host page passes to sendHook() is a label for your flow to branch on, not a routing key — the backend does not match names against nodes. Instead, the flow’s single Chat Hook trigger fires and exposes the name as {ChatHook.hook_name}, which you reference in your flow logic to decide what to do.

Example system prompt:

If {ChatHook.hook_name} is "screen_changed", briefly summarise the page at
{ChatHook.payload.url}. If it is "user_action", acknowledge the action and
update memory. Otherwise, continue the conversation normally.

For more complex routing, wire a conditional step on {ChatHook.hook_name} and branch into multiple downstream paths.

3. chatbotManager.sendHook(name, payload, options?)

chatbotManager.sendHook(
  name: string,
  payload?: Record<string, unknown>,
  options?: { flowVariables?: Record<string, unknown> }
): Promise<void>;

Arguments

ArgumentTypeRequiredDescription
namestringyesLabel forwarded to the flow as {ChatHook.hook_name}. The trigger fires regardless of the value; your flow logic branches on it.
payloadobjectnoJSON payload handed to the trigger as {ChatHook.payload}. Validated against the node’s schema if enabled. Default {}.
options.flowVariablesobjectnoSession variables to merge before the trigger fires. Available to downstream steps and future user messages.

Behaviour

  • Calling before onFHChatbotSessionCreated is safe; the call is buffered and flushed once the session exists. The buffer is capped — see Input limits above.
  • If the flow has no Chat Hook trigger, the request is a silent 200 no-op — so host pages can call sendHook() optimistically before the flow author wires a trigger. options.flowVariables is still persisted even in that case, so the same call serves as a context-only update.
  • If the Chat Hook trigger has schema validation enabled and the payload fails it, validation runs inside the step — the endpoint returns 200 but the flow emits a step error event (observable via onFHError).
  • name and options.flowVariables are bounded — see Input limits above. Violations return HTTP 422 and nothing is persisted.
  • Successful calls emit an onFHChatbotFlowVariablesUpdate event if options.flowVariables was supplied (see Event Reference).
  • Never throws. Network failures and non-2xx responses are logged via console.warn.

Example — proactive suggestion on SPA navigation

window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
  window.addEventListener('hashchange', function () {
    chatbotManager.sendHook('screen_changed', {
      url: window.location.href,
      screen_name: getScreenName(),
    }, {
      flowVariables: { current_page_url: window.location.href },
    });
  });
});

Example — context-only update (no Chat Hook wired)

If the flow has no Chat Hook trigger, the call is a silent 200 — so the same API can keep flow_variables in sync without firing anything:

window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
  window.addEventListener('hashchange', function () {
    chatbotManager.sendHook('navigate', {}, {
      flowVariables: {
        current_page_url: window.location.href,
        screen_name: getScreenName(),
      },
    });
  });
});

4. How the flow author consumes these values

  • {ChatHook.hook_name} and {ChatHook.payload.foo} — values passed via sendHook('x', { foo: 1 }) are exposed on the Chat Hook trigger’s execution path. Only available on the path started by the hook fire.
  • {flow_variables.foo} — values passed via options.flowVariables are merged into the session’s variable bag before the trigger fires. Every execution path (including normal Chat Input–triggered user messages) can read them.

If you want the next user message to see a new value, put it in options.flowVariables — the payload alone only affects the execution run started by that hook.


Event Reference

The FlowHunt JS API dispatches 11 custom events on the window object. All events use the CustomEvent API with bubbles: true and composed: true.

onFHChatbotReady

Fired when the chatbot widget has fully rendered and is ready to use.

  • Event data: none.
  • When: after the widget DOM is mounted and the chat button is visible.

onFHChatbotSessionCreated

Fired when a new chat session is created on the server.

  • Event data: event.detail.sessionId — the newly created session’s ID.
  • When: after a successful session creation API call.

onFHChatbotWindowOpened

Fired when the user opens the chat window. Not fired in embedded mode.

onFHChatbotWindowClosed

Fired when the user closes the chat window. Not fired in embedded mode.

onFHMessageSent

Fired when the user sends a message.

event.detail.metadata = {
  content: 'Hello, I need help with...',
  createdAt: '2026-02-19T10:30:00.000Z',
};

onFHMessageReceived

Fired when the chatbot receives and displays a response.

event.detail.metadata = {
  flow_id: 'abc123',
  message_id: 'msg_456',
  message: 'Sure, I can help you with that!',
  sender: {
    sender_name: 'Support Agent',
    sender_avatar: 'https://example.com/avatar.png',
  },
};

sender is optional and only present when a human agent is involved.

onFHFormDataSent

Fired when the user submits form data through the chatbot.

event.detail.metadata = {
  objectData: { name: 'John', email: 'john@example.com' },
  createdAt: '2026-02-19T10:31:00.000Z',
};

onFHFeedback

Fired when a user gives thumbs-up / thumbs-down feedback on a chatbot message.

event.detail.metadata = {
  message_id: 'msg_456',
  content: 'Optional feedback text',
  feedback: 'P', // 'P' = positive, 'N' = negative
};

onFHToolCall

Fired when a tool or action is executed during flow processing. Only fired in flowAssistant and flowAssistantV3 modes.

event.detail.metadata = {
  metadata: {
    flow_id: 'abc123',
    message_id: 'msg_789',
    message: 'Calling search API...',
  },
  createdAt: '2026-02-19T10:32:00.000Z',
};

onFHError

Fired when an error occurs during chatbot operation.

event.detail.metadata = {
  metadata: {
    flow_id: 'abc123',
    message_id: 'msg_err',
    message: 'Flow execution failed',
  },
  createdAt: '2026-02-19T10:33:00.000Z',
};

onFHChatbotFlowVariablesUpdate

Added in April 2026.

Fired after a successful chatbotManager.sendHook(...) call that supplied options.flowVariables. Not fired for sendHook() calls that omit flowVariables.

event.detail = {
  variables: {
    current_page_url: 'https://example.com/products',
    screen_name: 'products',
  },
};

Use it to observe merged-in variables (e.g. to sync your own host-side state, for debugging, or to re-render a UI element that depends on the same data).


Subscribing to Events

There are two ways to subscribe to chatbot events.

Method 1: Window Event Listeners

Use window.addEventListener anywhere in your page. This works even before the chatbot has loaded:

<script>
document.addEventListener('DOMContentLoaded', function () {
  window.addEventListener('onFHChatbotReady', function () {
    console.log('Chatbot is ready');
  });

  window.addEventListener('onFHChatbotSessionCreated', function (event) {
    console.log('Session created:', event.detail.sessionId);
  });

  window.addEventListener('onFHChatbotWindowOpened', function () {
    console.log('Chat window opened');
  });

  window.addEventListener('onFHChatbotWindowClosed', function () {
    console.log('Chat window closed');
  });

  window.addEventListener('onFHMessageSent', function (event) {
    console.log('User sent:', event.detail.metadata.content);
  });

  window.addEventListener('onFHMessageReceived', function (event) {
    console.log('Bot replied:', event.detail.metadata.message);
  });

  window.addEventListener('onFHFormDataSent', function (event) {
    console.log('Form submitted:', event.detail.metadata.objectData);
  });

  window.addEventListener('onFHFeedback', function (event) {
    var fb = event.detail.metadata;
    console.log('Feedback on message', fb.message_id, ':', fb.feedback);
  });

  window.addEventListener('onFHToolCall', function (event) {
    console.log('Tool called:', event.detail.metadata);
  });

  window.addEventListener('onFHError', function (event) {
    console.error('Chatbot error:', event.detail.metadata);
  });

  window.addEventListener('onFHChatbotFlowVariablesUpdate', function (event) {
    console.log('Variables merged:', event.detail.variables);
  });
});
</script>

To remove a listener, hold onto the handler reference:

var handleMessage = function (event) {
  console.log(event.detail.metadata);
};
window.addEventListener('onFHMessageReceived', handleMessage);
// Later …
window.removeEventListener('onFHMessageReceived', handleMessage);

Method 2: Manager Callback Methods

Inside the init() callback, use the chatbot manager’s built-in methods:

window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
  chatbotManager.onSessionCreated(function () {
    console.log('Session created');
  });

  chatbotManager.onWindowOpened(function () {
    console.log('Window opened');
  });

  chatbotManager.onWindowClosed(function () {
    console.log('Window closed');
  });

  chatbotManager.onMessageSent(function (event) {
    console.log('User sent:', event.metadata);
  });

  chatbotManager.onMessageReceived(function (event) {
    console.log('Bot replied:', event.metadata);
  });

  chatbotManager.onFormDataSent(function (event) {
    console.log('Form data:', event.metadata);
  });

  chatbotManager.onFeedback(function (event) {
    console.log('Feedback:', event.metadata);
  });

  chatbotManager.onToolCall(function (event) {
    console.log('Tool call:', event.metadata);
  });

  chatbotManager.onError(function (event) {
    console.error('Error:', event.metadata);
  });
});

Manager Methods Reference

MethodParametersDescription
onSessionCreated(fn)fn: () => voidListen for session creation
onWindowOpened(fn)fn: () => voidListen for window open
onWindowClosed(fn)fn: () => voidListen for window close
onMessageSent(fn)fn: (event) => voidListen for user messages
onMessageReceived(fn)fn: (event) => voidListen for bot responses
onFormDataSent(fn)fn: (event) => voidListen for form submissions
onFeedback(fn)fn: (event) => voidListen for user feedback
onToolCall(fn)fn: (event) => voidListen for tool executions
onError(fn)fn: (event) => voidListen for errors
openChat()Open the chat panel
closeChat()Close the chat panel
sendHook(name, payload?, options?) (new)name: string, payload?: object, options?: { flowVariables?: object }Fire the Chat Hook trigger in the running flow (or merge options.flowVariables silently if no trigger is wired)

Custom Chat Activation: Hide Button and Open on Click

To fully control when the chatbot appears, hide the default floating button and open the chat programmatically — for example, from your own custom button.

<button id="my-chat-button">Chat with us</button>

<script id="fh-chatbot-script-YOUR_CHATBOT_ID">
  var currentScript = document.currentScript
    || document.getElementById('fh-chatbot-script-YOUR_CHATBOT_ID');

  var script = document.createElement('script');
  script.async = true;
  script.src = 'https://app.flowhunt.io/api/chatbot/YOUR_CHATBOT_ID'
    + '?workspace_id=YOUR_WORKSPACE_ID&v=2';

  script.onload = function () {
    window.FHChatbot_YOUR_CHATBOT_ID.setConfig({ showChatButton: false });

    window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
      document.getElementById('my-chat-button')
        .addEventListener('click', function () {
          chatbotManager.openChat();
        });
    });
  };

  if (currentScript && currentScript.parentNode) {
    currentScript.parentNode.insertBefore(script, currentScript.nextSibling);
  } else {
    document.head.appendChild(script);
  }
</script>

Show a Custom Button Only When the Chatbot Is Ready

You can combine hidden activation with event listeners to create advanced interactions:

<button id="open-chat" style="display:none;">Need help?</button>

<script>
  window.addEventListener('onFHChatbotReady', function () {
    document.getElementById('open-chat').style.display = 'block';
  });
</script>

<script id="fh-chatbot-script-YOUR_CHATBOT_ID">
  var currentScript = document.currentScript
    || document.getElementById('fh-chatbot-script-YOUR_CHATBOT_ID');

  var script = document.createElement('script');
  script.async = true;
  script.src = 'https://app.flowhunt.io/api/chatbot/YOUR_CHATBOT_ID'
    + '?workspace_id=YOUR_WORKSPACE_ID&v=2';

  script.onload = function () {
    window.FHChatbot_YOUR_CHATBOT_ID.setConfig({ showChatButton: false });

    window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
      document.getElementById('open-chat')
        .addEventListener('click', function () {
          chatbotManager.openChat();
        });
    });
  };

  if (currentScript && currentScript.parentNode) {
    currentScript.parentNode.insertBefore(script, currentScript.nextSibling);
  } else {
    document.head.appendChild(script);
  }
</script>

Complete Integration Example

A full working example that demonstrates configuration overrides, event tracking, custom chat activation, and the new sendHook() method together:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>FlowHunt Chatbot Integration</title>
  </head>
  <body>
    <h1>My Website</h1>
    <button id="open-chat-btn">Talk to our AI assistant</button>
    <button id="close-chat-btn">Close chat</button>

    <!-- Subscribe to events before the chatbot loads -->
    <script>
      document.addEventListener('DOMContentLoaded', function () {
        window.addEventListener('onFHChatbotReady', function () {
          console.log('Chatbot widget is ready');
        });

        window.addEventListener('onFHChatbotSessionCreated', function (event) {
          console.log('New chat session started:', event.detail.sessionId);
        });

        window.addEventListener('onFHMessageSent', function (event) {
          console.log('User message:', event.detail.metadata.content);
        });

        window.addEventListener('onFHMessageReceived', function (event) {
          console.log('Bot response:', event.detail.metadata.message);
        });

        window.addEventListener('onFHChatbotFlowVariablesUpdate', function (event) {
          console.log('Context updated:', event.detail.variables);
        });

        window.addEventListener('onFHError', function (event) {
          console.error('Chatbot error:', event.detail.metadata);
        });
      });
    </script>

    <!-- FlowHunt integration -->
    <script id="fh-chatbot-script-YOUR_CHATBOT_ID">
      var currentScript = document.currentScript
        || document.getElementById('fh-chatbot-script-YOUR_CHATBOT_ID');

      var script = document.createElement('script');
      script.async = true;
      script.src = 'https://app.flowhunt.io/api/chatbot/YOUR_CHATBOT_ID'
        + '?workspace_id=YOUR_WORKSPACE_ID&v=2';

      script.onload = function () {
        window.FHChatbot_YOUR_CHATBOT_ID.setConfig({
          showChatButton: false,
          headerTitle: 'AI Assistant',
          maxWindowWidth: '600px',
          flowVariables: {
            source: 'website',
            current_page_url: window.location.href,
          },
          urlSuffix: '?utm_source=chatbot',
        });

        window.FHChatbot_YOUR_CHATBOT_ID.init(function (chatbotManager) {
          // Open / close from custom buttons
          document.getElementById('open-chat-btn')
            .addEventListener('click', function () {
              chatbotManager.openChat();
            });
          document.getElementById('close-chat-btn')
            .addEventListener('click', function () {
              chatbotManager.closeChat();
            });

          // Keep the flow's context in sync with SPA navigation and
          // optionally fire the Chat Hook trigger (if the flow has one wired).
          // If the flow has no Chat Hook, the call is a silent 200 — the
          // flow_variables still get merged, so the same line covers both
          // "notify the flow" and "just update context".
          window.addEventListener('hashchange', function () {
            chatbotManager.sendHook('screen_changed', {
              url: window.location.href,
            }, {
              flowVariables: { current_page_url: window.location.href },
            });
          });
        });
      };

      if (currentScript && currentScript.parentNode) {
        currentScript.parentNode.insertBefore(script, currentScript.nextSibling);
      } else {
        document.head.appendChild(script);
      }
    </script>
  </body>
</html>

Frequently asked questions

Learn more

Setting Up Welcome Messages and Onboarding for New FlowHunt Users
Setting Up Welcome Messages and Onboarding for New FlowHunt Users

Setting Up Welcome Messages and Onboarding for New FlowHunt Users

A comprehensive guide to configuring chatbot welcome messages and understanding the FlowHunt onboarding experience for new users, including registration notific...

6 min read
onboarding welcome message +3