> For the complete documentation index, see [llms.txt](https://shoppad.gitbook.io/yedric/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://shoppad.gitbook.io/yedric/developers/javascript-api.md).

# JavaScript API

Once `widget.js` loads, two entry points are available:

1. **`window.Yedric`**: global object, works with any widget instance on the page.
2. **The `<yedric-widget>` element itself**: useful when you want to target a specific instance or call `setConfig` / `getConfig`.

## `window.Yedric`

```ts
interface YedricGlobal {
  open(prompt?: string, persist?: boolean): void;
  close(persist?: boolean): void;
  toggle(state?: 'open' | 'closed'): void;
  getSessionId(): string | null;
  refresh(): void;
  on(eventName: string, callback: (eventName: string, data: unknown) => void): void;
  off(eventName: string, callback: (eventName: string, data: unknown) => void): void;
  setPreviewPageUrl(url: string | null | undefined): void;
  addContext(context: Record<string, unknown>): void;
  attachResource(uri: string, params?: Record<string, unknown>): void;
  registerClientTool(
    toolName: string,
    handler: (ctx: { toolCallId: string; toolName: string; args: unknown }) => Promise<unknown> | unknown,
  ): void;
  unregisterClientTool(toolName: string): void;
  event(name: string, payload?: Record<string, unknown>, opts?: { dedupeKey?: string }): void;
  eventStats(): ClientEventStats;
}
```

### `Yedric.open(prompt?, persist?)`

Open the panel. If `prompt` is provided, it's sent as a user message after the panel opens.

| `prompt` | `persist`         | Result                                                                        |
| -------- | ----------------- | ----------------------------------------------------------------------------- |
| omitted  | any               | Open the panel. Existing thread preserved. No message sent.                   |
| `string` | `false` (default) | Clear the thread, open the panel, send `prompt` as a new chat.                |
| `string` | `true`            | Open the panel, keep existing messages visible, send `prompt` as a follow-up. |

### `Yedric.close(persist?)`

Minimize the panel to the launcher (or, if **Hide beacon** is enabled in **Widget → Configure**, just close it).

Pass `persist: true` to keep the current conversation, so the next open resumes the existing thread instead of showing a fresh prompt screen. Omitted or `false`, a later `Yedric.open()` with no arguments starts a new chat.

```js
window.Yedric.close(true);  // keep the thread
window.Yedric.open();       // …and pick it back up
```

### `Yedric.toggle(state?)`

Flip the panel open/closed. Pass `'open'` or `'closed'` to force a specific state.

### `Yedric.getSessionId()`

Returns the active conversation's session id: the secure session id in [secure mode](/yedric/developers/secure-mode.md), otherwise the anonymous browser session id, or `null` before a session exists. Use it to hand the active conversation off to another widget instance (see [Secure Mode → Carrying a conversation across widget instances](/yedric/developers/secure-mode.md#carrying-a-conversation-across-widget-instances)).

### `Yedric.refresh()`

Re-fetch the active secure thread's messages from the server and replay them into the panel. Useful when another widget instance (e.g. a separate iframe on the same page) appended to the same thread and you want this instance to catch up. No-op outside secure mode or when there is no active session.

```js
// after a fullscreen editor (its own widget instance) closes:
window.Yedric.refresh();
```

### `Yedric.on(eventName, callback)`

Register a listener for widget events. See [Events](#events) below.

```js
window.Yedric.on('message_update', (eventName, data) => {
  console.log(data.content); // assistant message so far
});
```

The callback signature is `(eventName, data)`, so the event name is repeated as the first argument for convenience. Listeners are additive.

### `Yedric.off(eventName, callback)`

Remove a listener previously registered with `Yedric.on`. Pass the same event name and the same callback reference you registered.

```js
function onOpen(name, data) { /* … */ }
window.Yedric.on('open', onOpen);
// …later
window.Yedric.off('open', onOpen);
```

### `Yedric.setPreviewPageUrl(url)`

Override `window.location` for page prompt matchUrl/ignoreUrl matching, suggestion `{URL}`/`{{var}}` templating, and the `siteUrl` field on `POST /api/chat`. Pass `null`, `undefined`, or `''` to clear the override.

```js
window.Yedric.setPreviewPageUrl('https://example.com/checkout');
// …later
window.Yedric.setPreviewPageUrl(null); // back to window.location
```

### `Yedric.addContext(context)`

Inject extra key/value context into the next chat request. The context object is merged with any existing context and sent as the `context` field on `POST /api/chat`. The assistant's backend can surface these values in the system prompt via variable expansion.

```js
window.Yedric.addContext({
  currentPlan: 'pro',
  shopDomain: 'example-store.myshopify.com',
  cartItemCount: 3,
});
```

Context is **additive**, so each call merges into the existing map. Pass an empty object `{}` to replace without clearing (context persists across messages until the page reloads). Context is not cleared when the thread is reset via `Yedric.open(prompt)`. It persists until the page reloads or you overwrite the keys via another `addContext()` call. Use this to pass page-level state the assistant should know about without including it in the user's message.

### `Yedric.attachResource(uri, params?)`

Attach an MCP resource to the current conversation by its URI. The backend loads the resource's content into the assistant's system context for the rest of the chat, so the assistant can reference it without a tool call. Optional `params` are merged into the widget context and used to substitute `{{var}}` placeholders in the resource's path.

```js
window.Yedric.attachResource('mesa://automations/12123', { _id: 123 });
```

Use this to give the assistant the specific record the user is looking at (the automation, order, or document on screen) the moment the panel opens. When the resource loads, a [`resource_response`](#events) event fires.

### `Yedric.registerClientTool(toolName, handler)`

Run a custom HTTP tool in the host page instead of having the server call it. See [Advanced Configuration → Client-side tool execution](/yedric/developers/advanced-configuration.md#client-side-tool-execution) for the full flow.

```js
window.Yedric.registerClientTool('Contact_Support', ({ toolCallId, toolName, args }) => {
  window.HostApp.openSupportModal();
  return 'Support modal opened.';
});
```

### `Yedric.unregisterClientTool(toolName)`

Remove a previously registered handler.

### `Yedric.event(name, payload?, opts?)`

Report an app event to the assistant, fire and forget. This is the host-to-assistant direction; it is unrelated to `Yedric.on`, which observes widget events in the other direction. See [Client Events](/yedric/developers/client-events.md) for the full pipeline: gating, batching, the per-agent event definitions, and how the assistant reacts.

```js
window.Yedric.event('test_run_halt', {
  automation_id: '68a1f0…',
  run_id: '68b2c1…',
  state: 'paused',
});
```

Semantics the host can rely on:

* Dropped unless the panel is open and the current conversation has at least one user message. An event never opens the panel and never creates a conversation.
* Events are batched for about 2 seconds and coalesced: a newer event with the same `opts.dedupeKey` replaces the queued one. The default key is `` `${name}:${payload.automation_id ?? ''}` ``, so repeat events for one entity keep only the latest state.
* The event name must be defined on the agent (`plugins/{server}/events/*.json`); unknown names are dropped server-side.
* May produce no visible reply: the server can decide the events are background context or noise.
* Payloads must contain only identifiers and enums, never customer data. They reach the model, the conversation store, and the browser console.

### `Yedric.eventStats()`

Returns page-lifetime counters for the `Yedric.event` delivery funnel. Pairs with the `[Yedric events]` console lines for debugging; see [Client Events → Debugging](/yedric/developers/client-events.md#debugging).

```ts
interface ClientEventStats {
  enqueued: number;   // events accepted into the queue
  coalesced: number;  // queued events replaced by a newer same-key event
  dropped: {
    notInitialized: number;
    panelClosed: number;
    noConversation: number;
    gatesAtFlush: number;
    queueOverflow: number;
    nonRetryableStatus: number;
    retriesExhausted: number;
  };
  flushes: { attempted: number; retried: number };
  verdicts: { respond: number; context: number; ignore: number; ignoreReasons: Record<string, number> };
  turns: { started: number; handoffRejected: number; gaveUp: number };
}
```

Event-count fields (`enqueued`, `dropped.*`) count individual events; `flushes`, `verdicts`, and `turns` count batches.

## Events

All events are emitted via `Yedric.on(eventName, callback)`.

| Event               | When it fires                                                                                                                                      | Data shape                                                                                                                                                                                                     |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `open`              | Panel opened (by user, by `Yedric.open()`, or on initial render when **Default state** is Open).                                                   | `{}`                                                                                                                                                                                                           |
| `close`             | Panel closed.                                                                                                                                      | `{}`                                                                                                                                                                                                           |
| `message_create`    | A new message is appended to the thread (user send or assistant response start).                                                                   | `{ id: string; role: 'user' \| 'assistant'; content: string }`                                                                                                                                                 |
| `message_update`    | Assistant message is being streamed, firing for each text chunk with the accumulated content so far.                                               | `{ id: string; role: 'assistant'; content: string }`                                                                                                                                                           |
| `tool_request`      | A tool is invoked (custom HTTP, MCP, or client-delegated). Fires once per tool call, as it starts.                                                 | `{ type: 'tool_request'; toolCallId: string; toolName: string; args: unknown; transport: 'mcp' \| 'custom' \| 'client'; clientDelegate?: boolean; request?: { url: string; method: string; body?: unknown } }` |
| `tool_response`     | A tool result arrived (from server or from a client handler).                                                                                      | `{ toolCallId: string; result: unknown }`                                                                                                                                                                      |
| `frustration`       | User message matched the frustration heuristic (profanity, "this is awful", etc.). Fires at send time.                                             | `{ messageId: string; text: string }`                                                                                                                                                                          |
| `newchat_submit`    | User submits a typed prompt that starts a new chat thread (not a suggestion chip click).                                                           | `{ text: string }`                                                                                                                                                                                             |
| `suggestion_click`  | User clicks a starter suggestion chip instead of typing.                                                                                           | `{ name: string; prompt: string; useNameInMessage: boolean; anthropicSkillIds?: string[] }`                                                                                                                    |
| `human_support`     | User escalates to a human, via the escalation button or the TalkToHuman action, with the **External** provider selected.                           | `{ source: 'button' \| 'tool'; reason: string \| null; request: string; summary: string; toolCallId?: string }`                                                                                                |
| `response_vote`     | User votes on a response (or clears their vote) when **Response voting** is enabled.                                                               | `{ messageId: string; vote: 'up' \| 'down' \| null }`                                                                                                                                                          |
| `review_ask`        | User upvotes a response, a good moment to prompt for a review.                                                                                     | `{ messageId: string }`                                                                                                                                                                                        |
| `resource_response` | An MCP resource was loaded into the assistant's active context.                                                                                    | `{ uri: string; fromCache: boolean; isStatic: boolean; bytes: number; content?: string; error?: string }`                                                                                                      |
| `live_chat_started` | A live chat began. Built-in provider only.                                                                                                         | `{ liveChatId: string }`                                                                                                                                                                                       |
| `live_chat_resumed` | A page load rejoined a live chat that was already running. Separate from `live_chat_started` so a reload mid-chat is not counted as a second chat. | `{ liveChatId: string }`                                                                                                                                                                                       |
| `live_chat_message` | A message was added to a live chat, from either side.                                                                                              | `{ id: string; sender: 'visitor' \| 'agent' \| 'system'; senderUserId?: string; senderName?: string; text: string; attachments?: unknown[]; createdAt: number }`                                               |
| `live_chat_ended`   | A live chat closed.                                                                                                                                | `{ liveChatId: string; endedBy: 'visitor' \| 'agent' \| 'idle' \| 'abandoned' }`                                                                                                                               |
| `live_chat_rating`  | The visitor rated the finished chat.                                                                                                               | `{ liveChatId: string; sentiment: 'good' \| 'bad' }`                                                                                                                                                           |

The five `live_chat_*` events describe [live chat](/yedric/going-further/live-chat.md) and fire only when the assistant's escalation provider is **Built-in**. With the **External** provider you get `human_support` instead, and none of these.

### Subscribing early

Register listeners before the widget is fully connected. They're stored in a shared `Map` and fire as soon as the event happens. No need to wait for any ready signal:

```html
<script src="https://cdn.yedric.ai/widget.js?agent=agt_123"></script>
<script>
  window.Yedric.on('message_create', (name, data) => {
    console.log('[chat]', data.role, data.content);
  });
</script>
<yedric-widget key="yk_abc…" agent="agt_123"></yedric-widget>
```

### `tool_request` vs `registerClientTool`

Both fire for client-delegated tool calls. They play different roles:

* `on('tool_request', …)` is **observation only**, so returning a value from the callback does **not** provide the tool result.
* `registerClientTool(name, handler)` is **execution**, so the handler's return value is sent to the server as the tool result.

Use `on` for telemetry, analytics, or UI cues ("the assistant is looking something up"). Use `registerClientTool` when you want to actually handle the call.

## Element methods

Grab the element with `document.querySelector('yedric-widget')`. These methods are useful when:

* You have multiple widget instances (rare).
* You need `setConfig` / `getConfig` (not exposed on `window.Yedric`).

```ts
interface YedricWidgetElement extends HTMLElement {
  open(prompt?: string, persist?: boolean): void;
  close(persist?: boolean): void;
  toggle(state?: 'open' | 'closed'): void;
  getSessionId(): string | null;
  refresh(): void;
  setConfig(config: Partial<YedricWidgetConfig>): void;
  getConfig(): YedricWidgetConfig;
}
```

### `setConfig(partial)`

Merges the given fields into the widget config. Shallow merge, except `customColors` and `mascotVideos.urls` which are deep-merged.

```js
const el = document.querySelector('yedric-widget');
el.setConfig({
  theme: 'dark',
  customColors: { primary: '#7c3aed' },
  authToken: () => window.shopify.idToken(), // can be a string or a function
});
```

`authToken` accepts a string, a function, or an async function. The widget calls the function before each request, so you can use it for refreshing tokens. The legacy field `authorization` is still read if `authToken` is not set.

The `backendUrl` field is always derived from the `widget.js` script URL and cannot be overridden via `setConfig`.

### `getConfig()`

Returns a shallow copy of the current config. Useful for debugging, but don't mutate it in place, use `setConfig` to change anything.

### setConfig-only properties

These properties can only be set via `setConfig()`. They have no HTML attribute equivalents.

| Property               | Type                         | Description                                                                                                                                           |
| ---------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `greetingDescription`  | `string`                     | Secondary line shown below `greeting` in the empty-thread view.                                                                                       |
| `inputPlaceholder`     | `string`                     | Placeholder text in the prompt input. Default: `'Start a new conversation'`.                                                                          |
| `fontFamily`           | `string`                     | Sets the `--yedric-font-family` CSS variable for the panel.                                                                                           |
| `fontSize`             | `string`                     | Sets the `--yedric-font-size` CSS variable.                                                                                                           |
| `inputBorderRadius`    | `string`                     | Sets `--input-radius` for the prompt textarea card.                                                                                                   |
| `buttonBorderRadius`   | `string`                     | Sets `--btn-radius` for action buttons.                                                                                                               |
| `greetingFontSize`     | `string`                     | Sets `--greeting-size` for the greeting headline.                                                                                                     |
| `headerHeight`         | `string`                     | Sets `--title-bar-height` for the panel header.                                                                                                       |
| `userAvatarStyle`      | `'initials'` \| `'gravatar'` | How user message avatars are rendered. Default: `'initials'`.                                                                                         |
| `gravatarDefault`      | `string`                     | Gravatar `d=` fallback style (`identicon`, `retro`, `mm`, etc.). Applies when `userAvatarStyle` is `'gravatar'`.                                      |
| `gravatarForceDefault` | `boolean`                    | When `true`, always use the Gravatar default image instead of a user's own Gravatar. Applies when `userAvatarStyle` is `'gravatar'`. Default `false`. |
| `beaconTooltip`        | `string`                     | Custom tooltip text shown on the launcher FAB.                                                                                                        |

```js
const el = document.querySelector('yedric-widget');
el.setConfig({
  greetingDescription: 'Ask me anything about your orders.',
  inputPlaceholder: 'Type your question…',
  fontFamily: "'Inter', sans-serif",
  userAvatarStyle: 'gravatar',
  beaconTooltip: 'Chat with us',
});
```

## TypeScript typing stub

If your host app uses TypeScript, add this to a `.d.ts` in your project so `window.Yedric` is typed:

```ts
// types/yedric.d.ts

export type YedricEventName =
  | 'open'
  | 'close'
  | 'frustration'
  | 'suggestion_click'
  | 'newchat_submit'
  | 'human_support'
  | 'response_vote'
  | 'review_ask'
  | 'resource_response'
  | 'tool_request'
  | 'tool_response'
  | 'message_create'
  | 'message_update'
  | 'live_chat_started'
  | 'live_chat_resumed'
  | 'live_chat_message'
  | 'live_chat_ended'
  | 'live_chat_rating';

export interface YedricClientToolContext {
  toolCallId: string;
  toolName: string;
  args: unknown;
}

export interface YedricGlobal {
  open(prompt?: string, persist?: boolean): void;
  close(persist?: boolean): void;
  toggle(state?: 'open' | 'closed'): void;
  getSessionId(): string | null;
  refresh(): void;
  on(
    eventName: YedricEventName | (string & {}),
    callback: (eventName: string, data: unknown) => void,
  ): void;
  off(
    eventName: YedricEventName | (string & {}),
    callback: (eventName: string, data: unknown) => void,
  ): void;
  setPreviewPageUrl(url: string | null | undefined): void;
  addContext(context: Record<string, unknown>): void;
  attachResource(uri: string, params?: Record<string, unknown>): void;
  registerClientTool(
    toolName: string,
    handler: (ctx: YedricClientToolContext) => Promise<unknown> | unknown,
  ): void;
  unregisterClientTool(toolName: string): void;
  event(name: string, payload?: Record<string, unknown>, opts?: { dedupeKey?: string }): void;
  eventStats(): ClientEventStats;
}

export interface ClientEventStats {
  enqueued: number;
  coalesced: number;
  dropped: {
    notInitialized: number;
    panelClosed: number;
    noConversation: number;
    gatesAtFlush: number;
    queueOverflow: number;
    nonRetryableStatus: number;
    retriesExhausted: number;
  };
  flushes: { attempted: number; retried: number };
  verdicts: { respond: number; context: number; ignore: number; ignoreReasons: Record<string, number> };
  turns: { started: number; handoffRejected: number; gaveUp: number };
}

declare global {
  interface Window {
    Yedric: YedricGlobal;
  }
}

export {};
```

The `(string & {})` trick keeps IDE autocomplete for the known event names while still allowing any string (in case new events are added before this type is updated).
