Files
openclaw-mattermost-extension/docs/API.md
T

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 '@karti-ai/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 messages
  • POST_NOT_FOUND - Post was deleted or doesn't exist
  • RATE_LIMITED - Too many edit requests
  • AUTH_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 '@karti-ai/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: true if 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 '@karti-ai/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 baseUrl
  • INVALID_PARAMS - Missing fileId or destinationPath
  • FILE_TOO_LARGE - File exceeds maxSize
  • EMPTY_FILE - File has no content
  • BLOCKED_FILE_TYPE - Executable file extension
  • BLOCKED_MIME_TYPE - MIME type not in allowlist
  • FILE_NOT_FOUND - File doesn't exist
  • PERMISSION_DENIED - No access to file
  • AUTH_FAILED - Invalid token
  • DOWNLOAD_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 '@karti-ai/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 '@karti-ai/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 '@karti-ai/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 clicked
  • style: Button style - default, primary, or danger (optional, defaults to default)

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 option
  • value: 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 '@karti-ai/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 '@karti-ai/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 '@karti-ai/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 '@karti-ai/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 '@karti-ai/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 '@karti-ai/openclaw-mattermost';