Skip to content

Widget Public API

If you add custom code to your website, Rose exposes a small browser API on window.InboundXWidget.

You can use it to:

  • check whether the widget is ready
  • read Rose identity context from your own scripts
  • add Rose session and visitor IDs to your own links, redirects, forms, or booking flows
  • send Rose visitor IDs into tools such as HubSpot so contacts can be matched back to Rose

When to use it

The widget loads in the background after your page starts rendering.

If you want to read data from the widget as soon as the page loads, wait until Rose is ready:

<script>
function waitForRoseReady(callback) {
  const tryAgain = () => {
    if (!window.InboundXWidget) {
      window.setTimeout(tryAgain, 100);
      return;
    }

    if (window.InboundXWidget.isReady?.()) {
      callback();
      return;
    }

    if (window.InboundXWidget.onReady) {
      window.InboundXWidget.onReady(callback);
      return;
    }

    window.setTimeout(tryAgain, 100);
  };

  tryAgain();
}

waitForRoseReady(() => {
  console.log('Rose is ready');
});
</script>

You can also check readiness manually:

window.InboundXWidget?.isReady?.();

This returns:

  • true when the widget is fully loaded
  • false when it is still loading

Available methods

Method What it does Typical use
isReady() Returns true when the widget is ready Check whether you can safely call the API
onReady(callback) Runs your callback once the widget is ready Best option when adding custom scripts
getIdentityContext() Returns all available Rose identifiers in one object Recommended for forms, HubSpot, booking links, and redirects
getLastActiveSessionId() Returns the most recent Rose session where the visitor actively engaged Best session ID for CRM and post-chat conversion attribution
getSessionId() Returns the current Rose session ID Use only when you specifically need the current page's widget session
getVisitorId() Returns Rose's visitor ID for this browser when available Send this to HubSpot or your CRM to match contacts back to Rose visitors
getPersonId() Returns the visitor's Rose person ID when available Useful for advanced tracking
trackFormSubmitted(data?) Reports a form submission to Rose (fires the conversion event) Use when Rose cannot auto-detect your form (custom JS or single-page app)
startPostConversionQualification(options?) Opens Rose's post-conversion qualification flow Use after your own conversion success signal when Rose cannot detect it reliably

What getIdentityContext() returns

{
  sessionId: "sess_123",
  lastActiveSessionId: "sess_123",
  personId: "person_456",
  visitorId: "visitor_789"
}

Some values can be null.

For example:

  • lastActiveSessionId is the session ID to forward to HubSpot, booking tools, or your own CRM because it points to the last conversation where the visitor actively engaged
  • sessionId is the current widget session on the page. Do not use it as a fallback for CRM attribution.
  • visitorId identifies the Rose visitor and is the value to ingest into HubSpot when you want to match a contact back to Rose
  • personId may still be null if Rose has not identified the visitor yet

If you are forwarding multiple values, call getIdentityContext() once. If you only need one value, use the dedicated getter.

Most common use case

For most integrations, collect the identity context and forward both:

  • rose_session_id: lastActiveSessionId
  • rose_visitor_id: visitorId
const identity = window.InboundXWidget?.getIdentityContext?.();

const roseSessionId = identity?.lastActiveSessionId;
const roseVisitorId = identity?.visitorId;

if (!roseSessionId) {
  console.log('No active Rose conversation yet');
} else {
  const rosePayload = {
    rose_session_id: roseSessionId,
    rose_visitor_id: roseVisitorId
  };
  // Add rosePayload to your CRM form, redirect, or booking link.
}

The same logic with dedicated getters looks like this:

const roseSessionId = window.InboundXWidget?.getLastActiveSessionId?.();
const roseVisitorId = window.InboundXWidget?.getVisitorId?.();

if (!roseSessionId) {
  console.log('No active Rose conversation yet');
} else {
  const rosePayload = {
    rose_session_id: roseSessionId,
    rose_visitor_id: roseVisitorId
  };
  // Add rosePayload to your CRM form, redirect, or booking link.
}

Use this after the widget is ready, or inside a click handler.

Example: add Rose IDs to your redirect URL

Here is the basic idea:

const url = new URL('https://example.com/book-demo');
const identity = window.InboundXWidget?.getIdentityContext?.();
const lastActiveSessionId = identity?.lastActiveSessionId;
const visitorId = identity?.visitorId;

if (lastActiveSessionId) {
  url.searchParams.set('rose_session_id', lastActiveSessionId);
}

if (visitorId) {
  url.searchParams.set('rose_visitor_id', visitorId);
}

window.location.href = url.toString();

If both IDs are available, the final URL becomes:

https://example.com/book-demo?rose_session_id=sess_123&rose_visitor_id=visitor_789

If the visitor has not had an active Rose conversation yet, lastActiveSessionId can be null. In that case, the redirect still works and opens without rose_session_id.

Example: add Rose IDs to hidden form fields

Use this pattern when your form or HubSpot form includes hidden properties for Rose data.

<input type="hidden" name="rose_session_id" id="rose-session-id" />
<input type="hidden" name="rose_visitor_id" id="rose-visitor-id" />

<script>
window.InboundXWidget?.onReady?.(() => {
  const identity = window.InboundXWidget.getIdentityContext();
  const lastActiveSessionId = identity.lastActiveSessionId;
  const sessionInput = document.getElementById('rose-session-id');
  const visitorInput = document.getElementById('rose-visitor-id');

  if (lastActiveSessionId && sessionInput) {
    sessionInput.value = lastActiveSessionId;
  }

  if (identity.visitorId && visitorInput) {
    visitorInput.value = identity.visitorId;
  }
});
</script>

In HubSpot, create matching contact properties such as rose_session_id and rose_visitor_id, then map your hidden fields to those properties.

Examples by platform

<button id="book-demo-button">Book a demo</button>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const button = document.getElementById('book-demo-button');
  if (!button) return;

  button.addEventListener('click', () => {
    const url = new URL('https://example.com/book-demo');
    const identity = window.InboundXWidget?.getIdentityContext?.();
    const lastActiveSessionId = identity?.lastActiveSessionId;
    const visitorId = identity?.visitorId;

    if (lastActiveSessionId) {
      url.searchParams.set('rose_session_id', lastActiveSessionId);
    }

    if (visitorId) {
      url.searchParams.set('rose_visitor_id', visitorId);
    }

    window.location.href = url.toString();
  });
});
</script>
import React from 'react';

export default function BookDemoButton() {
  const handleClick = () => {
    const url = new URL('https://example.com/book-demo');
    const identity = window.InboundXWidget?.getIdentityContext?.();
    const lastActiveSessionId = identity?.lastActiveSessionId;
    const visitorId = identity?.visitorId;

    if (lastActiveSessionId) {
      url.searchParams.set('rose_session_id', lastActiveSessionId);
    }

    if (visitorId) {
      url.searchParams.set('rose_visitor_id', visitorId);
    }

    window.location.href = url.toString();
  };

  return <button onClick={handleClick}>Book a demo</button>;
}
import * as React from 'react';

export default function BookDemoButton() {
  const handleClick = () => {
    const url = new URL('https://example.com/book-demo');
    const identity = window.InboundXWidget?.getIdentityContext?.();
    const lastActiveSessionId = identity?.lastActiveSessionId;
    const visitorId = identity?.visitorId;

    if (lastActiveSessionId) {
      url.searchParams.set('rose_session_id', lastActiveSessionId);
    }

    if (visitorId) {
      url.searchParams.set('rose_visitor_id', visitorId);
    }

    window.location.href = url.toString();
  };

  return (
    <button
      onClick={handleClick}
      style={{ padding: '12px 20px', cursor: 'pointer' }}
    >
      Book a demo
    </button>
  );
}

In Framer, you can use this inside a Code Component or a custom code block.

Give your button an ID such as book-demo-button, then add this in Project Settings → Custom Code or on the page:

<script>
document.addEventListener('DOMContentLoaded', () => {
  const button = document.getElementById('book-demo-button');
  if (!button) return;

  button.addEventListener('click', function (event) {
    event.preventDefault();

    const url = new URL('https://example.com/book-demo');
    const identity = window.InboundXWidget?.getIdentityContext?.();
    const lastActiveSessionId = identity?.lastActiveSessionId;
    const visitorId = identity?.visitorId;

    if (lastActiveSessionId) {
      url.searchParams.set('rose_session_id', lastActiveSessionId);
    }

    if (visitorId) {
      url.searchParams.set('rose_visitor_id', visitorId);
    }

    window.location.href = url.toString();
  });
});
</script>

If you want to read identity context immediately on page load

For example, if you want to store Rose IDs in your own script before any button click, use waitForRoseReady:

<script>
function waitForRoseReady(callback) {
  const tryAgain = () => {
    if (!window.InboundXWidget) {
      window.setTimeout(tryAgain, 100);
      return;
    }

    if (window.InboundXWidget.isReady?.()) {
      callback();
      return;
    }

    if (window.InboundXWidget.onReady) {
      window.InboundXWidget.onReady(callback);
      return;
    }

    window.setTimeout(tryAgain, 100);
  };

  tryAgain();
}

waitForRoseReady(() => {
  const identity = window.InboundXWidget.getIdentityContext();
  const lastActiveSessionId = identity.lastActiveSessionId;

  if (!lastActiveSessionId) {
    console.log('No active Rose conversation yet');
    return;
  }

  console.log('Rose last active session:', lastActiveSessionId);
  console.log('Rose visitor:', identity.visitorId);
});
</script>

Report a form submission

Rose automatically detects most form submissions. If your form uses custom JavaScript or a single-page app flow that Rose cannot observe, call trackFormSubmitted() yourself when the submission succeeds. This fires the same conversion event Rose records for auto-detected forms.

This method only reports the conversion event. To open Rose's qualification flow, use startPostConversionQualification().

Call it from your own success handler — for example, after your form's API call returns OK:

// In your form's "submitted successfully" handler:
window.InboundXWidget?.trackFormSubmitted?.({ formId: 'demo-request' });
  • All fields are optional. trackFormSubmitted() with no arguments works.
  • formId is optional — pass it only if you want to tell several forms apart in analytics.
  • The widget loads in the background, so guard the call with ?. (or onReady). It returns false and records nothing if Rose is not ready yet.
window.InboundXWidget?.onReady?.(() => {
  // ... later, when the form is submitted:
  window.InboundXWidget.trackFormSubmitted({ formId: 'demo-request' });
});

Prefer no-code? Use Google Tag Manager

If you manage your tags through GTM, you can fire this same conversion from a GTM tag on form submit — without editing your site code. See GTM Conversion Tracking.

Start post-conversion qualification

If your site already knows when a visitor has converted, call startPostConversionQualification() from your own success handler. Rose opens the widget and starts the configured post-conversion qualification questions.

// In your form's "submitted successfully" handler:
window.InboundXWidget?.startPostConversionQualification?.({
  formId: 'demo-request',
  context: {
    email: 'lead@example.com',
    utm_source: 'google'
  }
});
  • formId is optional when your Rose config has one post-conversion form. Pass it when several forms exist.
  • context is optional. Values are included in Rose's post-conversion completion payload as visitor_contact.
  • trackConversion defaults to false. Set trackConversion: true only if you also want this call to record Rose's conversion event.
  • The widget loads in the background, so guard the call with ?. or run it inside onReady.
window.InboundXWidget?.onReady?.(() => {
  // ... later, when your form submission succeeds:
  window.InboundXWidget.startPostConversionQualification({
    formId: 'demo-request',
    trackConversion: true
  });
});

Which method should I use?

Goal Recommended method
Wait until Rose is ready onReady(callback)
Check readiness manually isReady()
Add Rose IDs to HubSpot, a form, a booking link, or a redirect getIdentityContext()
Get the best session ID for CRM attribution getLastActiveSessionId()
Get the current page's widget session ID getSessionId()
Get the Rose visitor ID for CRM matching getVisitorId()
Report a form submission Rose cannot auto-detect trackFormSubmitted(data?)
Start Rose qualification after a known conversion startPostConversionQualification(options?)

Good to know

  • The public API is available on the top-level page where the Rose widget is installed.
  • If you test inside a preview iframe, some values may be missing.
  • For CRM and HubSpot ingestion, prefer getIdentityContext() so you can send lastActiveSessionId and visitorId together.