Developer API

Embed a voice agent on your website

The <loomira-call> widget puts a live call button on any page with two lines of HTML. No API key ever reaches the browser: the widget mints an anonymous, short-lived session that only works from the origins you allowlist, and only while the agent is active.

Enable embedding in the portal

  1. Open your agent in the portal and switch to the Technical tab, section Embed.
  2. Enable embedding and add the exact origins your pages are served from.
  3. Save — the allowlist only applies once saved.
  4. Copy the snippet and paste it where the button should appear:

HTML

<script type="module" src="https://loomira.ai/embed/v1.js"></script>
<loomira-call agent-id="YOUR_AGENT_ID"></loomira-call>

Origin rules: an entry is an exact browser origin — https://host[:port], lowercase, no path, no trailing slash, no wildcards. https://www.example.com and https://example.com are different origins; list both if both serve the page. For local development, http://localhost:PORT is allowed.

Attributes

AttributeRequiredDescription
agent-idrequiredThe agent this button calls — copied with the snippet from the portal.
labeloptionalIdle-button text. Default: "Call us".
accent-coloroptionalAny CSS color; shorthand for the --loomira-accent custom property.
api-baseoptionalAPI origin override for testing against a non-production environment. The snippet never sets it.

Customized

<loomira-call
  agent-id="YOUR_AGENT_ID"
  label="Talk to our assistant"
  accent-color="#0a6c7e"
></loomira-call>

Deeper theming via CSS custom properties on the element: --loomira-accent, --loomira-accent-contrast, --loomira-radius, --loomira-font.

Security model & CSP

The widget calls the anonymous POST /v1/agents/{id}/embed-token endpoint. The (agent id, Origin) pair is the credential: the browser's Origin header must match your allowlist, so the agent id being visible in your page HTML is safe — it only works from your origins. Refusals are a uniform 403 (no reason disclosed to visitors) and the endpoint is strictly rate-limited per agent and origin.

If your site ships a Content-Security-Policy, allow the widget script with script-src https://loomira.ai (plus connect-src for https://api.loomira.ai and the wss: LiveKit endpoint returned in the session, and media-src/microphone permissions as your policy requires). The script tag carries no integrity hash on purpose: the file is first-party (served from loomira.ai itself, not a third-party CDN) and evergreen — it updates with every release, so a pinned hash would break the button on the next deploy.

No-widget fallback (vanilla JS)

Prefer your own UI? Fetch the embed token yourself and join with livekit-client from npm — the same origin rules apply:

TypeScript

import { Room } from "livekit-client"; // npm install livekit-client

// Anonymous, origin-gated: works only from the https origins you allowlisted.
const session = await fetch(
  "https://api.loomira.ai/v1/agents/YOUR_AGENT_ID/embed-token",
  { method: "POST" },
).then((r) => {
  if (!r.ok) throw new Error("embed refused"); // 403: origin not allowed / agent not active
  return r.json();
}); // -> { url, token, room }

const room = new Room();
await room.connect(session.url, session.token);
await room.localParticipant.setMicrophoneEnabled(true);
// Hang up with room.disconnect().

Troubleshooting 403

  • The page's origin is not on the allowlist — check protocol, host and port match exactly (and that you saved after adding it).
  • Embedding is not enabled for the agent (the toggle in the Technical tab).
  • The agent is not active — draft and archived agents never answer embeds.
  • The request has no Origin header — server-to-server calls must use the API-key-authenticated /web-call mint instead.
  • All other refusals (e.g. workspace billing state) are folded into the same uniform 403 on purpose; check the portal for the actual state.
Tutorial: build the agent behind the button