--- sidebar_position: 6 --- # Messages and Typing API The messages API reads current messages, sends messages, edits or deletes plugin-owned messages, moderates messages, syncs messages, and exposes typing state. ## Required Capabilities | Method | Capability | | --- | --- | | `messages.readCurrent()` | `messages.read` | | `messages.send(content, channelId?)` | `messages.send` | | `messages.sendAsPluginUser(request)` | `messages.send` | | `messages.setTyping(isTyping, channelId?)` | `messages.send` | | `messages.subscribeTyping(handler)` | `messages.read` | | `messages.edit(messageId, content)` | `messages.editOwn` | | `messages.delete(messageId)` | `messages.deleteOwn` | | `messages.moderateDelete(messageId)` | `messages.moderate` | | `messages.sync(messages)` | `messages.sync` | ## Read Current Messages ```js export function activate(context) { const messages = context.api.messages.readCurrent(); context.api.logger.info('Current messages', messages.slice(-3).map((message) => ({ id: message.id, channelId: message.channelId, senderName: message.senderName, content: message.content }))); } ``` ## Send a Message ```js export function activate(context) { const created = context.api.messages.send( 'Reminder: raid starts at 20:00. Bring repairs and snacks.', 'general' ); context.api.logger.info('Sent reminder', { messageId: created.id }); } ``` ## Send as a Plugin User ```js export function activate(context) { const botUserId = context.api.server.registerPluginUser({ id: 'poll-bot', displayName: 'Poll Bot' }); context.api.messages.sendAsPluginUser({ pluginUserId: botUserId, channelId: 'general', content: 'Poll is open: react with 1 for dungeon, 2 for arena, 3 for crafting.' }); } ``` Capabilities required: `users.manage` and `messages.send`. ## Edit and Delete Plugin-Owned Messages ```js export function activate(context) { const message = context.api.messages.send('Draft event reminder', 'announcements'); context.api.messages.edit(message.id, 'Event reminder: voice meetup starts in 15 minutes.'); context.api.messages.delete(message.id); } ``` ## Moderation Delete ```js export function activate(context) { context.api.messages.moderateDelete('msg-spam-20260429-001'); } ``` Use moderation from explicit moderator actions, not automatic activation. ## Typing State ```js export function activate(context) { context.api.messages.setTyping(true, 'general'); setTimeout(() => { context.api.messages.setTyping(false, 'general'); }, 1500); context.subscriptions.push(context.api.messages.subscribeTyping((event) => { context.api.logger.info('Typing event', { displayName: event.displayName, isTyping: event.isTyping, channelId: event.channelId, serverId: event.serverId, voiceChannel: event.voiceChannel?.name ?? null }); })); } ``` Example typing event: ```json { "serverId": "room-7ebdde75", "channelId": "general", "userId": "user-muse-01", "displayName": "Muse", "isTyping": true } ``` ## Sync Messages ```js export function activate(context) { context.api.messages.sync([ { id: 'external-standup-001', roomId: 'room-7ebdde75', channelId: 'standup', senderId: 'standup-importer', senderName: 'Standup Importer', content: 'Imported note: Alice is working on plugin docs.', timestamp: 1777473600000, isDeleted: false } ]); } ``` Sync should preserve message ids and timestamps from the source system when possible.