AurionAI Docs

Voice Widget

Embed the Aurion voice widget into your website for real-time AI voice support.

Voice Widget

The Aurion voice widget is an embeddable Preact component that adds AI-powered voice and chat support to any website. It connects to the same AI agent that powers phone and mobile app calls.

Embed the Widget

The widget is bootstrapped by setting window.AurionSettings and then injecting the loader script. The loader is served directly from your Aurion instance at /api/v1/voice-widget/embed/aurion.js. Paste this inline snippet into your page (the admin dashboard generates a copy-paste-ready version pre-filled with your values):

HTML
<script>
  (function () {
    window.AurionSettings = {
      appId: '<your-tenant-id>',
      publicKey: 'vwk_xxxxxxxxxxxxxxxxxxxxxxxx',
      apiBaseUrl: 'https://apps.aurionai.net',
    }
    var s = document.createElement('script')
    s.src = 'https://apps.aurionai.net/api/v1/voice-widget/embed/aurion.js'
    s.async = true
    document.head.appendChild(s)
  })()
</script>

The widget automatically renders a floating button in the bottom-right corner of your page.

There is no separate "widget ID" — bootstrap requires two values:

  • appId — your tenant UUID.
  • publicKey — the public widget key (format vwk_…). This is not an API key (itsm_sk_…); it is a public, embeddable identifier paired with an origin allowlist.

Get Your Bootstrap Values

  1. Log in to the Aurion Admin Dashboard
  2. Navigate to Configuration > Voice Widget
  3. Configure appearance, welcome text, and allowed origins
  4. Copy the generated embed_snippet (it already contains your appId and publicKey)

Widget Configuration

Configure the widget via the admin dashboard or the API. These endpoints live under /api/v1/voice-widget, which maps to the config API-key scope: GET /config needs config:read, PUT /config needs config:write. A key without the matching scope receives 403 {"detail": "API key missing required scope: config:write"}.

Get widget config
curl "https://apps.aurionai.net/api/v1/voice-widget/config" \
  -H "X-API-Key: itsm_sk_live_xxxx"

GET /config returns the full VoiceWidgetConfigResponse, including public_widget_key, embed_script_url, and a ready-to-paste embed_snippet.

Update widget config
curl -X PUT "https://apps.aurionai.net/api/v1/voice-widget/config" \
  -H "X-API-Key: itsm_sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "display_title": "Support",
    "launcher_label": "Chat with us",
    "welcome_text": "Hi! How can I help you today?",
    "launcher_position": "bottom-right",
    "accent_theme": "blue",
    "primary_color": "#6366F1",
    "allowed_origins": ["https://example.com", "https://app.example.com"]
  }'

enabled (bool), display_title (1–80 chars), and launcher_label (1–40 chars) are required — omitting any of them returns 422. Other fields are optional:

FieldTypeNotes
allowed_originsstring[] (max 50)Full normalized origins (scheme://host, no path), e.g. https://example.com
launcher_position"bottom-right" | "bottom-left"Default bottom-right
accent_theme"blue" | "emerald" | "amber" | "slate"Default blue
primary_color / accent_colorhex stringe.g. #2563eb
welcome_textstring (max 120)
launcher_bubble_messagestring (max 80)
logo_urldata URLPNG / JPEG / SVG, base64 encoded
show_powered_bybool
sso_auto_loginbool
theme"light" | "dark" | "auto"Default auto
quick_actions_enabledboolDefault true
*_translations{ locale: string }Per-locale maps for display_title, launcher_label, welcome_text, launcher_bubble_message

Note allowed_origins are full origins (https://example.com), not bare domains.

Widget Chat API

The public chat surface lives under /api/v1/widget. These endpoints are not authenticated with an API key or a Bearer session token — they authenticate every request with your widget public key (vwk_…) plus an Origin header that matches your allowed origins. The runtime sends these automatically; the examples below show the raw contract.

The optional HMAC-signed email token (returned by /session below) lets a returning visitor fetch their own conversation history. It is the output of the session call, not a login credential.

Create a Session

Exchange the visitor's email for an HMAC-signed email_token. Provide your tenant_id, public_key, and the visitor's email in the body; the request must carry an allowed Origin.

curl -X POST "https://apps.aurionai.net/api/v1/widget/session" \
  -H "Content-Type: application/json" \
  -H "Origin: https://example.com" \
  -d '{
    "tenant_id": "11111111-2222-3333-4444-555555555555",
    "public_key": "vwk_xxxxxxxxxxxxxxxxxxxxxxxx",
    "email": "jane@example.com"
  }'

Response (200 OK):

{
  "email_token": "eyJ0aWQiOiI...base64payload.hexsignature"
}

Send a Message

The message body field is body (1–4000 chars), and tenant_id + public_key go in the JSON body — there is no Authorization header.

curl -X POST "https://apps.aurionai.net/api/v1/widget/messages" \
  -H "Content-Type: application/json" \
  -H "Origin: https://example.com" \
  -d '{
    "tenant_id": "11111111-2222-3333-4444-555555555555",
    "public_key": "vwk_xxxxxxxxxxxxxxxxxxxxxxxx",
    "body": "I need help resetting my password"
  }'

Optional fields: sender_email, source_thread_id, source_message_id, upload_ids (from /uploads), metadata.

Response (200 OK):

{
  "tenant_id": "11111111-2222-3333-4444-555555555555",
  "conversation_id": "conv_abc123",
  "thread_id": "thread_abc123",
  "contact_id": "contact_abc123",
  "user_message_id": "msg_user_1",
  "ai_message_id": "msg_ai_1",
  "ai_response": "I can help you reset your password. Let me look up your account.",
  "deduplicated": false
}

Get Conversation History

History is fetched via query params — tenant_id, public_key, and the email_token from /session. Optional conversation_limit (1–20, default 5) and message_limit (1–100, default 40) control how much is returned. The Origin header must be allowed.

curl "https://apps.aurionai.net/api/v1/widget/history?tenant_id=11111111-2222-3333-4444-555555555555&public_key=vwk_xxxxxxxxxxxxxxxxxxxxxxxx&email_token=eyJ0aWQiOiI..." \
  -H "Origin: https://example.com"

Response (200 OK):

{
  "tenant_id": "11111111-2222-3333-4444-555555555555",
  "email": "jane@example.com",
  "conversations": [
    {
      "conversation_id": "conv_abc123",
      "thread_id": "thread_abc123",
      "subject": "Password reset",
      "status": "open",
      "updated_at": "2026-06-09T15:00:00Z",
      "messages": [
        {
          "id": "msg_user_1",
          "sender_type": "contact",
          "body": "I need help resetting my password",
          "created_at": "2026-06-09T14:59:00Z"
        }
      ]
    }
  ]
}

An invalid or expired email_token returns 401 {"detail": "Invalid or expired email token"}.

Upload an Attachment

Multipart form upload — send tenant_id and public_key as form fields alongside file, with an allowed Origin. Max 10 MB; the content type must start with image/, application/pdf, or text/.

curl -X POST "https://apps.aurionai.net/api/v1/widget/uploads" \
  -H "Origin: https://example.com" \
  -F "tenant_id=11111111-2222-3333-4444-555555555555" \
  -F "public_key=vwk_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -F "file=@screenshot.png"

Response (201 Created):

{
  "upload_id": "upl_abc123",
  "file_name": "screenshot.png",
  "content_type": "image/png",
  "size_bytes": 48213
}

Pass the returned upload_id in the upload_ids array on a subsequent /messages (or WebSocket) send to attach it.

WebSocket Connection

For real-time streaming, the widget connects via WebSocket. Auth is by tenant_id + public_key query params (both required) plus an allowed Origin — there is no token param. Optional source_thread_id and sender_email query params are also accepted.

wss://apps.aurionai.net/api/v1/widget/ws?tenant_id=11111111-2222-3333-4444-555555555555&public_key=vwk_xxxxxxxxxxxxxxxxxxxxxxxx

Missing tenant_id or public_key, or a disallowed origin, closes the socket with code 1008.

Send a message as a JSON frame with a body field (max length applies; optional source_thread_id, sender_email, source_message_id, upload_ids, metadata):

{
  "body": "I need help resetting my password"
}

The server streams a sequence of JSON frames in response: typing, ack, one or more chunk frames carrying the streamed AI reply, then done. Validation problems and rate limits arrive as error frames.

{
  "type": "chunk",
  "index": 0,
  "conversation_id": "conv_abc123",
  "thread_id": "thread_abc123",
  "chunk": "I can help you reset your password."
}
{
  "type": "error",
  "code": "validation_error",
  "message": "body is required"
}

Voice Token

There are two ways to obtain a LiveKit participant token for a voice session, depending on who is calling.

Authenticated path: POST /voice-widget/token

For an authenticated tenant member (dashboard session or an API key with config:write). It takes no request body — identity and room are derived from the authenticated user. Rate-limited to 5/minute.

curl -X POST "https://apps.aurionai.net/api/v1/voice-widget/token" \
  -H "X-API-Key: itsm_sk_live_xxxx"

Response:

{
  "token": "livekit_participant_token",
  "livekit_url": "wss://lk-staging.aurionai.net",
  "room_name": "room_abc123",
  "participant_identity": "user_jane",
  "pre_authenticated": true,
  "expires_in_seconds": 3600
}

The token field is livekit_url (the client WebSocket URL — the literal host is environment-dependent), and expires_in_seconds is 3600, so re-request after 60 minutes.

Public embed path: POST /voice-widget/embed/token

This is the path the embedded widget actually calls. It is public — authenticated by your widget public_key + an allowed Origin, not by an API key. Provide tenant_id and public_key; user_email is an optional, self-asserted hint (it only elevates pre_authenticated when the tenant has fully disabled voice auth and the email matches an existing requester).

curl -X POST "https://apps.aurionai.net/api/v1/voice-widget/embed/token" \
  -H "Content-Type: application/json" \
  -H "Origin: https://example.com" \
  -d '{
    "tenant_id": "11111111-2222-3333-4444-555555555555",
    "public_key": "vwk_xxxxxxxxxxxxxxxxxxxxxxxx"
  }'

The response is the same VoiceWidgetTokenResponse shape shown above (token, livekit_url, room_name, participant_identity, pre_authenticated, expires_in_seconds).

Public embed branding: POST /voice-widget/embed/config

Returns the widget's branding/behavior config for rendering. Same public auth model (tenant_id + public_key body, allowed Origin):

curl -X POST "https://apps.aurionai.net/api/v1/voice-widget/embed/config" \
  -H "Content-Type: application/json" \
  -H "Origin: https://example.com" \
  -d '{
    "tenant_id": "11111111-2222-3333-4444-555555555555",
    "public_key": "vwk_xxxxxxxxxxxxxxxxxxxxxxxx"
  }'

It returns the public-facing subset of the config (display_title, welcome_text, launcher_label, launcher_position, accent_theme, colors, logo_url, show_powered_by, mode, theme, plus the *_translations maps) — never the public_widget_key itself.

Origin Allowlist

For security, the widget only loads on origins you explicitly allow. Origins are full, normalized scheme://host values (e.g. https://example.com, no path or query), stored in allowed_origins and configured in the admin dashboard or via PUT /config. Requests whose Origin header is not allowed receive a 403 response.

First-party Aurion Help Center origins (under the configured Help Center domain suffix, including tenant subdomains) are auto-allowed and do not need an explicit allowed_origins entry.

Rate Limits

Per-endpoint limits on the widget surface:

EndpointLimit
POST /widget/session30/minute
POST /widget/messages60/minute
POST /widget/uploads30/minute
POST /voice-widget/token5/minute

API-key requests (e.g. the /voice-widget/config endpoints) are additionally subject to the global limit of 300 requests/minute per API key in both production and staging. A 429 response carries Retry-After, X-RateLimit-Limit, and X-RateLimit-Remaining headers and the body { "detail": "API key rate limit exceeded", "limit": 300 }.

Multimodal Support

The widget supports both voice and chat. The active surface is driven by the mode field on the embed config ("chat", "voice", or "both", default "both"). When both is configured, users can switch between voice and chat during a conversation and the AI agent maintains context across the switch.

Regenerate Widget Key

If your public widget key is compromised, regenerate it. This is a non-GET call under /api/v1/voice-widget, so an API key needs config:write:

curl -X POST "https://apps.aurionai.net/api/v1/voice-widget/config/regenerate-key" \
  -H "X-API-Key: itsm_sk_live_xxxx"

Response:

{
  "public_widget_key": "vwk_xxxxxxxxxxxxxxxxxxxxxxxx",
  "embed_snippet": "<script>(function(){window.AurionSettings={...};...})();</script>"
}

The old key stops working immediately — update your embed snippet with the new public_widget_key (or paste the returned embed_snippet).

GA Readiness Check

Check whether the voice widget is ready to embed for your tenant. This is a GET under /api/v1/voice-widget, so an API key needs config:read:

curl "https://apps.aurionai.net/api/v1/voice-widget/ga-readiness" \
  -H "X-API-Key: itsm_sk_live_xxxx"

Response:

{
  "ready": true,
  "failed_checks": [],
  "checks": {
    "allowed_origins_configured": { "passed": true, "reason": null, "details": {} }
  }
}

ready is false when any check fails; failed_checks lists the failing check keys.

Required Scopes

The /api/v1/voice-widget admin endpoints are gated by the config API-key scope (the path prefix maps to the config resource — there is no voice-widget:* scope). The public /widget/* and /voice-widget/embed/* endpoints use the widget public key + origin allowlist, not API-key scopes.

ScopeEndpoints
config:readGET /voice-widget/config, GET /voice-widget/ga-readiness
config:writePUT /voice-widget/config, POST /voice-widget/token, POST /voice-widget/config/regenerate-key
none (public key + Origin)POST /widget/session, POST /widget/messages, GET /widget/history, POST /widget/uploads, WS /widget/ws, POST /voice-widget/embed/token, POST /voice-widget/embed/config

On this page