Back to Home

Developer Documentation

Integrate Naoma AI demo sessions into your website with the SDK, or access session data programmatically via the REST API.

SDK

Embed AI-powered demo sessions with the JavaScript SDK.

Installation

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>

Configuration Options

ParameterTypeDescription
agentIdrequiredstringYour agent's id (e.g. naoma_agent_...).
recordingNoticestringCustom recording notice text to display in the demo session.

API Reference

Naoma.startDemo(config)

Opens the demo agent in a modal overlay.

ParameterTypeDescription
config.metadataobjectData 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.languagestringLanguage 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.
The 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.
Deprecated: 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();

Naoma.trackButtonView(element)

Tracks when a button becomes visible in the viewport.

ParameterTypeDescription
elementrequiredHTMLElementThe button element to track.
const btn = document.getElementById("start-demo-btn");
Naoma.trackButtonView(btn);

Naoma.getLanguages()

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 });
}

Naoma.checkClient()

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);
}

Widget API

The widget provides an interactive UI element that can be embedded on your site to offer demo experiences.

Naoma.widget.configure(config)

Configures the widget's position and data collection behavior.

ParameterTypeDescription
config.positionstringWidget position. Options: "top-left", "top-right", "bottom-left", "bottom-right", "top-center", "bottom-center", "left-center", "right-center". Default: "bottom-right".
config.ctaTextstringCustom text for the CTA button. Default: "Talk to an agent".
config.primaryColorstringPrimary color for buttons and input focus border. Replaces the default gradient with a solid color. e.g., "#0066FF".
config.textColorstringText color for buttons. Default: "white". e.g., "#FFFFFF".
config.collectDataobjectConfiguration for collecting additional user data before starting the demo.
collectData.dataKeyrequiredstringThe 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.placeholderstringPlaceholder text for the input field. Default: "Enter your email".
collectData.formTitlestringTitle text displayed above the input field. Default: "Start your demo".
collectData.validatorfunctionValidation function that receives the input value and returns true if valid, or an error message string if invalid.
Rendering: configure() only stores the configuration β€” the widget appears once you call Naoma.widget.show().
Widget state persistence: The widget remembers if the user collapsed it. On subsequent page loads, it remains collapsed until the user expands it again (stored in localStorage).
Mobile responsiveness: On viewports ≀ 480px, the data collection form automatically switches to a vertical layout.
// 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();

Naoma.widget.show() / Naoma.widget.hide()

Display or hide the widget on the page.

Naoma.widget.show();
Naoma.widget.hide();

Naoma.widget.expand() / Naoma.widget.collapse()

Expand the widget to show the full interface, or collapse it to its minimal state.

Naoma.widget.expand();
Naoma.widget.collapse();

Hero API

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.

Naoma.hero.mount(container, config)

Mounts the hero block into the given container element.

ParameterTypeDescription
containerrequiredHTMLElementThe DOM element to mount the hero into.
config.widthnumberWidth of the avatar area in pixels. Default: fills the container.
config.heightnumberHeight of the avatar area in pixels. Default: fills the container.
config.ctaTextstringCustom text for the CTA button. Default: "Start demo now".
config.primaryColorstringPrimary color for buttons and form elements. e.g., "#7C3AED".
config.textColorstringText color for buttons. Default: "white".
config.collectDataobjectConfiguration for collecting user data before starting the demo (same shape as widget’s collectData).
collectData.dataKeyrequiredstringThe 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.placeholderstringPlaceholder text for the input field. Default: "Enter your email".
collectData.formTitlestringTitle text displayed above the input field.
collectData.validatorfunctionValidation function that returns true if valid, or an error message string if invalid.
Metadata: The hero automatically merges metadata set via 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.
Styling: All hero elements use .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'));

Naoma.hero.unmount()

Removes the hero block from the page and cleans up resources.

Naoma.hero.unmount();

Complete Example

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>

Public API

Access session data and receive real-time event notifications via the REST API.

Base URL: https://api.naoma.app

Authentication

All API requests require authentication via one of two methods. To obtain your credentials, contact the Naoma team.

Bearer Token

Authorization: Bearer <token>

HTTP Basic Auth

Where client_id is your account's client ID and token is your API token.

Authorization: Basic <base64(client_id:token)>
Tip: Basic Auth is useful for manual exports β€” you can open a download link directly in your browser and enter your client_id and token when prompted. For example: https://api.naoma.app/v1/sessions.csv

The session object

Both the export and the webhook payload carry the same session object.

FieldTypeDescription
iduuidThe session. Stable across re-evaluations.
agent_idstringThe agent that ran the session. Route on it when one receiver serves several agents.
statusstringWhere the session ended up: active, abandoned, processed, or error.
metadataobjectThe data your site passed to the SDK, plus the email collected in the session. Values are strings; anything else you pass arrives JSON-encoded.
transcriptarrayThe dialogue, oldest first. See below.
summarystringOne-paragraph summary of the conversation.
evaluationobjectThe agent's evaluation. See below.
emailstringThe prospect's address collected during the session, normalized.
email_verifiedboolThe domain-level check: true when the domain accepts mail (not that the mailbox exists).
duration_secsintLength of the conversation.
created_atstringRFC 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.

Transcript

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.

Evaluation

{
  "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.

List Sessions

Returns your account's sessions, ordered oldest first. Test sessions are excluded.

GET /v1/sessions.json

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"
    }
  ]
}

GET /v1/sessions.csv

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.

Query Parameters

ParameterTypeDescription
sinceint64Unix timestamp. Returns only sessions created after this time. Default: 0.
limitintMax number of sessions to return (1–100). Values above 100 are clamped to 100. Default: 100.
fieldsstringComma-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_at

Pagination

Results 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.

Errors

StatusDescription
400Invalid query parameter.
401Missing or invalid credentials.

Webhooks

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.

Events

StatusDescription
session.evaluatedFired every time a session's evaluation is written, including re-evaluations of the same session. Test sessions and failed evaluations never fire.

Payload

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"
  }
}
Delivery: your endpoint should respond with a 2xx status within 10 seconds; any other outcome is retried up to 3 attempts, ~30 seconds apart. Re-evaluations re-deliver the session with its updated evaluation, so handle deliveries idempotently by session id.

Signature Verification

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.

  1. Compute HMAC-SHA256 of the raw request body using your secret as the key.
  2. Hex-encode the result.
  3. Compare it to the 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;
}

Need Help?

Our team can help you integrate Naoma into your website and get the most out of AI-powered demos.

Talk to Sales Team