21 KiB
API Reference
Complete reference for the Enhanced Mattermost Extension API.
Table of Contents
Actions
editMessage
Edit an existing message posted by the bot.
import { editMessage } from '@lumbridgecorp/openclaw-mattermost';
const result = await editMessage({
cfg: OpenClawConfig,
postId: string,
channelId: string,
message?: string,
props?: Record<string, unknown>,
accountId?: string | null,
fetchImpl?: MattermostFetch
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
cfg |
OpenClawConfig |
Yes | OpenClaw configuration object |
postId |
string |
Yes | ID of the post to edit |
channelId |
string |
Yes | Channel ID containing the post |
message |
string |
No | New message content |
props |
Record<string, unknown> |
No | Additional post properties |
accountId |
string | null |
No | Specific account to use |
fetchImpl |
MattermostFetch |
No | Custom fetch implementation |
Returns:
type EditMessageResult =
| { ok: true; postId: string; channelId: string }
| { ok: false; error: string; errorCode?: string };
Error Codes:
PERMISSION_DENIED- Cannot edit other users' messages or system messagesPOST_NOT_FOUND- Post was deleted or doesn't existRATE_LIMITED- Too many edit requestsAUTH_FAILED- Invalid or expired token
Example:
const result = await editMessage({
cfg: openclawConfig,
postId: 'abc123def456ghi789jkl012',
channelId: 'channel123',
message: 'Updated message content'
});
if (result.ok) {
console.log(`Edited post ${result.postId}`);
} else {
console.error(`Edit failed: ${result.error} (${result.errorCode})`);
}
deleteMessage
Delete a message posted by the bot.
import { deleteMessage } from '@lumbridgecorp/openclaw-mattermost';
const result = await deleteMessage({
cfg: OpenClawConfig,
postId: string,
channelId?: string,
accountId?: string | null,
fetchImpl?: MattermostFetch
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
cfg |
OpenClawConfig |
Yes | OpenClaw configuration object |
postId |
string |
Yes | ID of the post to delete |
channelId |
string |
No | Channel ID (for validation) |
accountId |
string | null |
No | Specific account to use |
fetchImpl |
MattermostFetch |
No | Custom fetch implementation |
Returns:
type DeleteMessageResult = { ok: true } | { ok: false; error: string };
Notes:
- Returns
ok: trueif post is already deleted (idempotent) - Cannot delete system messages
- Can only delete bot's own messages
Example:
const result = await deleteMessage({
cfg: openclawConfig,
postId: 'abc123def456ghi789jkl012'
});
if (result.ok) {
console.log('Message deleted successfully');
} else {
console.error(`Delete failed: ${result.error}`);
}
downloadFile
Download a file from Mattermost with security validations.
import { downloadFile } from '@lumbridgecorp/openclaw-mattermost';
const result = await downloadFile({
cfg: OpenClawConfig,
fileId: string,
destinationPath: string,
maxSize?: number,
accountId?: string | null,
fetchImpl?: MattermostFetch,
allowedMimePrefixes?: string[]
});
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
cfg |
OpenClawConfig |
Yes | - | OpenClaw configuration |
fileId |
string |
Yes | - | Mattermost file ID |
destinationPath |
string |
Yes | - | Local path to save file |
maxSize |
number |
No | 100MB | Maximum file size in bytes |
accountId |
string | null |
No | - | Specific account to use |
fetchImpl |
MattermostFetch |
No | - | Custom fetch implementation |
allowedMimePrefixes |
string[] |
No | Built-in list | Allowed MIME types |
Returns:
type DownloadFileResult =
| { ok: true; filePath: string; fileId: string; metadata: FileMetadata }
| { ok: false; error: string; errorCode?: string };
type FileMetadata = {
id: string;
name: string;
mimeType: string;
size: number;
extension: string;
};
Error Codes:
CONFIG_ERROR- Missing botToken or baseUrlINVALID_PARAMS- Missing fileId or destinationPathFILE_TOO_LARGE- File exceeds maxSizeEMPTY_FILE- File has no contentBLOCKED_FILE_TYPE- Executable file extensionBLOCKED_MIME_TYPE- MIME type not in allowlistFILE_NOT_FOUND- File doesn't existPERMISSION_DENIED- No access to fileAUTH_FAILED- Invalid tokenDOWNLOAD_FAILED- Network or server error
Security Restrictions:
Blocked file extensions:
.exe, .sh, .bat, .cmd, .com, .msi, .dll, .so, .dylib,
.app, .dmg, .pkg, .deb, .rpm, .apk, .ipa, .jar, .war,
.ear, .py, .rb, .pl, .php, .js, .ts, .vbs, .ps1, .psm1,
.scr, .hta, .bin, .run, .out, .elf
Default allowed MIME types:
image/*, video/*, audio/*, text/*
application/pdf, application/json, application/xml
application/csv, application/zip, application/gzip, application/tar
Example:
const result = await downloadFile({
cfg: openclawConfig,
fileId: 'file123abc',
destinationPath: '/tmp/report.pdf',
maxSize: 50 * 1024 * 1024 // 50MB
});
if (result.ok) {
console.log(`Downloaded: ${result.filePath}`);
console.log(`Size: ${result.metadata.size} bytes`);
console.log(`Type: ${result.metadata.mimeType}`);
}
sendMessage
Send a message to a Mattermost channel.
import { sendMessage } from '@lumbridgecorp/openclaw-mattermost';
await sendMessage({
cfg: OpenClawConfig,
channelId: string,
message: string,
rootId?: string,
fileIds?: string[],
props?: Record<string, unknown>,
accountId?: string | null
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
cfg |
OpenClawConfig |
Yes | OpenClaw configuration |
channelId |
string |
Yes | Target channel ID |
message |
string |
Yes | Message content (supports Markdown) |
rootId |
string |
No | Parent post ID for threaded replies |
fileIds |
string[] |
No | Array of uploaded file IDs to attach |
props |
Record<string, unknown> |
No | Additional post properties |
accountId |
string | null |
No | Specific account to use |
Example:
await sendMessage({
cfg: openclawConfig,
channelId: 'channel123',
message: 'Hello **World**!', // Markdown supported
rootId: 'parent-post-id' // Thread reply
});
addReaction
Add an emoji reaction to a message.
import { addReaction } from '@lumbridgecorp/openclaw-mattermost';
await addReaction({
cfg: OpenClawConfig,
postId: string,
emoji: string, // e.g., 'thumbsup', 'white_check_mark'
accountId?: string | null
});
Example:
await addReaction({
cfg: openclawConfig,
postId: 'post123',
emoji: 'thumbsup'
});
uploadFile
Upload a file to Mattermost.
import { uploadFile } from '@lumbridgecorp/openclaw-mattermost';
const fileInfo = await uploadFile({
cfg: OpenClawConfig,
channelId: string,
buffer: Buffer,
fileName: string,
contentType?: string,
accountId?: string | null
});
Returns: MattermostFileInfo object with id, name, mime_type, size
Example:
import { readFileSync } from 'fs';
const buffer = readFileSync('./document.pdf');
const fileInfo = await uploadFile({
cfg: openclawConfig,
channelId: 'channel123',
buffer,
fileName: 'document.pdf',
contentType: 'application/pdf'
});
// Use the file ID when sending a message
await sendMessage({
cfg: openclawConfig,
channelId: 'channel123',
message: 'Here is the document:',
fileIds: [fileInfo.id]
});
Interactive Directives
Interactive directives allow you to embed buttons and dropdowns in messages using a special syntax. These are parsed and converted to Mattermost interactive components.
Button Directive
Syntax:
[[mattermost_buttons: Label1:value1:style1, Label2:value2:style2, ...]]
Format:
Label: Display text on the button (max 30 chars)value: Value sent when button is clickedstyle: Button style -default,primary, ordanger(optional, defaults todefault)
Maximum: 5 buttons per directive
Example:
const message = `
Please review this request:
[[mattermost_buttons:
Approve:approve_request:primary,
Reject:reject_request:danger,
Review Later:review_later:default
]]
`;
await sendMessage({ cfg: openclawConfig, channelId: 'channel123', message });
Select/Dropdown Directive
Syntax:
[[mattermost_select: Placeholder | Label1:value1, Label2:value2, ...]]
Format:
Placeholder: Text shown when nothing selected (optional, defaults to "Choose an option")Label: Display text for the optionvalue: Value sent when option is selected
Maximum: 100 options per directive
Example:
const message = `
Select a priority level:
[[mattermost_select:
Select Priority |
Low:low,
Medium:medium,
High:high,
Critical:critical
]]
`;
await sendMessage({ cfg: openclawConfig, channelId: 'channel123', message });
Multiple Directives
You can combine multiple directives in a single message:
const message = `
Deployment ready for production.
[[mattermost_buttons: Deploy:deploy:primary, Cancel:cancel:danger]]
Select deployment region:
[[mattermost_select: Choose Region | US East:us-east, US West:us-west, EU:eu, APAC:apac]]
`;
Handling Interactions
When a user interacts with buttons or selects, Mattermost sends a callback to your configured interactions.callbackBaseUrl:
{
"type": "button" | "select",
"value": "the_button_or_option_value",
"user_id": "mattermost_user_id",
"channel_id": "channel_id",
"post_id": "original_post_id",
"context": { ... }
}
Configuration:
{
"channels": {
"mattermost": {
"interactions": {
"callbackBaseUrl": "https://your-bot.example.com/callbacks",
"allowedSourceIps": ["10.0.0.0/8", "192.168.1.0/24"]
}
}
}
}
Configuration Types
MattermostAccountConfig
interface MattermostAccountConfig {
// Core settings
baseUrl?: string;
botToken?: SecretInput;
pat?: SecretInput;
enabled?: boolean;
name?: string;
// Access control
dmPolicy?: 'open' | 'allowlist' | 'pairing';
allowFrom?: Array<string | number>;
groupPolicy?: 'open' | 'allowlist';
groupAllowFrom?: Array<string | number>;
requireMention?: boolean;
// Chat modes
chatmode?: 'oncall' | 'onmessage' | 'onchar';
oncharPrefixes?: string[];
// Message settings
textChunkLimit?: number;
chunkMode?: 'length' | 'newline';
blockStreaming?: boolean;
blockStreamingCoalesce?: BlockStreamingConfig;
replyToMode?: 'off' | 'first' | 'all';
responsePrefix?: string;
// Slash commands
commands?: {
native?: boolean | 'auto';
nativeSkills?: boolean | 'auto';
callbackPath?: string;
callbackUrl?: string;
};
// Interactions
interactions?: {
callbackBaseUrl?: string;
allowedSourceIps?: string[];
};
// Actions
actions?: {
reactions?: boolean;
downloadFile?: boolean;
delete?: boolean;
};
// Security
allowPrivateNetwork?: boolean;
configWrites?: boolean;
dangerouslyAllowNameMatching?: boolean;
// Retry
dmChannelRetry?: {
maxRetries?: number;
initialDelayMs?: number;
maxDelayMs?: number;
timeoutMs?: number;
};
// Group-specific settings
groups?: Record<string, {
requireMention?: boolean;
}>;
// Capabilities
capabilities?: string[] | { interactiveReplies?: boolean };
markdown?: MarkdownConfig;
}
SecretInput
Secret values can be provided in multiple formats:
type SecretInput =
| string // Raw value (not recommended for production)
| { $env: string } // Environment variable reference
| { $file: string } // File path containing secret
| { $secretRef: string }; // Secret manager reference
Examples:
{
"botToken": "${MATTERMOST_BOT_TOKEN}",
"botToken": { "$env": "MATTERMOST_BOT_TOKEN" },
"botToken": { "$file": "/run/secrets/bot_token" }
}
Error Handling
Error Classes
All errors extend MattermostError with structured information:
class MattermostError extends Error {
readonly code: ErrorCode;
readonly context: ErrorContext;
readonly retryable: boolean;
readonly userMessage: string;
readonly retryAfterMs?: number;
toJSON(): Record<string, unknown>;
toLogString(): string;
}
Error Codes
| Code | Description | Retryable |
|---|---|---|
MATTERMOST_API_ERROR |
General API error | Depends on status |
MATTERMOST_API_TIMEOUT |
Request timeout | Yes |
MATTERMOST_API_RATE_LIMIT |
Rate limited | Yes |
AUTHENTICATION_FAILED |
Invalid credentials | No |
TOKEN_INVALID |
Token revoked | No |
TOKEN_EXPIRED |
Token expired | No |
PERMISSION_DENIED |
Access denied | No |
POST_EDIT_DENIED |
Cannot edit post | No |
CHANNEL_ACCESS_DENIED |
Channel access denied | No |
RESOURCE_NOT_FOUND |
Resource not found | No |
VALIDATION_ERROR |
Invalid input | No |
CONFIGURATION_ERROR |
Bad configuration | No |
MISSING_BOT_TOKEN |
No bot token | No |
MISSING_BASE_URL |
No base URL | No |
NETWORK_ERROR |
Network failure | Yes |
CONNECTION_ERROR |
Cannot connect | Yes |
TIMEOUT_ERROR |
Operation timed out | Yes |
FILE_TOO_LARGE |
File exceeds limit | No |
BLOCKED_FILE_TYPE |
Executable file | No |
DOWNLOAD_FAILED |
Download error | Yes |
Using Error Boundaries
import { globalErrorBoundary, MattermostError } from '@lumbridgecorp/openclaw-mattermost';
// Register error handler
const unsubscribe = globalErrorBoundary.onError((error) => {
if (error instanceof MattermostError) {
console.error(`[${error.code}] ${error.toLogString()}`);
if (error.retryable) {
console.log(`Retry after: ${error.retryAfterMs}ms`);
}
}
});
// Wrap operations
const safeOperation = globalErrorBoundary.wrap(
riskyOperation,
'my-operation',
{ accountId: 'default' }
);
// Later: unsubscribe
disunsubscribe();
Retry Configuration
import { withRetry } from '@lumbridgecorp/openclaw-mattermost';
const result = await withRetry(
'my-operation',
async () => {
// Your operation here
return await someApiCall();
},
{
maxRetries: 5,
initialDelayMs: 1000,
maxDelayMs: 30000,
timeoutMs: 60000,
onRetry: ({ attempt, maxRetries, delayMs, error }) => {
console.log(`Retry ${attempt}/${maxRetries} after ${delayMs}ms: ${error.message}`);
}
}
);
Security Audit
collectMattermostSecurityAuditFindings
Run a comprehensive security audit on your configuration:
import { collectMattermostSecurityAuditFindings } from '@lumbridgecorp/openclaw-mattermost';
const findings = await collectMattermostSecurityAuditFindings({
cfg: OpenClawConfig,
accountId?: string | null,
account: ResolvedMattermostAccount
});
Returns: Array of SecurityAuditFinding:
type SecurityAuditFinding = {
checkId: string; // Unique check identifier
severity: 'info' | 'warn' | 'critical';
title: string;
detail: string;
remediation?: string;
};
Audit Checks
| Check ID | Severity | Description |
|---|---|---|
channels.mattermost.pat.hardcoded_token |
Critical | Bot token hardcoded in config |
channels.mattermost.pat.short_token |
Warn | Token unusually short |
channels.mattermost.pat.missing_token |
Critical | No bot token configured |
channels.mattermost.https.insecure_url |
Critical | HTTP instead of HTTPS |
channels.mattermost.https.missing_url |
Critical | No base URL configured |
channels.mattermost.input.insecure_callback |
Critical | Callback URL uses HTTP |
channels.mattermost.input.no_source_ip_restriction |
Warn | No IP allowlist on callbacks |
channels.mattermost.network.private_access_enabled |
Warn | Private network access enabled |
channels.mattermost.commands.access_groups_disabled |
Critical | Commands bypass access groups |
channels.mattermost.commands.no_allowlist |
Warn | No allowlist for slash commands |
channels.mattermost.config.legacy_dm_policy |
Info | Legacy dm.policy config found |
channels.mattermost.config.legacy_allow_from |
Info | Legacy dm.allowFrom config found |
channels.mattermost.allowlist.mutable_entries |
Warn | Non-ID entries in allowlist |
channels.mattermost.actions.download_file_enabled |
Info | File download enabled |
channels.mattermost.actions.delete_enabled |
Info | Message delete enabled |
channels.mattermost.groups.open_policy |
Warn | Open group policy |
channels.mattermost.dm.open_policy |
Warn | Open DM policy |
channels.mattermost.dm.pairing_mode |
Info | DM pairing mode active |
Example:
import {
collectMattermostSecurityAuditFindings,
collectAllMattermostSecurityAuditFindings
} from '@lumbridgecorp/openclaw-mattermost';
// Audit single account
const findings = await collectMattermostSecurityAuditFindings({
cfg: openclawConfig,
accountId: 'default',
account: resolvedAccount
});
// Audit all accounts
const allFindings = await collectAllMattermostSecurityAuditFindings({
cfg: openclawConfig,
listAccounts: (cfg) => resolveAllAccounts(cfg)
});
// Display findings by severity
const critical = findings.filter(f => f.severity === 'critical');
const warnings = findings.filter(f => f.severity === 'warn');
console.log(`Critical issues: ${critical.length}`);
for (const finding of critical) {
console.log(`\n[!] ${finding.title}`);
console.log(` ${finding.detail}`);
console.log(` Fix: ${finding.remediation}`);
}
Client API
createMattermostClient
Create a low-level Mattermost API client:
import { createMattermostClient } from '@lumbridgecorp/openclaw-mattermost';
const client = createMattermostClient({
baseUrl: string;
botToken: string;
pat?: string;
fetchImpl?: MattermostFetch;
allowPrivateNetwork?: boolean;
accountId?: string;
});
Client Methods:
| Method | Description |
|---|---|
request<T>(path, init?) |
Make authenticated API request |
fetchImpl(input, init?) |
Low-level fetch with guards |
Request Examples:
// Get current user
const me = await client.request<MattermostUser>('/users/me');
// Get a channel
const channel = await client.request<MattermostChannel>(`/channels/${channelId}`);
// Create a post
const post = await client.request<MattermostPost>('/posts', {
method: 'POST',
body: JSON.stringify({
channel_id: channelId,
message: 'Hello World'
})
});
Helper Functions
// User operations
fetchMattermostMe(client) -> MattermostUser
fetchMattermostUser(client, userId) -> MattermostUser
fetchMattermostUserByUsername(client, username) -> MattermostUser
// Channel operations
fetchMattermostChannel(client, channelId) -> MattermostChannel
fetchMattermostChannelByName(client, teamId, channelName) -> MattermostChannel
createMattermostDirectChannel(client, userIds) -> MattermostChannel
createMattermostDirectChannelWithRetry(client, userIds, options) -> MattermostChannel
// Post operations
createMattermostPost(client, params) -> MattermostPost
updateMattermostPost(client, postId, params) -> MattermostPost
// File operations
uploadMattermostFile(client, params) -> MattermostFileInfo
// Other
sendMattermostTyping(client, { channelId, parentId }) -> void
fetchMattermostUserTeams(client, userId) -> MattermostTeam[]
Type Exports
import {
// Config types
MattermostAccountConfig,
MattermostConfig,
SecretInput,
// Client types
MattermostClient,
MattermostFetch,
MattermostUser,
MattermostChannel,
MattermostPost,
MattermostFileInfo,
MattermostTeam,
// Action types
EditMessageParams,
EditMessageResult,
DeleteMessageParams,
DeleteMessageResult,
DownloadFileParams,
DownloadFileResult,
FileMetadata,
// Error types
MattermostError,
MattermostErrorOptions,
ErrorContext,
ErrorCode,
MattermostAPIError,
AuthenticationError,
ValidationError,
PermissionError,
RateLimitError,
ResourceNotFoundError,
NetworkError,
ConfigurationError,
// Security types
SecurityAuditFinding,
// Retry types
RetryConfig,
RetryAttemptInfo
} from '@lumbridgecorp/openclaw-mattermost';