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_CLIENT_ID with your unique client identifier.
<script>
window.NaomaConfig = {
clientId: 'YOUR_CLIENT_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 |
|---|---|---|
clientIdrequired | string | Your unique client identifier. |
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.// 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 = {
clientId: 'YOUR_CLIENT_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.csvReturns your account's sessions, ordered oldest first. Test sessions are excluded.
Returns sessions as a JSON array. A key is present only when it was selected via fields and has a value β the response never contains nulls. The transcript is filtered to user and assistant turns.
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"metadata": {"email": "jane@acme.com", "utm_source": "linkedin"},
"transcript": [
{"role": "assistant", "content": "Hi! I'm Naoma. What brings you here today?", "audio_offset_ms": 0},
{"role": "user", "content": "We're looking at AI for our sales team.", "audio_offset_ms": 5230},
{"role": "assistant", "content": "Great β how large is the team?", "audio_offset_ms": 9100}
],
"summary": "Jane from Acme evaluated Naoma for a 25-person sales team.",
"evaluation": {
"language": "en",
"customer_name": "Jane Doe",
"qualification": {
"status": "qualified",
"reasoning": "Jane manages a 25-person sales team and confirmed budget approval."
},
"questions": {
"team_size": {"was_asked": true, "was_answered": true, "answer": "About 25 people."}
},
"insights": {
"buying_timeline": {"value": "Decision expected this quarter", "evidence": "We want something running before the end of Q3."}
},
"prospect_questions": [
{"question": "Does it integrate with HubSpot?", "answer": "Yes, via webhooks."}
],
"objections": [],
"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."
},
"email": "jane@acme.com",
"email_verified": true,
"duration_secs": 184,
"created_at": "2026-07-14T10:30:00.123456Z"
}
]
}qualification.status is one of qualified, disqualified, not_enough_data. The questions and insights keys are the ones configured on your agent. email is the prospect's address collected during the session, normalized; email_verified reports the domain-level check β true when the domain accepts mail (not that the mailbox exists), false when it does not, and the key is omitted when no email was collected or the session is not yet evaluated.
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.
| 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. 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 full session object: every field is always present, with null when unset, and the transcript is unfiltered β it includes the agent's system message and tool activity alongside the spoken turns. The evaluation object has the same shape as in List Sessions.
{
"event_type": "session.evaluated",
"timestamp": "2026-07-14T10:30:05.123456789Z",
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"account_id": "0d4f6f10-3f9e-4b21-9d2a-6a1b2c3d4e5f",
"account_name": "Acme Inc",
"client_id": "acme",
"agent_id": "naoma_agent_4fz2k9qj1m8p7w3x",
"agent_name": "Acme Discovery Agent",
"metadata": {"email": "jane@acme.com", "utm_source": "linkedin"},
"referrer": "https://acme.com/pricing",
"is_test": false,
"draft": false,
"status": "processed",
"engine": "conva",
"external_conversation_id": null,
"transcript": [
{"role": "system", "content": "You are Acme's discovery agent..."},
{"role": "assistant", "content": "Hi! I'm Naoma. What brings you here today?", "audio_offset_ms": 0},
{"role": "user", "content": "We're looking at AI for our sales team.", "audio_offset_ms": 5230},
{"role": "tool_call", "tool_call": {"id": "call_1", "name": "open_email_collection", "args": {}}, "audio_offset_ms": 9100},
{"role": "tool_result", "tool_result": {"tool_call_id": "call_1", "result": {"status": "opened"}}, "audio_offset_ms": 9100},
{"role": "assistant", "content": "Great β how large is the team?", "audio_offset_ms": 9400}
],
"summary": "Jane from Acme evaluated Naoma for a 25-person sales team.",
"evaluation": {"...": "same shape as in List Sessions"},
"evaluation_error": null,
"duration_secs": 184,
"email": "jane@acme.com",
"email_verified": true,
"runtime_config_version_id": "7c1d2e30-aa11-4bde-8f00-112233445566",
"runtime_config": {"...": "the agent configuration the session ran on"},
"created_at": "2026-07-14T10:27:00.481920Z",
"updated_at": "2026-07-14T10:30:04.912345Z"
}
}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