Appearance
Embed Widget API Reference
REST API Endpoints
Create Embed Token
Create a new embed token for your widget.
POST /embed/createAuthentication: Required (Bearer token)
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
agentId | string | At least one | Text chat agent ID |
voiceAgentId | string | At least one | Voice/bidi agent ID |
allowedDomains | string[] | No | Domain allowlist (default: ["localhost"]) |
mode | string | No | "text", "voice", or "both" (auto-detected from agents) |
greeting | string | No | Greeting message |
color | string | No | Brand color hex (default: "#6366f1") |
position | string | No | "bottom-right" or "bottom-left" |
agentName | string | No | Display name in widget header |
title | string | No | Header title (overrides agentName display) |
subtitle | string | No | Secondary text under the title (e.g., "Powered by AI") |
logoUrl | string | No | URL of circular avatar image in header |
initialPrompt | string | No | Auto-sent to agent on first load — agent's reply becomes the opening message |
placeholder | string | No | Input box placeholder text (default: "Ask me anything...") |
size | string | No | Widget size: "compact" (340×480), "standard" (400×580), "large" (440×640) |
rateLimitPerDay | number | No | Max requests per day (default: 1000) |
rateLimitPerIp | number | No | Max requests per IP (default: 10) |
minutesLimit | number | No | Voice minutes limit (null = unlimited) |
creditLimit | number | No | Credit usage limit (null = unlimited) |
TIP
You must provide at least one of agentId or voiceAgentId. Providing both enables the dual-mode widget with text/voice toggle tabs.
Response:
json
{
"data": {
"embedTokenId": "emb-a1b2c3d4-...",
"embedToken": "emb_pk_live_...",
"agentId": "text-agent-uuid",
"voiceAgentId": "voice-agent-uuid",
"allowedDomains": ["yourdomain.com", "localhost"],
"config": {
"greeting": "Hi! How can I help?",
"color": "#6366f1",
"position": "bottom-right",
"mode": "both",
"agentName": "AI Assistant"
},
"snippet": "<script src=\"https://cdn.universalapi.co/embed/widget.js\"\n data-text-agent=\"text-agent-uuid\"\n data-voice-agent=\"voice-agent-uuid\"\n data-token=\"emb_pk_live_...\"\n async></script>",
"status": "active"
}
}Get Widget Config
Fetch widget configuration for a given embed token. This is a public endpoint — no user auth required. Validates the token and Origin header.
GET /embed/config?token=emb_pk_live_...Response:
json
{
"data": {
"agentId": "text-agent-uuid",
"voiceAgentId": "voice-agent-uuid",
"agentName": "AI Assistant",
"greeting": "Hi! How can I help?",
"color": "#6366f1",
"position": "bottom-right",
"mode": "both"
}
}List Embed Tokens
List all embed tokens for the authenticated user.
GET /embed/listAuthentication: Required
Response:
json
{
"data": {
"tokens": [
{
"embedTokenId": "emb-a1b2c3d4-...",
"tokenPrefix": "emb_pk_live_",
"agentId": "text-agent-uuid",
"voiceAgentId": "voice-agent-uuid",
"allowedDomains": ["yourdomain.com"],
"config": { ... },
"minutesUsed": 12.5,
"minutesLimit": null,
"status": "active",
"createdAt": 1714000000
}
],
"count": 1
}
}Update Embed Token
Update settings for an existing embed token.
PUT /embed/{embedTokenId}Authentication: Required
Request Body (all fields optional):
| Field | Type | Description |
|---|---|---|
allowedDomains | string[] | New domain allowlist |
rateLimitPerDay | number | New daily rate limit |
rateLimitPerIp | number | New per-IP rate limit |
config | object | Updated widget config (greeting, color, mode, etc.) |
status | string | "active" or "revoked" |
Delete (Revoke) Embed Token
Soft-delete an embed token. The widget stops working immediately.
DELETE /embed/{embedTokenId}Authentication: Required
Embed Chat (Streaming Credentials)
Get streaming credentials for text chat. The widget calls this internally.
POST /embed/chatRequest Body:
json
{
"token": "emb_pk_live_..."
}Response:
json
{
"data": {
"agentId": "text-agent-uuid",
"bearerToken": "uapi_ut_...",
"streamUrl": "https://stream.api.universalapi.co/agent/{agentId}/chat"
}
}The widget uses these credentials to stream directly from the agent API.
Script Tag Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
data-text-agent | string | At least one | Text chat agent ID |
data-voice-agent | string | At least one | Voice/bidi agent ID |
data-token | string | Yes | Embed token (emb_pk_live_...) |
data-mode | string | No | Override mode: "text", "voice", "both" |
data-position | string | No | "bottom-right" (default) or "bottom-left" |
data-color | string | No | Brand color hex |
data-greeting | string | No | Greeting text (shown as static first bubble in text mode) |
data-title | string | No | Header title (overrides agent name) |
data-subtitle | string | No | Secondary text under the title |
data-logo | string | No | URL of circular avatar image in header |
data-initial-prompt | string | No | Auto-sent to agent on load — response is opening message |
data-placeholder | string | No | Input box placeholder text |
data-size | string | No | "compact", "standard" (default), "large" |
data-default-tab | string | No | Which tab to show first in "both" mode: "text" (default) or "voice" |
JavaScript API (window.UniversalAPIWidget)
Methods
| Method | Arguments | Description |
|---|---|---|
open() | — | Open the widget panel |
close() | — | Close the widget panel |
switchTo(tab) | "text" or "voice" | Switch active tab (only in "both" mode) |
on(event, callback) | event name, function | Register event listener |
getMode() | — | Returns "text", "voice", or "both" |
getActiveTab() | — | Returns "text" or "voice" |
isReady() | — | Returns true after widget has initialized |
Properties
| Property | Type | Description |
|---|---|---|
version | string | Widget version (e.g., "3.0.0") |
Events
| Event | Data | Description |
|---|---|---|
ready | — | Widget has loaded and is ready |
open | — | Panel was opened |
close | — | Panel was closed |
voiceStart | — | Voice WebSocket connected, mic active |
voiceEnd | — | Voice session ended |
transcript | { text, role, is_final } | Voice transcript received |