---
sidebar_position: 4
---

# Plugin API Reference

`TojuClientPluginApi` is the object passed to a plugin activation context. The runtime freezes the API object before passing it to plugin code.

This page is the compact map. Use the focused API pages for concrete copy-paste examples with literal input data.

## Focused API Pages

- [Context and Logging](./api/context-and-logging.md)
- [Profile API](./api/profile.md)
- [Users and Roles API](./api/users-and-roles.md)
- [Server API](./api/server.md)
- [Channels API](./api/channels.md)
- [Messages and Typing API](./api/messages-and-typing.md)
- [Events API](./api/events.md)
- [Message Bus API](./api/message-bus.md)
- [P2P and Media API](./api/p2p-and-media.md)
- [Storage API](./api/storage.md)
- [UI API](./api/ui.md)

## Activation Types

```ts
interface TojuPluginDisposable {
  dispose: () => void;
}

interface TojuPluginActivationContext {
  api: TojuClientPluginApi;
  manifest: TojuPluginManifest;
  pluginId: string;
  subscriptions: TojuPluginDisposable[];
}

interface TojuClientPluginModule {
  activate?: (context: TojuPluginActivationContext) => Promise<void> | void;
  deactivate?: (context: TojuPluginActivationContext) => Promise<void> | void;
  onPluginDataChanged?: (context: TojuPluginActivationContext, event: unknown) => Promise<void> | void;
  onServerRequirementsChanged?: (context: TojuPluginActivationContext, snapshot: PluginRequirementsSnapshot) => Promise<void> | void;
  ready?: (context: TojuPluginActivationContext) => Promise<void> | void;
}
```

## Profiles

```ts
interface PluginApiProfileUpdate {
  description?: string;
  displayName: string;
}

interface PluginApiAvatarUpdate {
  avatarHash: string;
  avatarMime: string;
  avatarUrl: string;
}
```

| Method                         | Capability      | Description                                       |
| ------------------------------ | --------------- | ------------------------------------------------- |
| `profile.getCurrent()`         | `profile.read`  | Returns the current `User` or `null`.             |
| `profile.update(profile)`      | `profile.write` | Updates display name and optional description.    |
| `profile.updateAvatar(avatar)` | `profile.write` | Updates avatar URL, MIME type, and hash metadata. |

## Users and Roles

| Method                              | Capability     | Description                       |
| ----------------------------------- | -------------- | --------------------------------- |
| `users.getCurrent()`                | `users.read`   | Returns current `User` or `null`. |
| `users.list()`                      | `users.read`   | Returns known users.              |
| `users.readMembers()`               | `users.read`   | Returns active room members.      |
| `users.setRole(userId, role)`       | `roles.manage` | Updates a user's role.            |
| `users.kick(userId)`                | `users.manage` | Kicks a user.                     |
| `users.ban(userId, reason?)`        | `users.manage` | Bans a user with optional reason. |
| `roles.list()`                      | `roles.read`   | Returns room roles.               |
| `roles.setAssignments(assignments)` | `roles.manage` | Replaces role assignments.        |

## Server

```ts
interface PluginApiServerSettingsUpdate {
  description?: string;
  isPrivate?: boolean;
  maxUsers?: number;
  name?: string;
  password?: string;
  topic?: string;
}

interface PluginApiPluginUserRequest {
  avatarUrl?: string;
  displayName: string;
  id?: string;
}
```

| Method                                  | Capability      | Description                                  |
| --------------------------------------- | --------------- | -------------------------------------------- |
| `server.getCurrent()`                   | `server.read`   | Returns the current `Room` or `null`.        |
| `server.registerPluginUser(request)`    | `users.manage`  | Adds a plugin-owned user and returns its id. |
| `server.updatePermissions(permissions)` | `server.manage` | Updates partial room permissions.            |
| `server.updateSettings(settings)`       | `server.manage` | Updates room settings.                       |

## Channels

```ts
interface PluginApiChannelRequest {
  id?: string;
  name: string;
  position?: number;
}
```

| Method                              | Capability        | Description                        |
| ----------------------------------- | ----------------- | ---------------------------------- |
| `channels.list()`                   | `channels.read`   | Returns current room channels.     |
| `channels.select(channelId)`        | `channels.read`   | Selects a channel.                 |
| `channels.addAudioChannel(request)` | `channels.manage` | Adds a voice channel.              |
| `channels.addVideoChannel(request)` | `channels.manage` | Registers a video channel section. |
| `channels.rename(channelId, name)`  | `channels.manage` | Renames a channel.                 |
| `channels.remove(channelId)`        | `channels.manage` | Removes a channel.                 |

## Messages

```ts
interface PluginApiMessageAsPluginUserRequest {
  channelId?: string;
  content: string;
  pluginUserId: string;
}
```

| Method                                     | Capability           | Description                                        |
| ------------------------------------------ | -------------------- | -------------------------------------------------- |
| `messages.readCurrent()`                   | `messages.read`      | Returns current visible messages.                  |
| `messages.send(content, channelId?)`       | `messages.send`      | Sends a message and returns the created `Message`. |
| `messages.sendAsPluginUser(request)`       | `messages.send`      | Emits a message from a registered plugin user.     |
| `messages.setTyping(isTyping, channelId?)` | `messages.send`      | Broadcasts current typing state for a channel.     |
| `messages.subscribeTyping(handler)`        | `messages.read`      | Subscribes to peer typing state.                   |
| `messages.edit(messageId, content)`        | `messages.editOwn`   | Edits a plugin message.                            |
| `messages.delete(messageId)`               | `messages.deleteOwn` | Deletes a plugin message.                          |
| `messages.moderateDelete(messageId)`       | `messages.moderate`  | Performs a moderation delete.                      |
| `messages.sync(messages)`                  | `messages.sync`      | Syncs an array of messages into state.             |

## Events

```ts
interface PluginApiEventSubscription {
  eventName: string;
  handler: (event: PluginEventEnvelope) => void;
}

interface PluginEventEnvelope<TPayload = unknown> {
  emittedAt?: number;
  eventId?: string;
  eventName: string;
  payload: TPayload;
  pluginId: string;
  serverId: string;
  sourcePluginUserId?: string;
  sourceUserId?: string;
  type: 'plugin_event';
}
```

| Method                                     | Capability                | Description                                                 |
| ------------------------------------------ | ------------------------- | ----------------------------------------------------------- |
| `events.publishServer(eventName, payload)` | `events.server.publish`   | Sends a declared plugin event through the signaling server. |
| `events.subscribeServer(subscription)`     | `events.server.subscribe` | Subscribes to a declared server plugin event.               |
| `events.publishP2p(eventName, payload)`    | `events.p2p.publish`      | Sends a declared plugin event over peer paths.              |
| `events.subscribeP2p(subscription)`        | `events.p2p.subscribe`    | Registers a P2P event subscription.                         |

## Message Bus

```ts
interface PluginApiMessageBusEnvelope {
  channelId?: string;
  eventId: string;
  messages?: Message[];
  payload?: unknown;
  pluginId: string;
  roomId: string;
  sentAt: number;
  sourcePeerId?: string;
  sourceUserId?: string;
  topic: string;
}

interface PluginApiMessageBusLatestRequest {
  channelId?: string;
  includeDeleted?: boolean;
  limit?: number;
  sinceTimestamp?: number;
  targetPeerId?: string;
  topic?: string;
}

interface PluginApiMessageBusPublishRequest extends PluginApiMessageBusLatestRequest {
  includeLatestMessages?: boolean;
  includeSelf?: boolean;
  payload?: unknown;
  topic: string;
}

interface PluginApiMessageBusSubscription {
  channelId?: string;
  handler: (event: PluginApiMessageBusEnvelope) => void;
  latestMessageLimit?: number;
  replayLatest?: boolean;
  topic?: string;
}
```

| Method                                    | Capability                                         | Description                                                            |
| ----------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------- |
| `messageBus.publish(request)`             | `events.p2p.publish`, optionally `messages.read`   | Publishes a plugin-bus event, optionally including latest messages.    |
| `messageBus.sendLatestMessages(request?)` | `events.p2p.publish` and `messages.read`           | Sends a latest-message snapshot.                                       |
| `messageBus.subscribe(subscription)`      | `events.p2p.subscribe`, optionally `messages.read` | Subscribes to plugin-bus events, optionally replaying latest messages. |

## P2P and Media

```ts
interface PluginApiAudioClipRequest {
  volume?: number;
  url: string;
}

interface PluginApiCustomStreamRequest {
  label?: string;
  stream: MediaStream;
}
```

| Method                                     | Capability             | Description                                   |
| ------------------------------------------ | ---------------------- | --------------------------------------------- |
| `p2p.connectedPeers()`                     | `p2p.data`             | Returns connected peer ids.                   |
| `p2p.broadcastData(eventName, payload)`    | `p2p.data`             | Broadcasts plugin data.                       |
| `p2p.sendData(peerId, eventName, payload)` | `p2p.data`             | Sends plugin data targeted to a peer.         |
| `media.playAudioClip(request)`             | `media.playAudio`      | Plays an audio URL at optional volume.        |
| `media.addCustomAudioStream(request)`      | `media.addAudioStream` | Contributes an audio `MediaStream`.           |
| `media.addCustomVideoStream(request)`      | `media.addVideoStream` | Registers a video `MediaStream` contribution. |
| `media.setInputVolume(volume)`             | `audio.volume`         | Sets local input volume.                      |
| `media.setOutputVolume(volume)`            | `audio.volume`         | Sets local output volume.                     |

## Storage

| Method                         | Capability                 | Description                             |
| ------------------------------ | -------------------------- | --------------------------------------- |
| `clientData.read(key)`         | `storage.local`            | Reads async plugin-local data.          |
| `clientData.write(key, value)` | `storage.local`            | Writes async plugin-local data.         |
| `clientData.remove(key)`       | `storage.local`            | Removes async plugin-local data.        |
| `serverData.read(key)`         | `storage.serverData.read`  | Reads local per-user/per-server data.   |
| `serverData.write(key, value)` | `storage.serverData.write` | Writes local per-user/per-server data.  |
| `serverData.remove(key)`       | `storage.serverData.write` | Removes local per-user/per-server data. |
| `storage.get(key)`             | `storage.local`            | Legacy synchronous local read.          |
| `storage.set(key, value)`      | `storage.local`            | Legacy synchronous local write.         |
| `storage.remove(key)`          | `storage.local`            | Legacy synchronous local remove.        |

## UI Contributions

```ts
interface PluginApiActionContribution {
  icon?: string;
  label: string;
  run: () => Promise<void> | void;
}

interface PluginApiPageContribution {
  label: string;
  path: string;
  render: () => HTMLElement | string;
}

interface PluginApiPanelContribution {
  label: string;
  order?: number;
  render: () => HTMLElement | string;
}

interface PluginApiSettingsPageContribution {
  label: string;
  order?: number;
  render: () => HTMLElement | string;
  settingsKey?: string;
}

interface PluginApiChannelSectionContribution {
  label: string;
  order?: number;
  type?: 'audio' | 'custom' | 'video';
}

interface PluginApiEmbedRendererContribution {
  embedType: string;
  render: (payload: unknown) => HTMLElement | string;
}

interface PluginApiDomMountRequest {
  element: HTMLElement;
  position?: InsertPosition;
  target: Element | string;
}
```

| Method                                        | Capability           | Description                                                     |
| --------------------------------------------- | -------------------- | --------------------------------------------------------------- |
| `ui.registerAppPage(id, contribution)`        | `ui.pages`           | Adds a plugin app page.                                         |
| `ui.registerSettingsPage(id, contribution)`   | `ui.settings`        | Adds a plugin settings page.                                    |
| `ui.registerSidePanel(id, contribution)`      | `ui.sidePanel`       | Adds a side panel.                                              |
| `ui.registerChannelSection(id, contribution)` | `ui.channelsSection` | Adds a channel section.                                         |
| `ui.registerComposerAction(id, contribution)` | `ui.pages`           | Adds a composer action.                                         |
| `ui.registerProfileAction(id, contribution)`  | `ui.pages`           | Adds a profile action.                                          |
| `ui.registerToolbarAction(id, contribution)`  | `ui.pages`           | Adds an action tile to the server side panel View plugins menu. |
| `ui.registerEmbedRenderer(id, contribution)`  | `ui.embeds`          | Adds an embed renderer.                                         |
| `ui.mountElement(id, request)`                | `ui.dom`             | Mounts plugin-owned DOM into a target element or selector.      |

## Slash Commands

Slash commands appear in a Discord-style autocomplete menu when a user types `/` in the chat composer. A command with `scope: 'global'` (the default) is offered in every chat surface, including direct messages; a command with `scope: 'server'` only appears while a chat server is active. The user picks a command from the menu (or types it and presses Enter) and the `run` callback executes with the parsed arguments and the current interaction context.

```ts
type PluginApiSlashCommandScope = 'global' | 'server';

interface PluginApiSlashCommandOption {
  description?: string;
  name: string;
  required?: boolean;
  // 'rest' captures all remaining text; otherwise a single whitespace-delimited token
  type?: 'string' | 'number' | 'boolean' | 'rest';
}

interface PluginApiSlashCommandContext extends PluginApiActionContext {
  args: Record<string, string>; // parsed values keyed by option name
  command: string; // invoked name without the leading slash
  rawArgs: string; // raw text typed after the command name
}

interface PluginApiSlashCommandContribution {
  description?: string;
  icon?: string;
  name: string;
  options?: PluginApiSlashCommandOption[];
  run: (context: PluginApiSlashCommandContext) => Promise<void> | void;
  scope?: PluginApiSlashCommandScope;
}
```

| Method                              | Capability     | Description                                                          |
| ----------------------------------- | -------------- | ------------------------------------------------------------------- |
| `commands.register(id, command)`    | `ui.commands`  | Registers a `/` slash command for the chat composer.               |
| `commands.list()`                   | `ui.commands`  | Lists every slash command currently registered across all plugins. |

```ts
context.subscriptions.push(
  api.commands.register('shout', {
    description: 'Shout a message in uppercase',
    icon: '📢',
    name: 'shout',
    options: [{ name: 'message', required: true, type: 'rest' }],
    run: (slash) => api.messages.send(slash.args.message.toUpperCase()),
    scope: 'server'
  })
);
```

## Context and Logger

| Method                         | Capability | Description                                                                |
| ------------------------------ | ---------- | -------------------------------------------------------------------------- |
| `context.getCurrent()`         | None       | Reads current user, server, active text channel, and active voice channel. |
| `logger.debug(message, data?)` | None       | Writes a debug plugin log entry.                                           |
| `logger.info(message, data?)`  | None       | Writes an info plugin log entry.                                           |
| `logger.warn(message, data?)`  | None       | Writes a warning plugin log entry.                                         |
| `logger.error(message, data?)` | None       | Writes an error plugin log entry.                                          |
