@masheev/embed-sdk
Developer SDK access is beta. Start with an existing chat inbox from the dashboard. For guided implementation, use Integrate with AI. See authentication for signed customer identity and business tools for backend access.
Embeddable chat widget for any website. Available as a script tag, React hook, or headless API with full control over the UI.
Installation
pnpm add @masheev/embed-sdk
Or load via script tag (no build step):
<script src="https://unpkg.com/@masheev/embed-sdk"></script>
Entry Points
| Import path | Use case |
|---|---|
@masheev/embed-sdk/js | Vanilla JS — iframe widget with postMessage API |
@masheev/embed-sdk/react | React — hook wrapper around the iframe widget |
@masheev/embed-sdk/headless | React — direct WebSocket API, bring your own UI |
Quick Start
Script Tag
<script src="https://unpkg.com/@masheev/embed-sdk"></script>
<script>
Masheev.init({
inboxId: "your-inbox-id",
mode: "chat-widget",
position: "right",
agentName: "Support",
});
Masheev.on("message", function (msg) {
console.log(msg.role, msg.content);
});
</script>
React (iframe)
import { useEffect } from "react";
import { useMasheev } from "@masheev/embed-sdk/react";
function App() {
const { open, isReady, on } = useMasheev({
inboxId: "your-inbox-id",
mode: "chat-widget",
position: "right",
agentName: "Support",
});
useEffect(() => {
const unsub = on("message", ({ role, content }) => {
console.log(role, content);
});
return unsub;
}, [on]);
return (
<button onClick={open} disabled={!isReady}>
Chat with us
</button>
);
}
Headless (custom UI)
import { MasheevProvider, useMasheev } from "@masheev/embed-sdk/headless";
function Chat() {
const { messages, sendMessage, isLoading } = useMasheev();
return (
<div>
{messages.map((m) => (
<div key={m.id} className={m.role}>
{m.content}
{m.isLoading && <span>Typing...</span>}
</div>
))}
<input
onKeyDown={(e) => {
if (e.key === "Enter") {
sendMessage(e.currentTarget.value);
e.currentTarget.value = "";
}
}}
/>
</div>
);
}
function App() {
return (
<MasheevProvider
inboxId="your-inbox-id"
turnstileSiteKey="your-site-key"
customerInfo={{ name: "Jane", email: "jane@example.com" }}
>
<Chat />
</MasheevProvider>
);
}
JS SDK API
When loaded via script tag the global Masheev object exposes these methods. The same functions are available from @masheev/embed-sdk/js.
init(config)
Initialize the widget. Creates an iframe and attaches it to the page.
Masheev.init({
inboxId: "inbox_abc", // required
mode: "chat-widget", // "chat-widget" | "prompt-input" | "embedded"
position: "right", // "left" | "right"
baseUrl: "https://iframe.masheev.com",
placeholder: "Ask anything...",
agentName: "Support Bot",
agentTitle: "AI Assistant",
questions: ["How do I get started?", "Pricing?"],
privacyUrl: "https://example.com/privacy",
requireConsent: false, // true to show consent gate (regulated industries)
user: {
name: "Jane Doe",
email: "jane@example.com",
phone: "+1234567890",
userId: "usr_123",
company: "Acme Inc",
customAttributes: { plan: "pro" },
},
});
open() / close() / toggle()
Control widget visibility.
Masheev.open();
Masheev.close();
Masheev.toggle();
sendMessage(text)
Send a message programmatically.
Masheev.sendMessage("I need help with billing");
updateContext(context)
Update user context mid-conversation.
Masheev.updateContext({ name: "Jane Doe", email: "jane@acme.com" });
setQuestions(questions)
Replace the list of suggested questions.
Masheev.setQuestions(["How does pricing work?", "Do you offer a free trial?"]);
on(event, callback) / off(event, callback)
Subscribe to widget events. on() returns an unsubscribe function.
const unsub = Masheev.on("message", (msg) => {
console.log(msg.role, msg.content);
});
// later
unsub();
isReady()
Returns true once the iframe has loaded and is accepting commands.
destroy()
Remove the iframe and clean up all listeners.
version
SDK version string (matches the installed package version).
Configuration
MasheevConfig
| Property | Type | Default | Description |
|---|---|---|---|
inboxId | string | — | Required. Inbox to connect to. |
mode | WidgetMode | "chat-widget" | Display mode (see below). |
position | "left" | "right" | "right" | Bubble position (chat-widget mode only). |
colorScheme | "light" | "dark" | "auto" | "light" | Light or dark appearance. "auto" follows the host page (.dark / .light class or data-theme), then the OS setting. Change it after init with updateColorScheme(). |
theme | WidgetTheme | — | Appearance overrides (see WidgetTheme). Ignored when the inbox has a saved theme. |
baseUrl | string | "https://iframe.masheev.com" | Widget iframe host. |
placeholder | string | — | Input placeholder text. |
agentName | string | — | Agent display name in the header. |
agentTitle | string | — | Agent role / title. |
questions | string[] | — | Suggested questions shown to the user. |
privacyUrl | string | — | Your privacy policy. Overrides the URL in the inbox settings. With neither, the widget names your business instead and never links to Masheev's policy. |
requireConsent | boolean | false | Show a consent gate before any chat session starts, in every mode (see AI Disclosure & Consent). |
hideHeader | boolean | false | Hide the chat header (embedded mode only). Useful when you provide your own header. |
containerId | string | — | DOM element ID to mount the iframe into (default: document.body). Useful for embedded mode. |
user | UserContext | — | Pre-fill user identity (see below). |
tools | ClientToolDefinition[] | — | Client-side tools the AI can invoke (see Agent Actions). |
instructions | string | — | Instructions for the AI explaining how to use registered tools. |
WidgetTheme
Every field is optional. Fields you leave out come from the template preset.
| Property | Type | Default | Description |
|---|---|---|---|
template | "minimal" | "modern" | "corporate" | "playful" | "dark" | "custom" | "modern" | Preset palette. The fields below override individual values on top of it. |
primaryColor | string | from template | Send button, focus ring, unread indicator, selected chips. |
backgroundColor | string | from template | Panel background. The widget derives its surfaces and hairlines from this colour. |
textColor | string | from template | Body text. Adjusted automatically if it does not reach 4.5:1 against the background. |
userBubbleColor | string | primaryColor | Background of the visitor's own messages. |
userBubbleTextColor | string | derived | Text inside the visitor's messages. Contrast is enforced the same way as textColor. |
fontFamily | string | system stack | Font stack for the whole widget. See the caveat below. |
fontSize | "sm" | "base" | "lg" | "base" | Body text size. Every other size in the widget scales from it. |
messageStyle | "plain" | "bubble" | "plain" | "plain" renders AI replies as text on the panel surface. "bubble" keeps them inside a bubble. |
logoUrl | string | — | Image shown in the header instead of the default agent avatar. |
headerTitle | string | agent name | Header title text. |
poweredBy | boolean | true | Show the "Powered by Masheev" line in the footer. |
Masheev.init({
inboxId: "your-inbox-id",
colorScheme: "auto",
theme: {
template: "minimal",
primaryColor: "#2563eb",
userBubbleColor: "#2563eb",
fontSize: "base",
messageStyle: "plain",
},
});
Precedence. The theme saved on the inbox in the Masheev dashboard wins. The theme
you pass here applies only when the inbox has no saved theme, so a customer's own
appearance settings are never overridden by embed code.
Dark mode. Each template ships a light and a dark preset. When the resolved scheme is
dark the widget uses the template's dark preset. Colours you set explicitly are used as
given in both schemes — they are not recoloured for dark mode. Set the colours you want
for dark yourself, or use colorScheme: "light" to pin the widget to one scheme.
fontFamily caveat. The widget runs in an iframe that does not load web fonts. Only
fonts already installed on the visitor's device and the generic families (sans-serif,
serif, monospace) render. Anything else falls back to the system stack. The value is
sanitized before use, so url(), braces and semicolons are stripped.
Appearance editor. The same fields are editable per inbox in the dashboard under the inbox's appearance settings, with a live preview of the widget in light and dark.
UserContext
| Property | Type | Description |
|---|---|---|
name | string | User name |
email | string | User email |
phone | string | User phone |
userId | string | Your external user ID |
company | string | Company name |
customAttributes | Record<string, string | number | boolean> | Arbitrary metadata |
Widget Modes
The SDK creates and owns the widget's <iframe>. It sizes the frame itself: small while
closed, large while open, and again whenever the widget posts a resize message (for
example when the on-screen keyboard appears). The host page sets no dimensions and should
not style the frame — apart from embedded mode, where the container you provide defines
the available space.
chat-widget (default)
A floating launcher in the corner of the page. Tapping it opens a chat panel above the
launcher; on small screens the panel fills the viewport. The position prop puts the
launcher on the left or the right.
prompt-input
A single input pill docked at the bottom of the page, no launcher. Focusing it reveals suggested questions; sending a message opens a transcript panel above the pill, and the pill stays as the composer. Good for AI-first pages where chat is the primary action.
embedded
Fills 100% of its parent container. Use this when you want to place the widget inside a specific element rather than floating it over the page. The parent must have defined dimensions.
Mobile keyboard: in embedded mode the host owns the container's size, so the on-screen keyboard does not automatically shrink the widget. If you embed it full-screen on mobile, size the container against the visual viewport (e.g. height: 100dvh or a window.visualViewport listener) so the composer stays above the keyboard, or use chat-widget / prompt-input mode, which dock above the keyboard automatically.
AI Disclosure & Consent
Masheev tells end users that they are talking to an AI assistant. This helps you meet AI-disclosure rules such as Article 50 of the EU AI Act and US state chatbot laws, but you remain responsible for any other notice your use requires.
How Disclosure Works
When an AI agent is configured on an inbox, the server builds the AI identity notice for each channel:
- Chat widget — disclosure is shown as a note above the conversation, before the greeting (screen readers announce it first), and every widget footer starts with "AI-powered ·". AI-written messages carry
data-ai-generated="true"in the page. The greeting message itself stays conversational. - Headless SDK / API —
disclosureis returned with every session when an AI agent is configured; show it before the conversation. Unless the session request sendsdisclosureRendered: true(only do this if your UI shows the disclosure), Masheev also adds it to the start of the first AI reply. - SMS: every AI reply begins with the disclosure.
- WhatsApp and Instagram: the first AI reply in each conversation begins with the disclosure.
- Email: every AI reply ends with a disclosure footer.
- Voice: the call opens with the disclosure and a transcription notice, before your greeting.
The notice names the AI agent and your organization. You can add your own text after it per channel in the AI Agent settings (Dashboard > AI Agents > Disclosure tab). You cannot remove or replace it.
Your added text supports the {{agentName}} and {{orgName}} variables.
Consent Gate
For regulated industries that require explicit data-processing consent before a conversation, set requireConsent: true:
Masheev.init({
inboxId: "inbox_abc",
requireConsent: true,
privacyUrl: "https://example.com/privacy",
});
When enabled, the widget shows a consent gate before the chat UI, in every mode. The user must check a consent checkbox and click "Start Chat" to proceed. No session is created and no visitor identifier is stored until then.
Consent is stored only on Masheev's servers, on the contact record (for GDPR Art. 7(1) proof): the contact's consent.dataProcessing and consent.dataProcessingAt fields. The browser stores no consent flag. A returning visitor whose browser still holds its visitor identifier is not asked again while the consent wording is unchanged. See Privacy and cookies for everything the widget stores and when.
Session Endpoint (consent field)
When the widget sends consent data, it is included in the session creation request:
{
"inboxId": "string",
"consent": { "timestamp": "2025-01-15T10:30:00.000Z", "version": "ai-conversations-v1" }
}
The server merges the consent into the contact's existing consent record without overwriting other consent fields.
Agent Actions
Agent Actions let the AI call functions in your app during a conversation. Register tools with the SDK, and the AI agent can invoke them to perform actions like creating accounts, navigating pages, or fetching app state — with results flowing back to the AI for multi-step workflows.
Script Tag (JSON Schema)
<script src="https://unpkg.com/@masheev/embed-sdk"></script>
<script>
Masheev.init({
inboxId: "inbox_abc",
tools: [
{
name: "register_org",
description: "Register a new organization account",
parameters: {
type: "object",
properties: {
name: { type: "string", description: "Organization name" },
email: { type: "string", description: "Admin email" },
plan: { type: "string", enum: ["free", "pro", "enterprise"] },
},
required: ["name", "email"],
},
needsApproval: true,
execute: async function (args, options) {
options.onProgress("Creating organization...");
var org = await myApp.createOrg(args);
return { success: true, data: { orgId: org.id } };
},
},
],
instructions:
"You can help users register organizations. Ask for the org name, email, and plan, then use register_org to create it.",
});
</script>
React / Headless (Zod schemas)
The headless SDK supports Zod schemas for full TypeScript inference on tool arguments:
import { MasheevProvider, useMasheev } from "@masheev/embed-sdk/headless";
import { clientTool } from "@masheev/embed-sdk/headless";
import { z } from "zod";
const registerOrg = clientTool({
name: "register_org",
description: "Register a new organization account",
parameters: z.object({
name: z.string().describe("Organization name"),
email: z.string().email().describe("Admin email"),
plan: z.enum(["free", "pro", "enterprise"]).describe("Billing plan"),
}),
needsApproval: true,
execute: async (args, { onProgress }) => {
// args is fully typed: { name: string; email: string; plan: "free" | "pro" | "enterprise" }
onProgress("Creating organization...");
const org = await createOrg(args);
return { success: true, data: { orgId: org.id } };
},
});
function App() {
return (
<MasheevProvider
inboxId="inbox_abc"
tools={[registerOrg]}
instructions="You can help users register organizations."
>
<Chat />
</MasheevProvider>
);
}
Zod v4+ is required as a peer dependency when using Zod tool definitions. The SDK uses Zod's native toJSONSchema() for schema conversion — no additional libraries needed.
Tool Definition
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tool name (alphanumeric + underscores, max 64 chars). |
description | string | Yes | What the tool does (max 500 chars). Helps the AI decide when to use it. |
parameters | object | Yes | JSON Schema object describing the input, or a Zod schema (headless only). |
needsApproval | boolean | No | When true, the AI asks the user for confirmation before executing. |
execute | function | Yes | Async function called when the AI invokes the tool. Receives (args, options). |
Tool Result
The execute function must return a ClientToolResult:
interface ClientToolResult {
success: boolean;
data?: Record<string, unknown>; // Structured data for the AI
error?: string; // Human-readable error message
display?: "text" | "card" | "silent"; // UI hint (default: "text")
}
Progress Reporting
The execute function receives an onProgress callback to report status during long-running operations:
execute: async (args, { onProgress }) => {
onProgress("Validating input...");
await validate(args);
onProgress("Creating account...");
const result = await createAccount(args);
return { success: true, data: result };
};
Progress messages are shown as typing indicators in the chat.
Dynamic Tool Updates
Add or remove tools during a session with updateTools():
// JS SDK
Masheev.updateTools([/* new tool definitions */]);
// React hook
const { updateTools } = useMasheev({ inboxId: "..." });
updateTools([/* new tool definitions */]);
// Headless
const { updateTools } = useMasheev();
await updateTools([/* new tool definitions */]);
Instructions
The instructions field tells the AI how and when to use your tools. It is appended to the AI agent's system prompt:
Masheev.init({
inboxId: "inbox_abc",
tools: [addToCartTool, getInventoryTool],
instructions: `You are a shopping assistant. When the user asks about product availability,
use get_inventory first. When they want to buy something, use add_to_cart.
Always confirm the item and quantity before adding to cart.`,
});
Error Handling
| Scenario | What happens |
|---|---|
| Tool throws an error | SDK catches it and sends { success: false, error: "..." } back to the AI |
Tool times out (iframe: 60s default, per-tool timeout; headless: 25s) | SDK sends a timeout error; AI responds gracefully |
| Unknown tool name | SDK sends an error result immediately |
| Network failure posting result | SDK retries once after 2s; server has a 30s fallback timeout |
All errors are sent back to the AI as tool results, so it can self-correct or respond to the user.
Limits
| Limit | Value |
|---|---|
| Max tools per session | 10 |
| Tool name length | 64 characters |
| Description length | 500 characters |
| Max parameters per tool | 20 |
| Result data size | 50 KB (truncated if larger) |
| Error message length | 1000 characters |
| Tool result rate limit | 30/min per session |
| Tool update rate limit | 5/min per session |
Security
- Tool definitions are validated server-side (name pattern, limits, schema structure)
- Tool results are sanitized (data truncated at 50 KB, error at 1000 chars)
instructionsare placed after guardrails in the system prompt hierarchy — they cannot override the agent's identity or security rules- Tool results are treated as data, not instructions — prompt injection defenses apply
- No PII is logged server-side (only tool name + invocation ID)
Workflows
Guide AI conversations through structured, multi-step flows. Add a workflow config to init() or useMasheev().
Masheev.init({
inboxId: "your-inbox-id",
workflow: {
id: "onboarding",
name: "New User Onboarding",
steps: [
{ id: "welcome", name: "Welcome", instructions: "Greet the user." },
{ id: "setup", name: "Setup", instructions: "Help configure their workspace." },
],
context: { userName: "Alex" },
},
});
Workflows provide step-by-step AI guidance, per-step tool filtering, analytics funnels, and context interpolation ({{context.KEY}}).
See the full Workflows guide for defineWorkflow(), useWorkflow(), events, analytics dashboard, and security details.
Events
| Event | Payload | Fires when |
|---|---|---|
ready | — | Widget iframe has loaded |
open | — | Widget panel is opened |
close | — | Widget panel is closed |
message | { role: "user" | "ai", content: string } | A message is sent or received |
error | { message: string, code?: string } | An error occurs |
action:invoke | { invocationId, toolName, args } | AI invokes a client tool |
action:result | { invocationId, result } | Tool execution completes |
action:progress | { invocationId, status } | Tool reports progress |
All event subscriptions are type-safe:
Masheev.on("message", (payload) => {
// payload is typed as { role: "user" | "ai"; content: string }
});
React Hook API
useMasheev(config)
import { useMasheev } from "@masheev/embed-sdk/react";
const {
open, // () => void
close, // () => void
toggle, // () => void
sendMessage, // (text: string) => void
on, // (event, callback) => () => void (returns unsubscribe)
off, // (event, callback) => void
updateTools, // (tools: ClientToolDefinition[]) => void
isReady, // boolean
} = useMasheev({ inboxId: "..." });
The hook creates the SDK on mount and destroys it on unmount. It re-initialises when inboxId or baseUrl change.
Headless API
The headless entry point gives you direct access to the conversation over WebSocket. No iframe is created — you build the UI yourself.
<MasheevProvider>
Wrap your chat UI with the provider.
| Prop | Type | Default | Description |
|---|---|---|---|
inboxId | string | — | Required. Inbox to connect to. |
apiBase | string | "https://api.masheev.com" | API base URL. |
turnstileSiteKey | string | — | Cloudflare Turnstile site key for bot protection. |
customerInfo | { name?, email?, phone? } | — | Pre-fill customer identity. |
tools | ClientToolDefinition[] | — | Client-side tools the AI can invoke (see Agent Actions). |
instructions | string | — | Instructions for the AI on how to use tools. |
storage | "persistent" | "session" | "none" | "persistent" | Where the visitor identity is kept: localStorage, sessionStorage, or nowhere. Nothing is stored before the first session. |
requireConsent | boolean | false | Start no session until you call giveConsent(). |
useMasheev() (headless)
Must be called inside <MasheevProvider>.
const {
// Session
session, // WidgetSession | null
isConnected, // boolean
connectionStatus, // "connecting" | "connected" | "disconnected" | "error" | "gave_up"
error, // string | null
// Messages
messages, // Message[]
sendMessage, // (content: string) => Promise<void>
isLoading, // boolean
// UI state
isOpen, // boolean
setOpen, // (open: boolean) => void
// Tools
updateTools, // (tools: ClientToolDefinition[]) => Promise<void>
// Manual session creation
createSession, // () => Promise<WidgetSession | null>
// Consent (with requireConsent)
hasConsent, // boolean
giveConsent, // () => void, call from your own consent UI
} = useMasheev();
Lower-level hooks
| Hook | Returns | Use case |
|---|---|---|
useWidgetChat() | { messages, sendMessage, isLoading, error } | Chat operations only |
useWidgetSession() | { session, error, isLoading, createSession } | Session management |
useWidgetSocket() | { status } | Connection indicator |
Message
interface Message {
id: string;
role: "user" | "ai";
content: string;
timestamp: Date;
isLoading?: boolean; // true while the AI is streaming
}
WidgetSession
interface WidgetSession {
sessionToken: string;
conversationId: string;
greeting: string;
disclosure?: string; // AI disclosure: show it before the conversation
}
How It Works
Iframe SDK (JS / React)
init()creates a hidden<iframe>pointing tohttps://iframe.masheev.com/widget/{inboxId}.- Communication happens via
window.postMessagewith strict origin validation. - Commands issued before the iframe is ready are queued and flushed on the
readyevent. - The SDK owns the iframe's size. It resizes the frame when the widget opens or closes and whenever the widget posts a
resizemessage.
Headless SDK
- A session is created lazily on the first
sendMessage()call (or explicitly viacreateSession()). The visitor identity is stored only after that, as set bystorage(see Privacy and cookies). - The provider opens a WebSocket to
wss://api.masheev.com/ws/conversation/{conversationId}. - Messages from the AI arrive in real-time over the socket.
- Reconnection uses exponential backoff (1 s initial, 30 s max, 10 attempts, 30% jitter).
- A ping is sent every 25 s to keep the connection alive.
Bot Protection
When turnstileSiteKey is provided (headless only), the SDK embeds an invisible Cloudflare Turnstile challenge when a session is first requested, not on page load. The token is sent with the session-creation request and validated server-side.
Turnstile
In production, POST /api/widget/session requires a Cloudflare Turnstile token. A request without one is rejected with 403 Verification required, so the widget waits for the invisible challenge to resolve before it creates a session, and never sends an unverified request.
The challenge is served from https://challenges.cloudflare.com. If your site sends a Content Security Policy, allow that origin in both directives:
Content-Security-Policy: script-src https://challenges.cloudflare.com; frame-src https://challenges.cloudflare.com
If the challenge cannot load — blocked by CSP, blocked by a privacy extension, or a missing site key — the widget treats it as a startup failure rather than a message failure. Visitors see nothing at all while it retries in the background; on a development host, or with debug: true / ?masheev_debug=1, you get a card naming the cause.
REST Endpoints (headless internals)
These are called internally by the headless provider. You only need them if you are building a non-React integration from scratch.
POST /api/widget/session
Create a chat session.
Request:
{
"inboxId": "string",
"visitorId": "string",
"visitorToken": "string",
"customerInfo": { "name": "string", "email": "string", "phone": "string" },
"turnstileToken": "string",
"consent": { "timestamp": "ISO-8601 string", "version": "ai-conversations-v1" },
"disclosureRendered": false,
"clientTools": [{ "name": "string", "description": "string", "parameters": {} }],
"instructions": "string"
}
visitorId— optional. Send only the identifier a previous session returned, together with itsvisitorToken; the server issues a new one otherwise.consent— optional. RecordsdataProcessing: trueand the timestamp on the contact record for GDPR Art. 7(1) compliance.disclosureRendered— optional. Sendtrueonly if your UI showsdisclosurebefore the conversation. Otherwise the disclosure is added to the start of the first AI reply.clientTools— optional. Registers client-side tool definitions for the session (see Agent Actions).instructions— optional. Instructions for the AI on how to use the registered tools.
Response:
{
"sessionToken": "string",
"conversationId": "string",
"greeting": "string",
"disclosure": "string",
"orgName": "string",
"privacyPolicyUrl": "string",
"isResumed": false
}
greeting— the agent's conversational greeting (e.g. "Hello! How can I help you today?").nullfor resumed sessions.disclosure— the AI disclosure to show before the chat (e.g. "You're chatting with Alex, an AI assistant for Acme Corp."). Returned whenever an AI agent is configured, resumed sessions included.orgName,privacyPolicyUrl— the business name and its privacy policy from the inbox settings, for your privacy link.privacyPolicyUrlis absent if none is set.isResumed—truewhen continuing an existing conversation.
POST /api/widget/message
Send a message. Requires Authorization: Bearer {sessionToken}.
Request:
{ "content": "string" }
Or trigger an AI-first turn (workflow mode — AI speaks first with no user message):
{ "trigger": true }
Response:
{ "status": "processing" }
Content is delivered via WebSocket streaming, not in the HTTP response.
Session refresh: When the session token is close to expiring (<2 hours remaining), the response includes an X-Session-Token header with a refreshed token. Clients should check for this header and update their stored token.
POST /api/widget/tool-result
Submit the result of a client-side tool invocation. Requires Authorization: Bearer {sessionToken}.
Request:
{
"invocationId": "string",
"result": { "success": true, "data": {} }
}
Response:
{ "success": true }
POST /api/widget/update-tools
Update tool definitions mid-session. Requires Authorization: Bearer {sessionToken}.
Request:
{
"clientTools": [{ "name": "string", "description": "string", "parameters": {} }],
"instructions": "string"
}
Response:
{ "success": true }
WebSocket /ws/conversation/{conversationId}
Query params: ?token={sessionToken}&source=widget
Receives JSON frames:
{
"type": "message.new",
"payload": {
"messageId": "string",
"content": "string",
"sender": { "type": "ai" }
}
}
When Agent Actions are registered, the server also sends tool invocation events:
{
"type": "client_tool.invoke",
"payload": {
"invocationId": "string",
"toolName": "string",
"args": {}
}
}
Constants
import { SDK_VERSION, DEFAULT_API_BASE, DEFAULT_WIDGET_BASE } from "@masheev/embed-sdk/js";
SDK_VERSION; // installed package version, e.g. "0.5.1"
DEFAULT_API_BASE; // "https://api.masheev.com"
DEFAULT_WIDGET_BASE; // "https://iframe.masheev.com"