> ## Documentation Index
> Fetch the complete documentation index at: https://docs.karta.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Command API & events

> Drive the widget from your page with karta(...) - open it from your own button, identify the user, send messages, and subscribe to events.

The widget exposes one global function, `window.karta`, in the
Segment/Intercom style: you call `karta(command, ...args)`. It is
**load-order independent** - install a tiny inline queue stub, call it
immediately, and the bundle replays your calls in order once it loads.

If all you want is the drop-in launcher, you do not need any of this - see the
[Quickstart](/sdks/widget/quickstart). Reach for the command API when you want
to open the widget from your own UI, identify the signed-in user, or react to
what happens inside it.

## The inline queue stub

Add the stub once (before or after your own scripts), then load the bundle
async. Calls made before the bundle arrives are buffered and replayed in order.

```html theme={null}
<script>
  // Buffers calls until the real bundle arrives, then replays in order.
  window.karta = window.karta || function () {
    (window.karta.q = window.karta.q || []).push(arguments);
  };
</script>
<script
  async
  src="https://cdn.karta.sh/widget/v1/karta.js"
  data-embed-key="pk_live_xxx"
  data-agent="coffeeco/support-bot"
></script>

<script>
  // These run BEFORE the bundle finishes loading and still take effect, in
  // order: identify is applied before the session opens.
  karta("identify", { userId: "u_123" });
  karta("on", "message:received", (e) => analytics.track("agent_reply", e));
  document.querySelector("#help").addEventListener("click", () => karta("open"));
</script>
```

You can also drive the whole config from JavaScript with `init` instead of
`data-*`:

```js theme={null}
karta("init", {
  baseUrl: "https://agent.karta.sh",
  agentRef: "coffeeco/support-bot",
  embedKey: "pk_live_xxx",
  theme: { accent: "#d4ff4f" },
});
```

The runtime-resolved identity is not an appearance override: a named Karta uses
its Karta name, while other embeds use the underlying agent name.

## Commands

| Command                     | Args                          | Effect                                                                                                                                                                                  |
| --------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init`                      | `{ ...overrides }`            | Per-page config overrides, merged **before** mount. Ignored after the widget has mounted.                                                                                               |
| `identify`                  | `{ userId?, identityToken? }` | Identity for the **next** session. `userId` is **advisory only - never an auth credential**, and keys reload continuity. `identityToken` is the [verified](/sdks/widget/identity) path. |
| `open` / `close` / `toggle` | -                             | Show / hide / flip the panel. The first `open`/`toggle`/`sendMessage` triggers the lazy mount.                                                                                          |
| `sendMessage`               | `text`                        | Programmatically send a user message and drive a turn.                                                                                                                                  |
| `escalate`                  | `{ reason?, context? }`       | Fire the `escalate` event for your page to handle (e.g. open a live-chat handoff). Event-only.                                                                                          |
| `on` / `off` / `once`       | `eventName, callback`         | Subscribe / unsubscribe / subscribe-once to a host-page event (below).                                                                                                                  |
| `shutdown` / `reset`        | -                             | Tear the widget down (abort in-flight work, remove the host).                                                                                                                           |

Unknown commands are logged via `console.warn` and ignored - a bad call never
throws into your page.

## Events

Subscribe with `karta("on", name, callback)`:

| Event              | Payload                                  | Fires when                                                                                                                                                              |
| ------------------ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ready`            | -                                        | The widget has mounted and is interactive.                                                                                                                              |
| `open` / `close`   | -                                        | The panel was opened / closed.                                                                                                                                          |
| `session:started`  | `{ sessionId }`                          | A backing agent session was created. `sessionId` is empty for the session-implicit Responses transport.                                                                 |
| `message:sent`     | `{ text }`                               | The user submitted a message.                                                                                                                                           |
| `message:received` | `{ text }`                               | A turn completed; `text` is the final agent message.                                                                                                                    |
| `unread`           | `{ count }`                              | An agent message arrived while the panel was **closed** (running count; resets to `0` on open).                                                                         |
| `escalate`         | `{ reason?, context? }`                  | The user or a command asked to escalate to a human. A `karta('escalate', ...)` call delivers exactly the payload you pass, unchanged.                                   |
| `error`            | `{ message, code? }`                     | A turn failed (stream / auth / transport).                                                                                                                              |
| `limit_reached`    | `{ message, code?, retryAfterSeconds? }` | The agent hit a usage / spend / rate limit. The widget shows a friendly state and locks the composer - see [Security & privacy](/security/chat-widget#graceful-limits). |

```js theme={null}
karta("on", "unread", ({ count }) => setBadge(count));
karta("on", "escalate", ({ context }) => openLiveChat(context));
karta("on", "limit_reached", ({ retryAfterSeconds }) => showBackLater(retryAfterSeconds));
karta("on", "error", ({ message, code }) => console.warn("karta:", code, message));

const onReady = () => console.log("widget ready");
karta("on", "ready", onReady);
karta("off", "ready", onReady); // unsubscribe
```

A throwing subscriber is caught and logged - it can never break the widget or
starve other subscribers.

## Next

<CardGroup cols={2}>
  <Card title="Identity" icon="user-check" href="/sdks/widget/identity">
    `identify` in depth: soft vs verified, and the HMAC scheme.
  </Card>

  <Card title="Headless & React" icon="react" href="/sdks/widget/headless-react">
    Skip the prebuilt UI: `KartaAgentClient` and `<KartaWidget/>`.
  </Card>
</CardGroup>
