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

# Client API reference

> Page-side window.mcode.miniapp signatures, parameters, return values, permissions, and lifetimes.

This reference describes host interfaces available in official releases, with version differences noted in the relevant sections. See the [Client API guide](https://agent.minimax.io/docs/miniapp/authors/client-api.md) for examples and [Capabilities and progress](https://agent.minimax.io/docs/miniapp/progress.md) for version support.

## Entry and availability

```typescript theme={null}
const miniapp = window.mcode?.miniapp;
```

The host injects `window.mcode` into a managed Mini App's main page through preload. No runtime SDK or plugin ID is required. Ordinary browsers, other pages, and older hosts may omit these objects, so check before calling.

The v3.1.6 main page exposes the namespaces below. Presence does not establish a manifest declaration or an enabled permission: permission-protected calls are checked against page identity, declarations, and switches. Node uses the separate [Node API](https://agent.minimax.io/docs/miniapp/reference/node-api.md).

| Member | Page entry | Purpose |
| - | - | - |
| `message` | `miniapp.message.append` | Append to the message draft after confirmation |
| `window` | `miniapp.window` | This page's window handle; experimental |
| `speech` | `miniapp.speech.transcribe` | Host speech transcription |
| `tool` | `miniapp.tool.request` | Request tool permission ahead of time; no page `list` or `call` |
| `agent` | `miniapp.agent` | Page Agent API; see the [full reference](https://agent.minimax.io/docs/miniapp/reference/agent-api.md) |
| `capabilities` | `miniapp.capabilities.has` | Synchronous host interface detection |

Page async APIs reject with a lowercase code in `Error.message`. Do not rely on Node-style `code`, `retryable`, or `disposition` fields; see the [page error convention](https://agent.minimax.io/docs/miniapp/reference/errors.md#client).

## message.append

```typescript theme={null}
interface MiniAppMessageAPI {
  append(text: string): Promise<void>;
}
```

| Parameter | Type | Description |
| - | - | - |
| `text` | `string` | Plain text, up to 100,000 UTF-16 code units; preserves spaces, tabs, and newlines. HTML is treated as text |

Every call opens host confirmation, with no remembered choice. Even an empty string needs confirmation, but leaves the draft unchanged. After confirmation, the host preserves existing text, references, and attachments, and appends the new text. Resolution means the append completed, not that a message was sent or an Agent ran.

The target is the local, writable session containing the page. An unavailable target does not fall back to another session, a home draft, or a new session. This API grants no session reads and cannot change sent messages. There is no page cancellation parameter; the user can refuse the confirmation. After `result_unknown`, inspect the input before deciding to retry.

Errors are `user_cancelled`, `invalid_request`, `text_too_large`, `not_allowed`, `target_unavailable`, and `result_unknown`; see [Error codes](https://agent.minimax.io/docs/miniapp/reference/errors.md#client).

## window

The window handle interface may change, so feature-detect before use. It controls only its calling page's window. `id` is a stable opaque string, not a session or run ID; popping out or returning to the file panel does not change it.

```typescript theme={null}
interface MiniAppWindowHandle {
  readonly id: string;
  getState(): Promise<MiniAppWindowState>;
  update(patch: MiniAppWindowPatch): Promise<MiniAppWindowState>;
  focus(): Promise<void>;
  onDidChangeState(
    listener: (state: MiniAppWindowState) => void,
  ): { dispose(): void };
}

type MiniAppWindowState =
  | { readonly mode: 'inline'; readonly modeChangeReloadsPage?: boolean }
  | {
      readonly mode: 'window';
      readonly window: MiniAppWindowOptions;
      readonly modeChangeReloadsPage?: boolean;
    };
```

### State fields

`getState()` takes no arguments. `inline` means the page is in the file panel, with no `window` field. `window` means a separate window, with every field below.

Either state may also return a top-level `modeChangeReloadsPage` field indicating whether a mode change rebuilds the page; see [Page rebuilds and confirmation](#window-reload).

| `window` field | Type | Meaning |
| - | - | - |
| `width` / `height` | `number` | Content size, using the same dimensions as updates |
| `minWidth` / `minHeight` | `number` | Manifest minimum size, defaulting to 480 and 360 respectively |
| `x` / `y` | `number` | Screen coordinates of the content area, not the outer frame |
| `visible` | `boolean` | Whether the window is visible |
| `resizable` | `boolean` | Whether the current window can be resized |
| `frame` | `boolean` | Whether it has a system frame |
| `transparent` | `boolean` | Whether it uses a transparent window |
| `alwaysOnTop` | `boolean` | Whether it stays on top |
| `clickThrough` | `boolean` | Whether mouse events pass through |
| `skipTaskbar` | `boolean` | Whether it is hidden from the taskbar |

### update(patch)

```typescript theme={null}
interface MiniAppWindowPatch {
  readonly mode?: 'inline' | 'window';
  readonly width?: number;
  readonly height?: number;
  readonly x?: number;
  readonly y?: number;
  readonly visible?: boolean;
  readonly resizable?: boolean;
  readonly alwaysOnTop?: boolean;
  readonly clickThrough?: boolean;
  readonly skipTaskbar?: boolean;
}
```

A successful reply contains the full `MiniAppWindowState`. All fields are optional; omitted or `undefined` fields remain unchanged, and an empty object is valid. Unknown fields, wrong types, and non-finite numbers fail with `invalid_request`. In v3.1.10, cancelling window-transfer confirmation resolves with the current state and the original `mode`: a successful reply does not prove that the requested `mode` took effect. See [Page rebuilds and confirmation](#window-reload).

| Field or action | Rule |
| - | - |
| `mode` | Only `inline` or `window`. Even setting the current mode requires a user gesture while `navigator.userActivation.isActive` is `true` |
| `width` / `height` | Finite numbers greater than 0 and no greater than 10,000; rounded and clamped to the minimum size when applied. Programmatic resizing is refused when the manifest or the resulting window disallows it |
| `x` / `y` | Finite numbers from −100,000 to 100,000; rounded when applied |
| `alwaysOnTop: true` / `clickThrough: true` | The manifest's `surface.window` must already declare the corresponding field `true`; switching it off does not need that ceiling |
| `visible` | Hiding does not itself close the page; scripts, timers, and network requests keep running. Showing without a user gesture does not actively take focus |
| `frame`, `transparent`, `minWidth`, `minHeight` | Manifest-only construction options; including them in a runtime patch gives `not_permitted` |
| Inline state | If the target remains `inline`, fields other than `mode` give `inline_mode` |

When updating `x`, `y`, `width`, or `height`, the host checks the final content rectangle after rounding and applying minimum dimensions. It must overlap the same display work area by at least 40 px on both axes; otherwise the update fails with `not_permitted`. This rule also applies to size-only updates, not just window moves.

Updates without `mode` in the same animation frame are merged, with later fields winning. All callers in that frame share one result or rejection. A `mode` update is sent immediately to preserve its user gesture.

### focus()

Takes no arguments and returns `Promise<void>`. A user gesture is required. A separate window is restored from minimization, shown, and focused; an inline page requests host focus and selects its own tab. Do not rely on it to expand a collapsed file panel. It does not create a page or control another Mini App.

### onDidChangeState(listener)

Synchronously returns `{ dispose() }`. A non-function listener synchronously throws `Error('invalid_request')`. An `update()` delivers state if the current document remains alive and receives a successful reply. This does not prove that `mode` changed; inspect the delivered state. While the document remains alive, periodic checks detect external changes such as dragging a window, with no guarantee of every intermediate state. Initial subscription does not immediately deliver an initial value; call `getState()` for one.

Call `dispose()` to unsubscribe; leaving the page also clears listeners. A throwing listener does not interrupt a window update. Window errors are `inline_mode`, `not_permitted`, `busy`, `result_unknown`, and `invalid_request`. After `result_unknown`, use `getState()` to reconcile; see [Window errors](https://agent.minimax.io/docs/miniapp/reference/errors.md#window).

### Page rebuilds and confirmation

Official releases from v3.1.9 may return the optional `modeChangeReloadsPage?: boolean` field. Check the runtime result of `getState()`; this field is not an `update` patch option. When it is `true`, an actual change between `inline` and `window` rebuilds the page. Hosts that move the native page do not return this flag, so not every window transfer reloads the page.

A rebuild preserves the logical window ID and Node service, but the current document, JavaScript memory, media streams, page subscriptions, and pending page Promises do not survive. Save necessary state first. Do not put required work after `await update({ mode })` or wait for the old document's `onDidChangeState` to confirm completion. When the destination document starts, call `getState()`, restore saved content, and subscribe again. An absent flag does not guarantee that a host reload, crash, or another page-ending event cannot end the document.

From v3.1.10, popping out or returning a page that requires rebuilding first displays the impact in the main host window and waits for user confirmation. Closing the separate window or pressing Cmd/Ctrl+W uses the same flow; the original window is not hidden or destroyed while waiting. Cancellation leaves the page, requests, and window intact. `update()` resolves with the current state, whose `mode` may still be the original value, rather than rejecting with `user_cancelled`. After confirmation, the old page's requests and subscriptions are cancelled or cleaned up when the page ends. Completed operations are not undone, and requests are not replayed automatically.

## speech.transcribe

```typescript theme={null}
interface MiniAppSpeechAudioFormat {
  readonly sampleRate: 16000;
  readonly channelCount: 1;
  readonly sampleFormat: 's16';
}

interface MiniAppSpeechTranscribeInput {
  readonly audio: ArrayBuffer | ArrayBufferView;
  readonly format: MiniAppSpeechAudioFormat;
}

interface MiniAppSpeechTranscribeOptions {
  readonly onPartial?: (text: string) => void;
}

interface MiniAppSpeechAPI {
  transcribe(
    input: MiniAppSpeechTranscribeInput,
    options?: MiniAppSpeechTranscribeOptions,
  ): Promise<{ readonly text: string }>;
}
```

| Parameter | Description |
| - | - |
| `input.audio` | One raw PCM utterance: at least one sample, an even byte count, and at most 16,000,000 bytes (500 seconds). Accepts `Int16Array`, `ArrayBuffer`, or a view over an ordinary `ArrayBuffer`; shared buffers are refused |
| `input.format` | Exactly the three fields and values above: 16 kHz, mono, signed 16-bit little-endian PCM. Other rates, channels, or extra format fields are refused |
| `options.onPartial` | Optional callback receiving intermediate text while the host transcribes; returned `text` is final. A callback error is logged to the page console and does not stop transcription |

Declare `deviceAccess: ["microphone"]` and keep its switch on. The page captures audio, chooses the utterance boundary, converts samples, and stops all media tracks. The host transcribes the complete utterance; `onPartial` is not live recognition while recording. Do not pass WAV, WebM, Ogg, FLAC, MP3, or `MediaRecorder` output directly.

Only one transcription per Mini App can run at once; another gets `busy`. There is no page `signal` or `cancel()` parameter. Ending the page aborts the in-flight transcription, and host processing has a 180-second deadline. Switching microphone access off refuses later calls without forcibly stopping a media stream the page already owns.

Errors are `invalid_request`, `not_permitted`, `audio_too_large`, `busy`, `runtime_unavailable`, `transcription_failed`, and `result_unknown`; see [Speech errors](https://agent.minimax.io/docs/miniapp/reference/errors.md#speech). See the [guide](https://agent.minimax.io/docs/miniapp/authors/client-api.md#speech) for recording examples.

## tool.request

```typescript theme={null}
interface MiniAppToolRequestAPI {
  request(
    names: readonly string[],
  ): Promise<Record<string, 'allow' | 'ask' | 'deny'>>;
}
```

| Parameter or return | Description |
| - | - |
| `names` | 1–10 distinct, nonempty runtime tool names, each at most 1,024 UTF-8 bytes. Obtain them from Node `context.tool.list()` and pass them to the page through a business endpoint; never construct them |
| Return | A permission snapshot keyed by the requested names: `allow` permits a call, `ask` still needs confirmation, and `deny` currently refuses it. This is not an authorization credential for later calls |

Declare `toolAccess: ["call"]`, keep the Node service running, and invoke directly from a user gesture. The host asks only about tools still at `ask`, letting the user tick which to always allow. Leaving a box unticked or refusing does not necessarily reject the entire request: it returns actual permissions. Entries already `allow` or `deny` are not changed by this request.

When the tool-calling switch is off, the method returns `deny` for every requested name without a prompt. Without `toolAccess` there is no usable tool session; an unavailable Node tool session or channel rejects with `runtime_unavailable`. The page has neither tool `list()` nor `call()`, nor a cancellation parameter for this method. Actual calls use the [Node tool API](https://agent.minimax.io/docs/miniapp/reference/node-api.md#tools), and the host judges permission again on every call.

Errors are `invalid_request`, `not_permitted`, `approval_required`, `runtime_unavailable`, and `result_unknown`; see [Tool permission errors](https://agent.minimax.io/docs/miniapp/reference/errors.md#tool-request). Read the rejection's `Error.message`; do not rely on the `permissions` field attached to Node-side errors.

## capabilities.has

```typescript theme={null}
interface MiniAppHostCapabilities {
  has(name: string): boolean;
}
```

Returns a boolean synchronously without asking for permission or making an async host call. Unknown names return `false`; a non-string passed at runtime also returns `false`. Older hosts may omit `capabilities`, so use `miniapp?.capabilities?.has(name)`.

This table lists all 14 names in v3.1.6. It is fixed per host version and shared by the page and Node. `true` means the host implements the feature, not that the current side has that object, the manifest declared it, or the user granted it.

| Name | Page entry (after `miniapp.`) | Node entry |
| - | - | - |
| `agent.agents` | `agent.agents()` | `context.agent.agents()` |
| `agent.create` | `agent.create()` | `context.agent.create()` |
| `agent.models` | `agent.models()` | `context.agent.models()` |
| `agent.observe` | `agent.observe()` | `context.agent.observe()` |
| `agent.open` | `agent.open()` | None |
| `agent.run` | `agent.run()` | `context.agent.run()` |
| `agent.session` | `agent.session()` | `context.agent.session(id)` |
| `agent.sessions` | `agent.sessions()` | `context.agent.sessions()` |
| `hostConnector` | None | `context.hostConnector` |
| `message.append` | `message.append()` | None |
| `speech.transcribe` | `speech.transcribe()` | None |
| `tool` | No list or call entry | `context.tool` |
| `tool.request` | `tool.request()` | `context.tool.request()` |
| `window` | `window` handle | None |

`agent.reply`, `agent.answer`, and `agent.cancel` are not separate capability names; do not probe those names to infer method support. Check each API's availability and permission requirements as well.

## Agent namespace

For page `miniapp.agent` methods (`run`, `reply`, `answer`, `cancel`, `open`, catalogs, and session observation), including parameter differences from Node, see the [Agent API reference](https://agent.minimax.io/docs/miniapp/reference/agent-api.md).

## Camera and system notifications

These capabilities are supported from MiniMax Code v3.1.9. They use standard browser APIs, with no new host namespace or capability-detection name. Declare the minimum version in `engines.mcode` and the required items in `deviceAccess`; users can turn access off in plugin details.

| Declaration | Page operation | Conditions |
| - | - | - |
| `camera` | `navigator.mediaDevices.getUserMedia({ video: true })` | Camera is declared and enabled; the operating system may also require permission |
| `notifications` | `new Notification(...)` | Notifications are declared and enabled; the creating page handles the notification |

A combined audio/video request requires both `microphone` and `camera` to be declared and enabled. Turning a permission off blocks new capture requests; the page must still stop media streams it already holds. Standard capture errors, such as `NotAllowedError`, come from the browser API and do not use host speech-transcription error codes.

A notification click handler can call the window handle's `focus()` while the original page remains alive. Do not rely on it to expand a collapsed file panel. After that page closes or reloads, an old notification cannot reopen it. This API does not provide background notifications after the page closes.

See the [Camera](https://agent.minimax.io/docs/miniapp/authors/client-api.md#camera) and [System notifications](https://agent.minimax.io/docs/miniapp/authors/client-api.md#notifications) guides for examples and limits.
