Installation
Choose your preferred installation method:
Add the script before </body>: < script
src = "https://dashboard.ourguide.ai/ourguide-b2b-widget.iife.js"
data-api-url = "https://dashboard.ourguide.ai"
data-product-id = "YOUR_PRODUCT_ID"
data-agent-name = "Your Agent Name"
></ script >
Attribute Required Description data-api-urlYes https://api.ourguide.aidata-product-idYes Your product ID from dashboard data-agent-nameNo Display name (defaults to “Assistant”)
After the script loads, use window.ourguide() to pass functions and objects: Command Description setIdentityTokenFetcherAuto-refreshing JWT function. See Identity setContextPage context object. See Context onToolResultCallback on tool completion. See Callbacks registerToolRenderersCustom inline visuals. See Renderers identifyManual JWT token (alternative to fetcher) registerToolsClient-side tool handlers
Install the SDK: npm install @ourguide-ai/client @ourguide-ai/ui
Add to your app:
import { OurguideWidget } from '@ourguide-ai/ui' ;
import '@ourguide-ai/ui/styles.css' ;
export function OurguideWidgetClient () {
return (
< OurguideWidget
productId = "YOUR_PRODUCT_ID"
apiUrl = "https://dashboard.ourguide.ai"
agentName = "Your Agent Name"
/>
);
}
Prop Required Description productIdYes Your product ID from dashboard apiUrlYes https://api.ourguide.aiagentNameNo Display name (defaults to “Assistant”) getIdentityTokenNo Async function returning a JWT. Auto-refreshes on expiry. See Identity Verification contextNo Page context object sent with every message. Reactive — updates when the object changes toolRenderersNo Map of tool name → React component for inline rendering. See Tool Renderers onToolResultNo Callback (toolName, result) => void fired when a server-side tool completes navigateNo SPA navigation function (e.g. router.push) for internal links
Find Your Product ID
Go to dashboard.ourguide.ai/dashboard/deploy/widget
Copy your Product ID
User Identity
Authenticate users for persistent conversations and user-specific actions.
Option A — Auto-refresh (recommended). Pass a function that fetches a JWT. The SDK calls it on mount and re-calls it automatically when the token expires (401):window . ourguide ( 'setIdentityTokenFetcher' , async () => {
const res = await fetch ( '/api/ourguide-token' );
const { token } = await res . json ();
return token ;
});
Option B — Manual. Pass a static token (you handle refresh yourself):window . ourguide ( 'identify' , {
token: 'jwt-from-your-backend' , // Required
name: 'John Doe' // Optional
})
// On logout
window . ourguide ( 'resetUser' )
Pass getIdentityToken — the SDK handles calling it on mount and auto-refreshing on expiry: import { OurguideWidget } from '@ourguide-ai/ui' ;
function App () {
return (
< OurguideWidget
productId = "YOUR_PRODUCT_ID"
apiUrl = "https://api.ourguide.ai"
getIdentityToken = {async () => {
const res = await fetch ( '/api/ourguide-token' );
const { token } = await res . json ();
return token ;
} }
/>
);
}
See Identity Verification for backend JWT setup.
Register tools the agent can call. The return value from your handler is sent back to the agent, so it can confirm success or handle errors intelligently.
See Client-Side Tools for full setup.
window . ourguide ( 'registerTools' , {
navigateToPage : async ({ path }) => {
router . push ( path )
return { success: true } // Agent sees this result
},
showUpgradeModal : async () => {
document . getElementById ( 'upgrade-modal' ). showModal ()
return { shown: true }
}
})
import { OurguideWidget } from '@ourguide-ai/ui' ;
function App () {
const tools = {
navigateToPage : async ({ path }) => {
router . push ( path );
return { success: true }; // Agent sees this result
},
showUpgradeModal : async () => {
document . getElementById ( 'upgrade-modal' ). showModal ();
return { shown: true };
}
};
return (
< OurguideWidget
productId = "YOUR_PRODUCT_ID"
apiUrl = "https://api.ourguide.ai"
tools = { tools }
/>
);
}
Return meaningful results from your tools. The agent uses these to provide accurate feedback like “Done! I’ve navigated you to Settings” or handle errors gracefully.
React to server-side tool completions — refresh data, show a diff UI, dispatch events, etc.
window . ourguide ( 'onToolResult' , ( toolName , result ) => {
if ( toolName === 'propose_revision' ) {
showDiffUI ( result );
}
});
< OurguideWidget
onToolResult = { ( toolName , result ) => {
if ( toolName === 'propose_revision' ) showDiffUI ( result );
} }
...
/>
Render custom visuals inline in the chat when a tool completes — data cards, tables, charts, and more.
Your renderer receives { result, status } and returns one of:
Return type Use case HTML string Data cards, tables, simple visuals HTMLElement Charts via Chart.js, D3, ECharts, Plotly ReactNode Full React components (React SDK only)
Data Card Show key metrics with change indicators: window . ourguide ( 'registerToolRenderers' , {
query_analytics_chart : ({ result }) => {
const r = result || {};
const color = r . changePercent > 0 ? '#059669' : '#ef4444' ;
const arrow = r . changePercent > 0 ? '↑' : '↓' ;
return `<div style="padding:12px; border:1px solid #e2e8f0; border-radius:8px; background:#fff; font-family:system-ui,sans-serif;">
<div style="font-size:13px; font-weight:600; color:#0f172a;"> ${ r . title || 'Metric' } </div>
<div style="font-size:24px; font-weight:700; color:#2563eb; margin-top:4px;"> ${ r . currentValue || 'N/A' } </div>
${ r . changePercent != null
? `<div style="font-size:12px; color: ${ color } ;"> ${ arrow } ${ Math . abs ( r . changePercent ). toFixed ( 1 ) } %</div>`
: '' }
${ r . dashboard_url
? `<a href=" ${ r . dashboard_url } " target="_blank" style="font-size:11px; color:#2563eb; text-decoration:none; margin-top:8px; display:inline-block;">View full chart ↗</a>`
: '' }
</div>` ;
}
});
Table Display structured data in rows and columns: window . ourguide ( 'registerToolRenderers' , {
query_data_table : ({ result }) => {
const r = result || {};
const headers = r . columns || [];
const rows = r . rows || [];
return `<div style="border:1px solid #e2e8f0; border-radius:8px; overflow:hidden; font-family:system-ui,sans-serif;">
<table style="width:100%; border-collapse:collapse; font-size:12px;">
<thead>
<tr style="background:#f8fafc;">
${ headers . map ( h =>
'<th style="padding:8px 12px; text-align:left; font-weight:600; border-bottom:1px solid #e2e8f0;">' + h + '</th>'
). join ( '' ) }
</tr>
</thead>
<tbody>
${ rows . map ( row =>
'<tr style="border-bottom:1px solid #f1f5f9;">' +
row . map ( cell =>
'<td style="padding:6px 12px; color:#334155;">' + cell + '</td>'
). join ( '' ) +
'</tr>'
). join ( '' ) }
</tbody>
</table>
</div>` ;
}
});
Chart (Chart.js, D3, ECharts) For interactive charts, return an HTMLElement instead of a string. The widget mounts it directly: <!-- Load Chart.js (or any charting library) -->
< script src = "https://cdn.jsdelivr.net/npm/chart.js" ></ script >
< script >
window . ourguide ( 'registerToolRenderers' , {
query_analytics_chart : ({ result }) => {
const canvas = document . createElement ( 'canvas' );
canvas . style . width = '100%' ;
canvas . style . maxHeight = '200px' ;
new Chart ( canvas , {
type: 'line' ,
data: {
labels: result . data . map ( d => d . date ),
datasets: [{
label: result . title ,
data: result . data . map ( d => d . value ),
borderColor: '#2563eb' ,
tension: 0.3 ,
}]
},
options: { responsive: true , plugins: { legend: { display: false } } }
});
return canvas ; // Return the DOM element
}
});
</ script >
Timing: window.ourguide() must be called after ourguide-widget.js has loaded. In plain HTML this happens naturally (scripts execute in order). In Next.js, use onLoad:< Script
src = "https://api.ourguide.ai/static/ourguide-widget.js"
data-product-id = "YOUR_PRODUCT_ID"
data-api-url = "https://api.ourguide.ai"
strategy = "afterInteractive"
onLoad = { () => {
window . ourguide ( 'registerToolRenderers' , { ... });
} }
/>
The widget uses Shadow DOM for CSS isolation. Use inline style attributes — Tailwind classes and external CSS won’t apply inside the widget.
React SDK renderers return JSX — full React components with hooks, state, and libraries like Recharts: < OurguideWidget
toolRenderers = { {
query_analytics_chart : ({ result }) => < MyChart data = { result . data } /> ,
} }
...
/>
Quick Q&A
Some users find it more intuitive to read setup instructions in Q&A format. Here’s everything above as quick answers:
How do I add the chat to my app?
Install the SDK and drop in one component: npm install @ourguide-ai/client @ourguide-ai/ui
< OurguideWidget
productId = "YOUR_PRODUCT_ID"
apiUrl = "https://dashboard.ourguide.ai"
/>
That’s it. Chat works anonymously out of the box. No provider wrapping needed.
How do I authenticate users?
Two steps:
Create a backend endpoint (~15 lines) that reads your existing session and mints a JWT signed with your Ourguide verification secret. Ourguide never sees your auth system — the JWT is the bridge.
Pass it as a prop:
< OurguideWidget
productId = "YOUR_PRODUCT_ID"
apiUrl = "https://dashboard.ourguide.ai"
getIdentityToken = {async () => {
const res = await fetch ( '/api/ourguide-token' );
const { token } = await res . json ();
return token ;
} }
/>
The SDK calls this on mount and auto-refreshes when the token expires. No useEffect, no setInterval, no manual calls. This enables: conversation history, personalized responses, and user-specific tool actions. See Identity Verification for the backend JWT setup.
How do I give the AI context about the current page?
Pass a context object. It’s reactive — when it changes (e.g., user navigates), the AI automatically knows. const context = useMemo (() => {
const match = pathname . match ( / \/ items \/ ( [ ^ / ] + ) / );
return match ? { itemId: match [ 1 ] } : {};
}, [ pathname ]);
< OurguideWidget
...
context = { context }
/>
This gets injected into the AI’s system prompt so it can perform page-aware actions like “edit this item” without asking which one.
How do I handle SPA navigation for links?
Pass your router’s push function: < OurguideWidget
...
navigate = { ( path ) => router . push ( path ) }
/>
Internal links the AI returns will use your SPA router instead of a full page reload.
How do I react when the AI calls a tool?
What does the full setup look like?
Everything is a prop on one component: < OurguideWidget
productId = "..."
apiUrl = "https://dashboard.ourguide.ai"
getIdentityToken = { fetchToken }
navigate = { ( path ) => router . push ( path ) }
onToolResult = { ( name , result ) => ... }
toolRenderers = { { ... } }
/>
No providers, no initializers, no polling.
Troubleshooting
Issue Solution Widget not appearing Check Product ID, verify script/component is rendered Console errors Check apiUrl is correct CORS errors Contact support to whitelist your domain