Integrate Naoma AI demo sessions into your website with the SDK, or access session data programmatically via the REST API.
Embed AI-powered demo sessions with the JavaScript SDK.
Add this snippet to every page where you want to start a demo session. Replace YOUR_AGENT_ID with your agent's id.
<script>
window.NaomaConfig = {
agentId: 'YOUR_AGENT_ID',
// recordingNotice: 'This session is being recorded' // optional
};
(function(d,s,id){
var js,fjs=d.getElementsByTagName(s)[0];
if(d.getElementById(id))return;
js=d.createElement(s);js.id=id;
js.src='https://demoagent.naoma.app/sdk/sdk.js';
fjs.parentNode.insertBefore(js,fjs);
})(document,'script','naoma-sdk');
</script>| Parameter | Type | Description |
|---|---|---|
agentIdrequired | string | Your agent's id (e.g. naoma_agent_...). |
recordingNotice | string | Custom recording notice text to display in the demo session. |
Opens the demo agent in a modal overlay.
| Parameter | Type | Description |
|---|---|---|
config.metadata | object | Data from the user session to pass to the demo (e.g., user ID, name, email, plan). Merged over metadata set via Naoma.setMetadata(). The former name sessionData still works as a deprecated alias. |
config.language | string | Language code for the demo session (e.g., "en", "es", "de"). If not provided, the visitor's browser languages are matched against the agent's configured languages, falling back to the agent's default. |
email metadata key is special. The address under metadata.email becomes the session's email field: Naoma verifies its domain (email_verified in session data and webhook payloads) and, when follow-up emails are enabled for your account, sends the prospect a conversation summary to it. Other keys are stored and passed through as-is.config.sessionData and Naoma.setSessionData() are the former names for config.metadata and Naoma.setMetadata(). They still work as aliases but will be removed β use the metadata names.// Start demo with default settings
Naoma.startDemo();
// Start demo in Spanish
Naoma.startDemo({ language: 'es' });
// Start demo with metadata
Naoma.startDemo({
metadata: {
userId: '12345',
name: 'John Doe',
email: 'john@example.com',
plan: 'premium'
}
});
// Start demo with metadata and language
Naoma.startDemo({
metadata: { userId: '12345', name: 'John Doe' },
language: 'de'
});
// Or set metadata once for all demos started on the page
Naoma.setMetadata({ userId: '12345', email: 'john@example.com' });
Naoma.startDemo();Tracks when a button becomes visible in the viewport.
| Parameter | Type | Description |
|---|---|---|
elementrequired | HTMLElement | The button element to track. |
const btn = document.getElementById("start-demo-btn");
Naoma.trackButtonView(btn);Returns the list of languages supported by the agent, sorted by the user's browser language preferences. Languages matching the user's preferences appear first.
Returns: Promise<string[]> β Array of language codes
const languages = await Naoma.getLanguages();
console.log(languages); // ["en", "es", "de"]
// Build a language selector
if (languages.length > 1) {
const select = document.createElement('select');
languages.forEach(lang => {
const option = document.createElement('option');
option.value = lang;
option.textContent = lang.toUpperCase();
select.appendChild(option);
});
select.onchange = () => Naoma.startDemo({ language: select.value });
}Performs pre-flight checks to determine if the client is eligible to run demo sessions. Use this to conditionally show or hide demo UI elements.
Returns: Promise<{allowed: boolean, reason: string|null}>
const { allowed, reason } = await Naoma.checkClient();
if (allowed) {
document.getElementById('demo-button').style.display = 'block';
} else {
console.log('Demo not available:', reason);
}The widget provides an interactive UI element that can be embedded on your site to offer demo experiences.
Configures the widget's position and data collection behavior.
| Parameter | Type | Description |
|---|---|---|
config.position | string | Widget position. Options: "top-left", "top-right", "bottom-left", "bottom-right", "top-center", "bottom-center", "left-center", "right-center". Default: "bottom-right". |
config.ctaText | string | Custom text for the CTA button. Default: "Talk to an agent". |
config.primaryColor | string | Primary color for buttons and input focus border. Replaces the default gradient with a solid color. e.g., "#0066FF". |
config.textColor | string | Text color for buttons. Default: "white". e.g., "#FFFFFF". |
config.collectData | object | Configuration for collecting additional user data before starting the demo. |
collectData.dataKeyrequired | string | The key under which the collected value is added to session metadata. Use "email" when collecting the prospect's email address β that key powers email verification and follow-up emails (see startDemo above). |
collectData.placeholder | string | Placeholder text for the input field. Default: "Enter your email". |
collectData.formTitle | string | Title text displayed above the input field. Default: "Start your demo". |
collectData.validator | function | Validation function that receives the input value and returns true if valid, or an error message string if invalid. |
Naoma.widget.show().// Position widget with email collection
Naoma.widget.configure({
position: 'bottom-left',
collectData: {
dataKey: 'email',
formTitle: 'Get started today',
placeholder: 'Enter your email',
validator: (value) => value && value.includes('@') || 'Valid email required'
}
});
Naoma.widget.show();
// Position widget with custom colors
Naoma.widget.configure({
position: 'top-right',
ctaText: 'Start a demo',
primaryColor: '#0066FF',
textColor: '#FFFFFF'
});
Naoma.widget.show();Display or hide the widget on the page.
Naoma.widget.show();
Naoma.widget.hide();Expand the widget to show the full interface, or collapse it to its minimal state.
Naoma.widget.expand();
Naoma.widget.collapse();The hero block embeds a large animated avatar directly into your page β ideal for landing page hero sections. A placeholder image is shown instantly while the animation loads.
Mounts the hero block into the given container element.
| Parameter | Type | Description |
|---|---|---|
containerrequired | HTMLElement | The DOM element to mount the hero into. |
config.width | number | Width of the avatar area in pixels. Default: fills the container. |
config.height | number | Height of the avatar area in pixels. Default: fills the container. |
config.ctaText | string | Custom text for the CTA button. Default: "Start demo now". |
config.primaryColor | string | Primary color for buttons and form elements. e.g., "#7C3AED". |
config.textColor | string | Text color for buttons. Default: "white". |
config.collectData | object | Configuration for collecting user data before starting the demo (same shape as widgetβs collectData). |
collectData.dataKeyrequired | string | The key under which the collected value is added to session metadata. Use "email" when collecting the prospect's email address β that key powers email verification and follow-up emails (see startDemo above). |
collectData.placeholder | string | Placeholder text for the input field. Default: "Enter your email". |
collectData.formTitle | string | Title text displayed above the input field. |
collectData.validator | function | Validation function that returns true if valid, or an error message string if invalid. |
Naoma.setMetadata() with any data collected via the form. You can pre-set known user data (name, phone, etc.) and the hero will include it when starting the demo..naoma-hero-* CSS classes that you can override with your own styles.// Simple hero, click CTA to start demo immediately
Naoma.hero.mount(document.getElementById('hero-container'), {
width: 500,
height: 500,
ctaText: 'Start demo now',
primaryColor: '#7C3AED'
});
// Hero with email collection
Naoma.hero.mount(document.getElementById('hero-container'), {
ctaText: 'Try it now',
primaryColor: '#4f46e5',
collectData: {
dataKey: 'email',
placeholder: 'Enter your work email',
formTitle: 'Get a personalized demo',
validator: (value) => {
if (!value || !value.includes('@')) return 'Please enter a valid email';
return true;
}
}
});
// Pre-set metadata, then mount hero
Naoma.setMetadata({ username: 'John', phone: '+1234567890' });
Naoma.hero.mount(document.getElementById('hero-container'));Removes the hero block from the page and cleans up resources.
Naoma.hero.unmount();A full page example using the demo button, hero block, and widget.
<!doctype html>
<html>
<head>
<script>
window.NaomaConfig = {
agentId: 'YOUR_AGENT_ID'
};
(function(d,s,id){
var js,fjs=d.getElementsByTagName(s)[0];
if(d.getElementById(id))return;
js=d.createElement(s);js.id=id;
js.src='https://demoagent.naoma.app/sdk/sdk.js';
fjs.parentNode.insertBefore(js,fjs);
})(document,'script','naoma-sdk');
</script>
</head>
<body>
<div id="demo-section">
<button onclick="startDemo()" id="startDemoButton">Get a demo now!</button>
</div>
<script>
// === OPTION 1: Demo Button ===
// Track button view (optional)
Naoma.trackButtonView(document.querySelector("#startDemoButton"));
// Start demo on button click with metadata
function startDemo() {
Naoma.startDemo({
metadata: {
name: "John Doe",
company: "Acme Inc",
userId: "12345"
}
});
}
// === OPTION 2: Hero Block ===
// Mount an animated avatar hero with email collection
Naoma.hero.mount(document.getElementById('demo-section'), {
width: 400,
height: 400,
ctaText: 'Start demo now',
primaryColor: '#7C3AED',
collectData: {
dataKey: 'email',
placeholder: 'Enter your work email',
validator: (value) => value && value.includes('@') || 'Valid email required'
}
});
// === OPTION 3: Widget ===
// Configure widget position and email collection, then display it
Naoma.widget.configure({
position: 'bottom-right',
ctaText: 'Start a demo',
primaryColor: '#7C3AED',
collectData: {
dataKey: 'email',
formTitle: 'Get started today',
placeholder: 'Enter your email',
validator: (value) => value && value.includes('@') || 'Valid email required'
}
});
Naoma.widget.show();
</script>
</body>
</html>Access session data and receive real-time event notifications via the REST API.
Base URL: https://api.naoma.app
All API requests require authentication via one of two methods. To obtain your credentials, contact the Naoma team.
Authorization: Bearer <token>Where client_id is your account's client ID and token is your API token.
Authorization: Basic <base64(client_id:token)>client_id and token when prompted. For example: https://api.naoma.app/v1/sessions.csvBoth the export and the webhook payload carry the same session object.
| Field | Type | Description |
|---|---|---|
id | uuid | The session. Stable across re-evaluations. |
agent_id | string | The agent that ran the session. Route on it when one receiver serves several agents. |
status | string | Where the session ended up: active, abandoned, processed, or error. |
metadata | object | The data your site passed to the SDK, plus the email collected in the session. Values are strings; anything else you pass arrives JSON-encoded. |
transcript | array | The dialogue, oldest first. See below. |
summary | string | One-paragraph summary of the conversation. |
evaluation | object | The agent's evaluation. See below. |
email | string | The prospect's address collected during the session, normalized. |
email_verified | bool | The domain-level check: true when the domain accepts mail (not that the mailbox exists). |
duration_secs | int | Length of the conversation. |
created_at | string | RFC 3339 timestamp of when the session started. |
A key is present only when it has a value. The object never contains nulls, so read a missing key as βno valueβ, not as an error.
The dialogue as the prospect experienced it: user and assistant turns only. The agent's system prompt and its tool activity are not part of it, and neither are the popup actions the prospect took β a submitted email address arrives on email and metadata instead.
[
{"role": "assistant", "content": "Hi! I'm Naoma. What brings you here today?"},
{"role": "user", "content": "We're looking at AI for our sales team."},
{"role": "assistant", "content": "Great β how large is", "interrupted": true},
{"role": "user", "content": "about 25 people"}
]interrupted means the prospect cut in: content is what they heard before that, not what the agent had generated. Text the agent generated but never spoke is absent. Two assistant entries can follow each other; that is one spoken turn with tool activity in between.
{
"language": "en",
"customer_name": "Jane Doe",
"qualification": {"status": "qualified", "reasoning": "Confirmed team size and budget."},
"questions": {"team_size": {"was_asked": true, "was_answered": true, "answer": "About 25 people."}},
"insights": {"buying_timeline": {"value": "This quarter", "evidence": "Before the end of Q3."}},
"prospect_questions": [{"question": "Does it integrate with HubSpot?", "answer": "Yes, via webhooks."}],
"objections": [{"objection": "The price looks high for our size.", "response": "Walked through the per-demo cost at 100 demos a month."}],
"prospect_summary": "Thanks for the chat, Jane! We covered your team setup and booked a demo.",
"summary": "Jane Doe evaluated Naoma for inbound qualification; qualified with a Q3 timeline."
}qualification.status is one of qualified, disqualified, not_enough_data. The questions and insights keys are the ones configured on your agent.
Returns your account's sessions, ordered oldest first. Test sessions are excluded.
Returns the session objects under items. A key is present only when it was selected via fields and has a value.
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "naoma_agent_5cbudhf8pwrvu8bhcb3ovxcer",
"status": "processed",
"metadata": {"email": "jane@acme.com", "utm_source": "linkedin"},
"transcript": [
{"role": "assistant", "content": "Hi! I'm Naoma. What brings you here today?"},
{"role": "user", "content": "We're looking at AI for our sales team."}
],
"summary": "Jane from Acme evaluated Naoma for a 25-person sales team.",
"evaluation": {"...": "see The session object"},
"email": "jane@acme.com",
"email_verified": true,
"duration_secs": 184,
"created_at": "2026-07-14T10:30:00.123456Z"
}
]
}Returns a CSV file with a header row followed by data rows for the selected fields. Empty cells stand for missing values; email_verified is true, false, or empty. Columns follow the order you pass in fields; omit the parameter and they follow the order listed below.
| Parameter | Type | Description |
|---|---|---|
since | int64 | Unix timestamp. Returns only sessions created after this time. Default: 0. |
limit | int | Max number of sessions to return (1β100). Values above 100 are clamped to 100. Default: 100. |
fields | string | Comma-separated list of fields to include. Available: id, metadata, transcript, summary, evaluation, email, email_verified, duration_secs, created_at, status, agent_id. Unknown names are ignored. Default: all. |
Example:
GET /v1/sessions.json?since=1700000000&limit=50&fields=id,summary,created_atResults are ordered by created_at ascending. To paginate, use the created_at of the last returned item (as a unix timestamp) as the since value in the next request. Because since has whole-second granularity, the next page can repeat the previous page's last items β dedupe by id.
| Status | Description |
|---|---|
400 | Invalid query parameter. |
401 | Missing or invalid credentials. |
Webhooks allow you to receive real-time notifications when events occur. To register a webhook, contact the Naoma team with the URL you'd like to receive events at. You will receive a webhook secret for signature verification.
| Status | Description |
|---|---|
session.evaluated | Fired every time a session's evaluation is written, including re-evaluations of the same session. Test sessions and failed evaluations never fire. |
Webhook events are delivered as POST requests with a JSON body. data is the session object, with every field it has β the export's fields parameter has no equivalent here.
{
"event_type": "session.evaluated",
"timestamp": "2026-07-14T10:30:05.123456789Z",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "naoma_agent_5cbudhf8pwrvu8bhcb3ovxcer",
"status": "processed",
"metadata": {"email": "jane@acme.com", "utm_source": "linkedin"},
"transcript": [
{"role": "assistant", "content": "Hi! I'm Naoma. What brings you here today?"},
{"role": "user", "content": "We're looking at AI for our sales team."}
],
"summary": "Jane from Acme evaluated Naoma for a 25-person sales team.",
"evaluation": {"...": "see The session object"},
"email": "jane@acme.com",
"email_verified": true,
"duration_secs": 184,
"created_at": "2026-07-14T10:27:00.481920Z"
}
}id.Each request includes an X-Webhook-Signature header containing an HMAC-SHA256 hex digest of the request body, signed with your webhook secret. Verification is optional but recommended.
X-Webhook-Signature header value.const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return signature === expected;
}Our team can help you integrate Naoma into your website and get the most out of AI-powered demos.
Talk to Sales Team