Skip to Content
DocumentationGuidesWidget Integration

What you’ll build

By the end of this guide your website will have a floating voice button that:

  • Opens a live AI voice call when clicked
  • Uses your branding (colors, logo, name)
  • Passes your logged-in user’s details to the agent
  • Lets the agent open forms mid-call and submit them to your backend

Step 1 — Create your agent in the dashboard

  1. Sign in at app.oshara.ai 
  2. Go to Characters → New Character
  3. Fill in:
    • Slug — a URL-safe ID, e.g. support-bot (you’ll use this in the script tag)
    • System prompt — what the agent should do and how it should behave
    • Greeting — the first thing it says when a call starts
  4. Under Allowed Origins, add your website domain:
    https://yoursite.com
  5. Save the character.

Step 2 — Embed the script tag

Paste this before the closing </body> tag on every page where you want the widget:

<script src="https://api.oshara.ai/widget.js" data-agent="support-bot" data-api-url="https://api.oshara.ai"> </script>

Replace support-bot with your character’s slug. A floating button will appear in the bottom-right corner.

Test it: Open the page, click the button, allow mic access, and click Start Call. You should hear the greeting within a few seconds.


Step 3 — Customise the appearance

Go to Characters → [your character] → Widget Appearance in the dashboard and set your brand colors, logo, and panel size. Or configure it via the API:

PATCH /api/ai-characters/support-bot/ { "widget_appearance": { "name": "Acme Assistant", "logo_url": "https://cdn.yoursite.com/logo.png", "fab_label": "Talk to us", "theme": { "primary_color": "#FF6B35", "background_color": "#FFFFFF", "text_color": "#1A1A1A" }, "layout": { "position": "bottom-right", "font_family": "Inter, sans-serif" } } }

See the full Appearance reference for every option.


Step 4 — Pass your user’s context to the agent

When your user is logged in, you want the agent to know who they are. There are two routes, and which one you want depends on whether the agent has to trust the values.

From the page (simplest)

Hand the widget what your page already knows. It travels with the session and lands in the agent’s prompt:

<script src="https://api.oshara.ai/widget.js" data-agent="support-bot" data-api-url="https://api.oshara.ai" data-context="The visitor is on the Pro plan and has an open billing ticket (#4821)." data-metadata='{"user_id":"u_123","plan":"pro"}'> </script>

If you only learn who the visitor is after login, set it from JavaScript instead — the handle is ready as soon as the script is parsed:

window.__OsharaVoiceWidget.setContext( `Signed in as ${user.name}. Trial ends in ${daysLeft} days.` ); window.__OsharaVoiceWidget.setMetadata({ user_id: user.id, plan: user.plan }); // Open the call on something only this page knows window.__OsharaVoiceWidget.setGreeting( `Welcome back, ${user.firstName}. Still about ticket #4821?` ); // Let the agent's MCP tools act as this user window.__OsharaVoiceWidget.setMcpHeaders({ Authorization: `Bearer ${shortLivedUserToken}`, });

Values are picked up when the next call starts. Full reference: Context & Metadata.

Server-side session start (when the values must be trusted)

Anything the page sends, a visitor can edit in devtools. So when the agent must be certain who it’s talking to — before it reads an account balance or cancels a subscription — mint the session on your backend, where the visitor can’t reach the values. This also keeps your API key server-side.

Your backend (Node.js example):

// POST /your-api/start-voice-session app.post("/your-api/start-voice-session", requireAuth, async (req, res) => { const user = req.user; // your authenticated user const response = await fetch("https://api.oshara.ai/api/agents/agent-session/", { method: "POST", headers: { "Content-Type": "application/json", "Origin": "https://yoursite.com" }, body: JSON.stringify({ agent: "support-bot", metadata: { user_id: user.id, user_name: user.name, user_email: user.email, account_tier: user.plan, // any extra context the agent should know } }) }); const session = await response.json(); res.json(session); // { token, livekit_url, room_name, session_id, ... } });

Your frontend then joins with that token. The embed script always mints its own session, so a pre-minted token needs a client that lets you supply one — use the SDK, or connect with the LiveKit client directly as shown in Starting Sessions.

Either route ends up in the same place: metadata reaches the agent’s prompt as caller context and is available when it fills tool arguments, and mcp_headers is merged into every MCP and HTTP tool call for that session.


Step 5 — Handle post-call form submissions

If you’ve defined forms on your character, the widget will POST submissions to your submit_url. Set up an endpoint on your backend to receive them:

// Express example app.post("/api/demo-requests", express.json(), (req, res) => { const { name, email, company, date } = req.body; // Save to your database, trigger a Zapier hook, send a Slack notification... await db.demoRequests.create({ name, email, company, date }); await slack.notify(`New demo request from ${name} <${email}>`); res.status(200).json({ ok: true }); });

In the form definition, point submit_url at this endpoint:

{ "id": "book-demo", "title": "Book a demo", "submit_url": "https://yoursite.com/api/demo-requests", "fields": [ { "name": "name", "label": "Your name", "type": "text", "required": true }, { "name": "email", "label": "Work email", "type": "email", "required": true }, { "name": "company", "label": "Company", "type": "text" }, { "name": "date", "label": "Preferred date","type": "date" } ] }

The widget sends a JSON POST with the field values as the body. Return any 2xx to confirm. See Form Handling for the full form configuration reference.


Step 6 — Auto-open for dedicated landing pages

For a page whose entire purpose is the voice agent (e.g. a demo page):

<script src="https://api.oshara.ai/widget.js" data-agent="support-bot" data-open-chat="true"> </script>

The panel opens immediately on page load — no button click needed.


Checklist

  • Character created with a slug
  • Your domain added to Allowed Origins
  • Script tag added to your site
  • Call works end-to-end in the browser
  • Branding configured (colors, logo, FAB label)
  • User context passed via data-context / setContext() / metadata (if your site has login)
  • Opening line tailored via data-greeting / setGreeting() (optional)
  • Form submit_url pointing at your backend (if using forms)
Last updated on