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
- tell Rose when your cookie banner allows it to identify the visitor
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:
This returns:
truewhen the widget is fully loadedfalsewhen 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 |
setIdentificationConsent(granted) |
Tells Rose whether your cookie banner allows visitor identification | Sites where Rose identifies visitors only after consent |
What getIdentityContext() returns¶
{
sessionId: "sess_123",
lastActiveSessionId: "sess_123",
personId: "person_456",
visitorId: "visitor_789"
}
Some values can be null.
For example:
lastActiveSessionIdis the session ID to forward to HubSpot, booking tools, or your own CRM because it points to the last conversation where the visitor actively engagedsessionIdis the current widget session on the page. Do not use it as a fallback for CRM attribution.visitorIdidentifies the Rose visitor and is the value to ingest into HubSpot when you want to match a contact back to RosepersonIdmay still benullif 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:lastActiveSessionIdrose_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:
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. formIdis 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
?.(oronReady). It returnsfalseand 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'
}
});
formIdis optional when your Rose config has one post-conversion form. Pass it when several forms exist.contextis optional. Values are included in Rose's post-conversion completion payload asvisitor_contact.trackConversiondefaults tofalse. SettrackConversion: trueonly 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 insideonReady.
window.InboundXWidget?.onReady?.(() => {
// ... later, when your form submission succeeds:
window.InboundXWidget.startPostConversionQualification({
formId: 'demo-request',
trackConversion: true
});
});
Identify visitors only after cookie consent¶
By default Rose identifies a visitor's company (IP lookup and de-anonymization providers) once they engage with the widget. If your cookie banner must allow this first, ask your Rose account manager to turn on Identify only after cookie consent for your site. Rose then stays visible and answers every visitor, but identifies nobody until your page reports consent.
Report consent from your banner's consent callback, or from a Google Tag Manager tag fired by your consent trigger:
function setRoseIdentificationConsent(granted) {
// Read by Rose if it loads after this call.
window.__INBOUNDX_IDENTIFICATION_CONSENT__ = granted;
// Applies immediately if Rose is already loaded.
window.InboundXWidget?.setIdentificationConsent?.(granted);
}
Call setRoseIdentificationConsent(true) when the visitor accepts and
setRoseIdentificationConsent(false) when they refuse or withdraw consent. Calling it
on every page load, before or after Rose loads, and calling it several times, are all
safe: the latest value wins.
- Consent given mid-conversation: Rose identifies the visitor from their next message.
- Consent withdrawn (
false): Rose starts no new identification on that page. - Rose does not read your banner itself. You choose which consent purpose allows visitor identification.
- The option covers identification only. Before consent, Rose still stores its session and analytics identifiers in the browser, records the conversation and measures widget usage (displays, opens, messages) under that analytics identifier.
Example with a banner that implements the IAB TCF v2 API (Sirdata, Didomi, OneTrust…):
<script>
window.__tcfapi?.('addEventListener', 2, function (tcData, success) {
if (!success) return;
if (tcData.eventStatus !== 'tcloaded' && tcData.eventStatus !== 'useractioncomplete') return;
// Replace with the purpose(s) you map to visitor identification.
setRoseIdentificationConsent(tcData.purpose.consents[1] === true);
});
</script>
With Google Tag Manager, add a Custom HTML tag containing
<script>setRoseIdentificationConsent(true);</script> (define the function in the
same tag or earlier) and fire it on the trigger your banner uses for the accepted
consent.
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?) |
| Identify visitors only once your cookie banner allows it | setIdentificationConsent(granted) |
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 sendlastActiveSessionIdandvisitorIdtogether.