{"baseUrl":"https://docs.openclaw.ai","builtAt":"2026-02-03T05:57:13.622Z","chunks":[{"path":"automation/auth-monitoring.md","title":"auth-monitoring","content":"# Auth monitoring\n\nOpenClaw exposes OAuth expiry health via `openclaw models status`. Use that for\nautomation and alerting; scripts are optional extras for phone workflows.","url":"https://docs.openclaw.ai/automation/auth-monitoring"},{"path":"automation/auth-monitoring.md","title":"Preferred: CLI check (portable)","content":"```bash\nopenclaw models status --check\n```\n\nExit codes:\n\n- `0`: OK\n- `1`: expired or missing credentials\n- `2`: expiring soon (within 24h)\n\nThis works in cron/systemd and requires no extra scripts.","url":"https://docs.openclaw.ai/automation/auth-monitoring"},{"path":"automation/auth-monitoring.md","title":"Optional scripts (ops / phone workflows)","content":"These live under `scripts/` and are **optional**. They assume SSH access to the\ngateway host and are tuned for systemd + Termux.\n\n- `scripts/claude-auth-status.sh` now uses `openclaw models status --json` as the\n source of truth (falling back to direct file reads if the CLI is unavailable),\n so keep `openclaw` on `PATH` for timers.\n- `scripts/auth-monitor.sh`: cron/systemd timer target; sends alerts (ntfy or phone).\n- `scripts/systemd/openclaw-auth-monitor.{service,timer}`: systemd user timer.\n- `scripts/claude-auth-status.sh`: Claude Code + OpenClaw auth checker (full/json/simple).\n- `scripts/mobile-reauth.sh`: guided re‑auth flow over SSH.\n- `scripts/termux-quick-auth.sh`: one‑tap widget status + open auth URL.\n- `scripts/termux-auth-widget.sh`: full guided widget flow.\n- `scripts/termux-sync-widget.sh`: sync Claude Code creds → OpenClaw.\n\nIf you don’t need phone automation or systemd timers, skip these scripts.","url":"https://docs.openclaw.ai/automation/auth-monitoring"},{"path":"automation/cron-jobs.md","title":"cron-jobs","content":"# Cron jobs (Gateway scheduler)\n\n> **Cron vs Heartbeat?** See [Cron vs Heartbeat](/automation/cron-vs-heartbeat) for guidance on when to use each.\n\nCron is the Gateway’s built-in scheduler. It persists jobs, wakes the agent at\nthe right time, and can optionally deliver output back to a chat.\n\nIf you want _“run this every morning”_ or _“poke the agent in 20 minutes”_,\ncron is the mechanism.","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"TL;DR","content":"- Cron runs **inside the Gateway** (not inside the model).\n- Jobs persist under `~/.openclaw/cron/` so restarts don’t lose schedules.\n- Two execution styles:\n - **Main session**: enqueue a system event, then run on the next heartbeat.\n - **Isolated**: run a dedicated agent turn in `cron:`, optionally deliver output.\n- Wakeups are first-class: a job can request “wake now” vs “next heartbeat”.","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Quick start (actionable)","content":"Create a one-shot reminder, verify it exists, and run it immediately:\n\n```bash\nopenclaw cron add \\\n --name \"Reminder\" \\\n --at \"2026-02-01T16:00:00Z\" \\\n --session main \\\n --system-event \"Reminder: check the cron docs draft\" \\\n --wake now \\\n --delete-after-run\n\nopenclaw cron list\nopenclaw cron run --force\nopenclaw cron runs --id \n```\n\nSchedule a recurring isolated job with delivery:\n\n```bash\nopenclaw cron add \\\n --name \"Morning brief\" \\\n --cron \"0 7 * * *\" \\\n --tz \"America/Los_Angeles\" \\\n --session isolated \\\n --message \"Summarize overnight updates.\" \\\n --deliver \\\n --channel slack \\\n --to \"channel:C1234567890\"\n```","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Tool-call equivalents (Gateway cron tool)","content":"For the canonical JSON shapes and examples, see [JSON schema for tool calls](/automation/cron-jobs#json-schema-for-tool-calls).","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Where cron jobs are stored","content":"Cron jobs are persisted on the Gateway host at `~/.openclaw/cron/jobs.json` by default.\nThe Gateway loads the file into memory and writes it back on changes, so manual edits\nare only safe when the Gateway is stopped. Prefer `openclaw cron add/edit` or the cron\ntool call API for changes.","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Beginner-friendly overview","content":"Think of a cron job as: **when** to run + **what** to do.\n\n1. **Choose a schedule**\n - One-shot reminder → `schedule.kind = \"at\"` (CLI: `--at`)\n - Repeating job → `schedule.kind = \"every\"` or `schedule.kind = \"cron\"`\n - If your ISO timestamp omits a timezone, it is treated as **UTC**.\n\n2. **Choose where it runs**\n - `sessionTarget: \"main\"` → run during the next heartbeat with main context.\n - `sessionTarget: \"isolated\"` → run a dedicated agent turn in `cron:`.\n\n3. **Choose the payload**\n - Main session → `payload.kind = \"systemEvent\"`\n - Isolated session → `payload.kind = \"agentTurn\"`\n\nOptional: `deleteAfterRun: true` removes successful one-shot jobs from the store.","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Concepts","content":"### Jobs\n\nA cron job is a stored record with:\n\n- a **schedule** (when it should run),\n- a **payload** (what it should do),\n- optional **delivery** (where output should be sent).\n- optional **agent binding** (`agentId`): run the job under a specific agent; if\n missing or unknown, the gateway falls back to the default agent.\n\nJobs are identified by a stable `jobId` (used by CLI/Gateway APIs).\nIn agent tool calls, `jobId` is canonical; legacy `id` is accepted for compatibility.\nJobs can optionally auto-delete after a successful one-shot run via `deleteAfterRun: true`.\n\n### Schedules\n\nCron supports three schedule kinds:\n\n- `at`: one-shot timestamp (ms since epoch). Gateway accepts ISO 8601 and coerces to UTC.\n- `every`: fixed interval (ms).\n- `cron`: 5-field cron expression with optional IANA timezone.\n\nCron expressions use `croner`. If a timezone is omitted, the Gateway host’s\nlocal timezone is used.\n\n### Main vs isolated execution\n\n#### Main session jobs (system events)\n\nMain jobs enqueue a system event and optionally wake the heartbeat runner.\nThey must use `payload.kind = \"systemEvent\"`.\n\n- `wakeMode: \"next-heartbeat\"` (default): event waits for the next scheduled heartbeat.\n- `wakeMode: \"now\"`: event triggers an immediate heartbeat run.\n\nThis is the best fit when you want the normal heartbeat prompt + main-session context.\nSee [Heartbeat](/gateway/heartbeat).\n\n#### Isolated jobs (dedicated cron sessions)\n\nIsolated jobs run a dedicated agent turn in session `cron:`.\n\nKey behaviors:\n\n- Prompt is prefixed with `[cron: ]` for traceability.\n- Each run starts a **fresh session id** (no prior conversation carry-over).\n- A summary is posted to the main session (prefix `Cron`, configurable).\n- `wakeMode: \"now\"` triggers an immediate heartbeat after posting the summary.\n- If `payload.deliver: true`, output is delivered to a channel; otherwise it stays internal.\n\nUse isolated jobs for noisy, frequent, or \"background chores\" that shouldn't spam\nyour main chat history.\n\n### Payload shapes (what runs)\n\nTwo payload kinds are supported:\n\n- `systemEvent`: main-session only, routed through the heartbeat prompt.\n- `agentTurn`: isolated-session only, runs a dedicated agent turn.\n\nCommon `agentTurn` fields:\n\n- `message`: required text prompt.\n- `model` / `thinking`: optional overrides (see below).\n- `timeoutSeconds`: optional timeout override.\n- `deliver`: `true` to send output to a channel target.\n- `channel`: `last` or a specific channel.\n- `to`: channel-specific target (phone/chat/channel id).\n- `bestEffortDeliver`: avoid failing the job if delivery fails.\n\nIsolation options (only for `session=isolated`):\n\n- `postToMainPrefix` (CLI: `--post-prefix`): prefix for the system event in main.\n- `postToMainMode`: `summary` (default) or `full`.\n- `postToMainMaxChars`: max chars when `postToMainMode=full` (default 8000).\n\n### Model and thinking overrides\n\nIsolated jobs (`agentTurn`) can override the model and thinking level:\n\n- `model`: Provider/model string (e.g., `anthropic/claude-sonnet-4-20250514`) or alias (e.g., `opus`)\n- `thinking`: Thinking level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`; GPT-5.2 + Codex models only)\n\nNote: You can set `model` on main-session jobs too, but it changes the shared main\nsession model. We recommend model overrides only for isolated jobs to avoid\nunexpected context shifts.\n\nResolution priority:\n\n1. Job payload override (highest)\n2. Hook-specific defaults (e.g., `hooks.gmail.model`)\n3. Agent config default\n\n### Delivery (channel + target)\n\nIsolated jobs can deliver output to a channel. The job payload can specify:\n\n- `channel`: `whatsapp` / `telegram` / `discord` / `slack` / `mattermost` (plugin) / `signal` / `imessage` / `last`\n- `to`: channel-specific recipient target\n\nIf `channel` or `to` is omitted, cron can fall back to the main session’s “last route”\n(the last place the agent replied).\n\nDelivery notes:\n\n- If `to` is set, cron auto-delivers the agent’s final output even if `deliver` is omitted.\n- Use `deliver: true` when you want last-route delivery without an explicit `to`.\n- Use `deliver: false` to keep output internal even if a `to` is present.\n\nTarget format reminders:\n\n- Slack/Discord/Mattermost (plugin) targets should use explicit prefixes (e.g. `channel:`, `user:`) to avoid ambiguity.\n- Telegram topics should use the `:topic:` form (see below).\n\n#### Telegram delivery targets (topics / forum threads)\n\nTelegram supports forum topics via `message_thread_id`. For cron delivery, you can encode\nthe topic/thread into the `to` field:\n\n- `-1001234567890` (chat id only)\n- `-1001234567890:topic:123` (preferred: explicit topic marker)\n- `-1001234567890:123` (shorthand: numeric suffix)\n\nPrefixed targets like `telegram:...` / `telegram:group:...` are also accepted:\n\n- `telegram:group:-1001234567890:topic:123`","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"JSON schema for tool calls","content":"Use these shapes when calling Gateway `cron.*` tools directly (agent tool calls or RPC).\nCLI flags accept human durations like `20m`, but tool calls use epoch milliseconds for\n`atMs` and `everyMs` (ISO timestamps are accepted for `at` times).\n\n### cron.add params\n\nOne-shot, main session job (system event):\n\n```json\n{\n \"name\": \"Reminder\",\n \"schedule\": { \"kind\": \"at\", \"atMs\": 1738262400000 },\n \"sessionTarget\": \"main\",\n \"wakeMode\": \"now\",\n \"payload\": { \"kind\": \"systemEvent\", \"text\": \"Reminder text\" },\n \"deleteAfterRun\": true\n}\n```\n\nRecurring, isolated job with delivery:\n\n```json\n{\n \"name\": \"Morning brief\",\n \"schedule\": { \"kind\": \"cron\", \"expr\": \"0 7 * * *\", \"tz\": \"America/Los_Angeles\" },\n \"sessionTarget\": \"isolated\",\n \"wakeMode\": \"next-heartbeat\",\n \"payload\": {\n \"kind\": \"agentTurn\",\n \"message\": \"Summarize overnight updates.\",\n \"deliver\": true,\n \"channel\": \"slack\",\n \"to\": \"channel:C1234567890\",\n \"bestEffortDeliver\": true\n },\n \"isolation\": { \"postToMainPrefix\": \"Cron\", \"postToMainMode\": \"summary\" }\n}\n```\n\nNotes:\n\n- `schedule.kind`: `at` (`atMs`), `every` (`everyMs`), or `cron` (`expr`, optional `tz`).\n- `atMs` and `everyMs` are epoch milliseconds.\n- `sessionTarget` must be `\"main\"` or `\"isolated\"` and must match `payload.kind`.\n- Optional fields: `agentId`, `description`, `enabled`, `deleteAfterRun`, `isolation`.\n- `wakeMode` defaults to `\"next-heartbeat\"` when omitted.\n\n### cron.update params\n\n```json\n{\n \"jobId\": \"job-123\",\n \"patch\": {\n \"enabled\": false,\n \"schedule\": { \"kind\": \"every\", \"everyMs\": 3600000 }\n }\n}\n```\n\nNotes:\n\n- `jobId` is canonical; `id` is accepted for compatibility.\n- Use `agentId: null` in the patch to clear an agent binding.\n\n### cron.run and cron.remove params\n\n```json\n{ \"jobId\": \"job-123\", \"mode\": \"force\" }\n```\n\n```json\n{ \"jobId\": \"job-123\" }\n```","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Storage & history","content":"- Job store: `~/.openclaw/cron/jobs.json` (Gateway-managed JSON).\n- Run history: `~/.openclaw/cron/runs/.jsonl` (JSONL, auto-pruned).\n- Override store path: `cron.store` in config.","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Configuration","content":"```json5\n{\n cron: {\n enabled: true, // default true\n store: \"~/.openclaw/cron/jobs.json\",\n maxConcurrentRuns: 1, // default 1\n },\n}\n```\n\nDisable cron entirely:\n\n- `cron.enabled: false` (config)\n- `OPENCLAW_SKIP_CRON=1` (env)","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"CLI quickstart","content":"One-shot reminder (UTC ISO, auto-delete after success):\n\n```bash\nopenclaw cron add \\\n --name \"Send reminder\" \\\n --at \"2026-01-12T18:00:00Z\" \\\n --session main \\\n --system-event \"Reminder: submit expense report.\" \\\n --wake now \\\n --delete-after-run\n```\n\nOne-shot reminder (main session, wake immediately):\n\n```bash\nopenclaw cron add \\\n --name \"Calendar check\" \\\n --at \"20m\" \\\n --session main \\\n --system-event \"Next heartbeat: check calendar.\" \\\n --wake now\n```\n\nRecurring isolated job (deliver to WhatsApp):\n\n```bash\nopenclaw cron add \\\n --name \"Morning status\" \\\n --cron \"0 7 * * *\" \\\n --tz \"America/Los_Angeles\" \\\n --session isolated \\\n --message \"Summarize inbox + calendar for today.\" \\\n --deliver \\\n --channel whatsapp \\\n --to \"+15551234567\"\n```\n\nRecurring isolated job (deliver to a Telegram topic):\n\n```bash\nopenclaw cron add \\\n --name \"Nightly summary (topic)\" \\\n --cron \"0 22 * * *\" \\\n --tz \"America/Los_Angeles\" \\\n --session isolated \\\n --message \"Summarize today; send to the nightly topic.\" \\\n --deliver \\\n --channel telegram \\\n --to \"-1001234567890:topic:123\"\n```\n\nIsolated job with model and thinking override:\n\n```bash\nopenclaw cron add \\\n --name \"Deep analysis\" \\\n --cron \"0 6 * * 1\" \\\n --tz \"America/Los_Angeles\" \\\n --session isolated \\\n --message \"Weekly deep analysis of project progress.\" \\\n --model \"opus\" \\\n --thinking high \\\n --deliver \\\n --channel whatsapp \\\n --to \"+15551234567\"\n```\n\nAgent selection (multi-agent setups):\n\n```bash\n# Pin a job to agent \"ops\" (falls back to default if that agent is missing)\nopenclaw cron add --name \"Ops sweep\" --cron \"0 6 * * *\" --session isolated --message \"Check ops queue\" --agent ops\n\n# Switch or clear the agent on an existing job\nopenclaw cron edit --agent ops\nopenclaw cron edit --clear-agent\n```\n\nManual run (debug):\n\n```bash\nopenclaw cron run --force\n```\n\nEdit an existing job (patch fields):\n\n```bash\nopenclaw cron edit \\\n --message \"Updated prompt\" \\\n --model \"opus\" \\\n --thinking low\n```\n\nRun history:\n\n```bash\nopenclaw cron runs --id --limit 50\n```\n\nImmediate system event without creating a job:\n\n```bash\nopenclaw system event --mode now --text \"Next heartbeat: check battery.\"\n```","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Gateway API surface","content":"- `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove`\n- `cron.run` (force or due), `cron.runs`\n For immediate system events without a job, use [`openclaw system event`](/cli/system).","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-jobs.md","title":"Troubleshooting","content":"### “Nothing runs”\n\n- Check cron is enabled: `cron.enabled` and `OPENCLAW_SKIP_CRON`.\n- Check the Gateway is running continuously (cron runs inside the Gateway process).\n- For `cron` schedules: confirm timezone (`--tz`) vs the host timezone.\n\n### Telegram delivers to the wrong place\n\n- For forum topics, use `-100…:topic:` so it’s explicit and unambiguous.\n- If you see `telegram:...` prefixes in logs or stored “last route” targets, that’s normal;\n cron delivery accepts them and still parses topic IDs correctly.","url":"https://docs.openclaw.ai/automation/cron-jobs"},{"path":"automation/cron-vs-heartbeat.md","title":"cron-vs-heartbeat","content":"# Cron vs Heartbeat: When to Use Each\n\nBoth heartbeats and cron jobs let you run tasks on a schedule. This guide helps you choose the right mechanism for your use case.","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Quick Decision Guide","content":"| Use Case | Recommended | Why |\n| ------------------------------------ | ------------------- | ---------------------------------------- |\n| Check inbox every 30 min | Heartbeat | Batches with other checks, context-aware |\n| Send daily report at 9am sharp | Cron (isolated) | Exact timing needed |\n| Monitor calendar for upcoming events | Heartbeat | Natural fit for periodic awareness |\n| Run weekly deep analysis | Cron (isolated) | Standalone task, can use different model |\n| Remind me in 20 minutes | Cron (main, `--at`) | One-shot with precise timing |\n| Background project health check | Heartbeat | Piggybacks on existing cycle |","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Heartbeat: Periodic Awareness","content":"Heartbeats run in the **main session** at a regular interval (default: 30 min). They're designed for the agent to check on things and surface anything important.\n\n### When to use heartbeat\n\n- **Multiple periodic checks**: Instead of 5 separate cron jobs checking inbox, calendar, weather, notifications, and project status, a single heartbeat can batch all of these.\n- **Context-aware decisions**: The agent has full main-session context, so it can make smart decisions about what's urgent vs. what can wait.\n- **Conversational continuity**: Heartbeat runs share the same session, so the agent remembers recent conversations and can follow up naturally.\n- **Low-overhead monitoring**: One heartbeat replaces many small polling tasks.\n\n### Heartbeat advantages\n\n- **Batches multiple checks**: One agent turn can review inbox, calendar, and notifications together.\n- **Reduces API calls**: A single heartbeat is cheaper than 5 isolated cron jobs.\n- **Context-aware**: The agent knows what you've been working on and can prioritize accordingly.\n- **Smart suppression**: If nothing needs attention, the agent replies `HEARTBEAT_OK` and no message is delivered.\n- **Natural timing**: Drifts slightly based on queue load, which is fine for most monitoring.\n\n### Heartbeat example: HEARTBEAT.md checklist\n\n```md\n# Heartbeat checklist\n\n- Check email for urgent messages\n- Review calendar for events in next 2 hours\n- If a background task finished, summarize results\n- If idle for 8+ hours, send a brief check-in\n```\n\nThe agent reads this on each heartbeat and handles all items in one turn.\n\n### Configuring heartbeat\n\n```json5\n{\n agents: {\n defaults: {\n heartbeat: {\n every: \"30m\", // interval\n target: \"last\", // where to deliver alerts\n activeHours: { start: \"08:00\", end: \"22:00\" }, // optional\n },\n },\n },\n}\n```\n\nSee [Heartbeat](/gateway/heartbeat) for full configuration.","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Cron: Precise Scheduling","content":"Cron jobs run at **exact times** and can run in isolated sessions without affecting main context.\n\n### When to use cron\n\n- **Exact timing required**: \"Send this at 9:00 AM every Monday\" (not \"sometime around 9\").\n- **Standalone tasks**: Tasks that don't need conversational context.\n- **Different model/thinking**: Heavy analysis that warrants a more powerful model.\n- **One-shot reminders**: \"Remind me in 20 minutes\" with `--at`.\n- **Noisy/frequent tasks**: Tasks that would clutter main session history.\n- **External triggers**: Tasks that should run independently of whether the agent is otherwise active.\n\n### Cron advantages\n\n- **Exact timing**: 5-field cron expressions with timezone support.\n- **Session isolation**: Runs in `cron:` without polluting main history.\n- **Model overrides**: Use a cheaper or more powerful model per job.\n- **Delivery control**: Can deliver directly to a channel; still posts a summary to main by default (configurable).\n- **No agent context needed**: Runs even if main session is idle or compacted.\n- **One-shot support**: `--at` for precise future timestamps.\n\n### Cron example: Daily morning briefing\n\n```bash\nopenclaw cron add \\\n --name \"Morning briefing\" \\\n --cron \"0 7 * * *\" \\\n --tz \"America/New_York\" \\\n --session isolated \\\n --message \"Generate today's briefing: weather, calendar, top emails, news summary.\" \\\n --model opus \\\n --deliver \\\n --channel whatsapp \\\n --to \"+15551234567\"\n```\n\nThis runs at exactly 7:00 AM New York time, uses Opus for quality, and delivers directly to WhatsApp.\n\n### Cron example: One-shot reminder\n\n```bash\nopenclaw cron add \\\n --name \"Meeting reminder\" \\\n --at \"20m\" \\\n --session main \\\n --system-event \"Reminder: standup meeting starts in 10 minutes.\" \\\n --wake now \\\n --delete-after-run\n```\n\nSee [Cron jobs](/automation/cron-jobs) for full CLI reference.","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Decision Flowchart","content":"```\nDoes the task need to run at an EXACT time?\n YES -> Use cron\n NO -> Continue...\n\nDoes the task need isolation from main session?\n YES -> Use cron (isolated)\n NO -> Continue...\n\nCan this task be batched with other periodic checks?\n YES -> Use heartbeat (add to HEARTBEAT.md)\n NO -> Use cron\n\nIs this a one-shot reminder?\n YES -> Use cron with --at\n NO -> Continue...\n\nDoes it need a different model or thinking level?\n YES -> Use cron (isolated) with --model/--thinking\n NO -> Use heartbeat\n```","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Combining Both","content":"The most efficient setup uses **both**:\n\n1. **Heartbeat** handles routine monitoring (inbox, calendar, notifications) in one batched turn every 30 minutes.\n2. **Cron** handles precise schedules (daily reports, weekly reviews) and one-shot reminders.\n\n### Example: Efficient automation setup\n\n**HEARTBEAT.md** (checked every 30 min):\n\n```md\n# Heartbeat checklist\n\n- Scan inbox for urgent emails\n- Check calendar for events in next 2h\n- Review any pending tasks\n- Light check-in if quiet for 8+ hours\n```\n\n**Cron jobs** (precise timing):\n\n```bash\n# Daily morning briefing at 7am\nopenclaw cron add --name \"Morning brief\" --cron \"0 7 * * *\" --session isolated --message \"...\" --deliver\n\n# Weekly project review on Mondays at 9am\nopenclaw cron add --name \"Weekly review\" --cron \"0 9 * * 1\" --session isolated --message \"...\" --model opus\n\n# One-shot reminder\nopenclaw cron add --name \"Call back\" --at \"2h\" --session main --system-event \"Call back the client\" --wake now\n```","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Lobster: Deterministic workflows with approvals","content":"Lobster is the workflow runtime for **multi-step tool pipelines** that need deterministic execution and explicit approvals.\nUse it when the task is more than a single agent turn, and you want a resumable workflow with human checkpoints.\n\n### When Lobster fits\n\n- **Multi-step automation**: You need a fixed pipeline of tool calls, not a one-off prompt.\n- **Approval gates**: Side effects should pause until you approve, then resume.\n- **Resumable runs**: Continue a paused workflow without re-running earlier steps.\n\n### How it pairs with heartbeat and cron\n\n- **Heartbeat/cron** decide _when_ a run happens.\n- **Lobster** defines _what steps_ happen once the run starts.\n\nFor scheduled workflows, use cron or heartbeat to trigger an agent turn that calls Lobster.\nFor ad-hoc workflows, call Lobster directly.\n\n### Operational notes (from the code)\n\n- Lobster runs as a **local subprocess** (`lobster` CLI) in tool mode and returns a **JSON envelope**.\n- If the tool returns `needs_approval`, you resume with a `resumeToken` and `approve` flag.\n- The tool is an **optional plugin**; enable it additively via `tools.alsoAllow: [\"lobster\"]` (recommended).\n- If you pass `lobsterPath`, it must be an **absolute path**.\n\nSee [Lobster](/tools/lobster) for full usage and examples.","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Main Session vs Isolated Session","content":"Both heartbeat and cron can interact with the main session, but differently:\n\n| | Heartbeat | Cron (main) | Cron (isolated) |\n| ------- | ------------------------------- | ------------------------ | ---------------------- |\n| Session | Main | Main (via system event) | `cron:` |\n| History | Shared | Shared | Fresh each run |\n| Context | Full | Full | None (starts clean) |\n| Model | Main session model | Main session model | Can override |\n| Output | Delivered if not `HEARTBEAT_OK` | Heartbeat prompt + event | Summary posted to main |\n\n### When to use main session cron\n\nUse `--session main` with `--system-event` when you want:\n\n- The reminder/event to appear in main session context\n- The agent to handle it during the next heartbeat with full context\n- No separate isolated run\n\n```bash\nopenclaw cron add \\\n --name \"Check project\" \\\n --every \"4h\" \\\n --session main \\\n --system-event \"Time for a project health check\" \\\n --wake now\n```\n\n### When to use isolated cron\n\nUse `--session isolated` when you want:\n\n- A clean slate without prior context\n- Different model or thinking settings\n- Output delivered directly to a channel (summary still posts to main by default)\n- History that doesn't clutter main session\n\n```bash\nopenclaw cron add \\\n --name \"Deep analysis\" \\\n --cron \"0 6 * * 0\" \\\n --session isolated \\\n --message \"Weekly codebase analysis...\" \\\n --model opus \\\n --thinking high \\\n --deliver\n```","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Cost Considerations","content":"| Mechanism | Cost Profile |\n| --------------- | ------------------------------------------------------- |\n| Heartbeat | One turn every N minutes; scales with HEARTBEAT.md size |\n| Cron (main) | Adds event to next heartbeat (no isolated turn) |\n| Cron (isolated) | Full agent turn per job; can use cheaper model |\n\n**Tips**:\n\n- Keep `HEARTBEAT.md` small to minimize token overhead.\n- Batch similar checks into heartbeat instead of multiple cron jobs.\n- Use `target: \"none\"` on heartbeat if you only want internal processing.\n- Use isolated cron with a cheaper model for routine tasks.","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/cron-vs-heartbeat.md","title":"Related","content":"- [Heartbeat](/gateway/heartbeat) - full heartbeat configuration\n- [Cron jobs](/automation/cron-jobs) - full cron CLI and API reference\n- [System](/cli/system) - system events + heartbeat controls","url":"https://docs.openclaw.ai/automation/cron-vs-heartbeat"},{"path":"automation/gmail-pubsub.md","title":"gmail-pubsub","content":"# Gmail Pub/Sub -> OpenClaw\n\nGoal: Gmail watch -> Pub/Sub push -> `gog gmail watch serve` -> OpenClaw webhook.","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Prereqs","content":"- `gcloud` installed and logged in ([install guide](https://docs.cloud.google.com/sdk/docs/install-sdk)).\n- `gog` (gogcli) installed and authorized for the Gmail account ([gogcli.sh](https://gogcli.sh/)).\n- OpenClaw hooks enabled (see [Webhooks](/automation/webhook)).\n- `tailscale` logged in ([tailscale.com](https://tailscale.com/)). Supported setup uses Tailscale Funnel for the public HTTPS endpoint.\n Other tunnel services can work, but are DIY/unsupported and require manual wiring.\n Right now, Tailscale is what we support.\n\nExample hook config (enable Gmail preset mapping):\n\n```json5\n{\n hooks: {\n enabled: true,\n token: \"OPENCLAW_HOOK_TOKEN\",\n path: \"/hooks\",\n presets: [\"gmail\"],\n },\n}\n```\n\nTo deliver the Gmail summary to a chat surface, override the preset with a mapping\nthat sets `deliver` + optional `channel`/`to`:\n\n```json5\n{\n hooks: {\n enabled: true,\n token: \"OPENCLAW_HOOK_TOKEN\",\n presets: [\"gmail\"],\n mappings: [\n {\n match: { path: \"gmail\" },\n action: \"agent\",\n wakeMode: \"now\",\n name: \"Gmail\",\n sessionKey: \"hook:gmail:{{messages[0].id}}\",\n messageTemplate: \"New email from {{messages[0].from}}\\nSubject: {{messages[0].subject}}\\n{{messages[0].snippet}}\\n{{messages[0].body}}\",\n model: \"openai/gpt-5.2-mini\",\n deliver: true,\n channel: \"last\",\n // to: \"+15551234567\"\n },\n ],\n },\n}\n```\n\nIf you want a fixed channel, set `channel` + `to`. Otherwise `channel: \"last\"`\nuses the last delivery route (falls back to WhatsApp).\n\nTo force a cheaper model for Gmail runs, set `model` in the mapping\n(`provider/model` or alias). If you enforce `agents.defaults.models`, include it there.\n\nTo set a default model and thinking level specifically for Gmail hooks, add\n`hooks.gmail.model` / `hooks.gmail.thinking` in your config:\n\n```json5\n{\n hooks: {\n gmail: {\n model: \"openrouter/meta-llama/llama-3.3-70b-instruct:free\",\n thinking: \"off\",\n },\n },\n}\n```\n\nNotes:\n\n- Per-hook `model`/`thinking` in the mapping still overrides these defaults.\n- Fallback order: `hooks.gmail.model` → `agents.defaults.model.fallbacks` → primary (auth/rate-limit/timeouts).\n- If `agents.defaults.models` is set, the Gmail model must be in the allowlist.\n- Gmail hook content is wrapped with external-content safety boundaries by default.\n To disable (dangerous), set `hooks.gmail.allowUnsafeExternalContent: true`.\n\nTo customize payload handling further, add `hooks.mappings` or a JS/TS transform module\nunder `hooks.transformsDir` (see [Webhooks](/automation/webhook)).","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Wizard (recommended)","content":"Use the OpenClaw helper to wire everything together (installs deps on macOS via brew):\n\n```bash\nopenclaw webhooks gmail setup \\\n --account openclaw@gmail.com\n```\n\nDefaults:\n\n- Uses Tailscale Funnel for the public push endpoint.\n- Writes `hooks.gmail` config for `openclaw webhooks gmail run`.\n- Enables the Gmail hook preset (`hooks.presets: [\"gmail\"]`).\n\nPath note: when `tailscale.mode` is enabled, OpenClaw automatically sets\n`hooks.gmail.serve.path` to `/` and keeps the public path at\n`hooks.gmail.tailscale.path` (default `/gmail-pubsub`) because Tailscale\nstrips the set-path prefix before proxying.\nIf you need the backend to receive the prefixed path, set\n`hooks.gmail.tailscale.target` (or `--tailscale-target`) to a full URL like\n`http://127.0.0.1:8788/gmail-pubsub` and match `hooks.gmail.serve.path`.\n\nWant a custom endpoint? Use `--push-endpoint ` or `--tailscale off`.\n\nPlatform note: on macOS the wizard installs `gcloud`, `gogcli`, and `tailscale`\nvia Homebrew; on Linux install them manually first.\n\nGateway auto-start (recommended):\n\n- When `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts\n `gog gmail watch serve` on boot and auto-renews the watch.\n- Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to opt out (useful if you run the daemon yourself).\n- Do not run the manual daemon at the same time, or you will hit\n `listen tcp 127.0.0.1:8788: bind: address already in use`.\n\nManual daemon (starts `gog gmail watch serve` + auto-renew):\n\n```bash\nopenclaw webhooks gmail run\n```","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"One-time setup","content":"1. Select the GCP project **that owns the OAuth client** used by `gog`.\n\n```bash\ngcloud auth login\ngcloud config set project \n```\n\nNote: Gmail watch requires the Pub/Sub topic to live in the same project as the OAuth client.\n\n2. Enable APIs:\n\n```bash\ngcloud services enable gmail.googleapis.com pubsub.googleapis.com\n```\n\n3. Create a topic:\n\n```bash\ngcloud pubsub topics create gog-gmail-watch\n```\n\n4. Allow Gmail push to publish:\n\n```bash\ngcloud pubsub topics add-iam-policy-binding gog-gmail-watch \\\n --member=serviceAccount:gmail-api-push@system.gserviceaccount.com \\\n --role=roles/pubsub.publisher\n```","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Start the watch","content":"```bash\ngog gmail watch start \\\n --account openclaw@gmail.com \\\n --label INBOX \\\n --topic projects//topics/gog-gmail-watch\n```\n\nSave the `history_id` from the output (for debugging).","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Run the push handler","content":"Local example (shared token auth):\n\n```bash\ngog gmail watch serve \\\n --account openclaw@gmail.com \\\n --bind 127.0.0.1 \\\n --port 8788 \\\n --path /gmail-pubsub \\\n --token \\\n --hook-url http://127.0.0.1:18789/hooks/gmail \\\n --hook-token OPENCLAW_HOOK_TOKEN \\\n --include-body \\\n --max-bytes 20000\n```\n\nNotes:\n\n- `--token` protects the push endpoint (`x-gog-token` or `?token=`).\n- `--hook-url` points to OpenClaw `/hooks/gmail` (mapped; isolated run + summary to main).\n- `--include-body` and `--max-bytes` control the body snippet sent to OpenClaw.\n\nRecommended: `openclaw webhooks gmail run` wraps the same flow and auto-renews the watch.","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Expose the handler (advanced, unsupported)","content":"If you need a non-Tailscale tunnel, wire it manually and use the public URL in the push\nsubscription (unsupported, no guardrails):\n\n```bash\ncloudflared tunnel --url http://127.0.0.1:8788 --no-autoupdate\n```\n\nUse the generated URL as the push endpoint:\n\n```bash\ngcloud pubsub subscriptions create gog-gmail-watch-push \\\n --topic gog-gmail-watch \\\n --push-endpoint \"https:///gmail-pubsub?token=\"\n```\n\nProduction: use a stable HTTPS endpoint and configure Pub/Sub OIDC JWT, then run:\n\n```bash\ngog gmail watch serve --verify-oidc --oidc-email \n```","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Test","content":"Send a message to the watched inbox:\n\n```bash\ngog gmail send \\\n --account openclaw@gmail.com \\\n --to openclaw@gmail.com \\\n --subject \"watch test\" \\\n --body \"ping\"\n```\n\nCheck watch state and history:\n\n```bash\ngog gmail watch status --account openclaw@gmail.com\ngog gmail history --account openclaw@gmail.com --since \n```","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Troubleshooting","content":"- `Invalid topicName`: project mismatch (topic not in the OAuth client project).\n- `User not authorized`: missing `roles/pubsub.publisher` on the topic.\n- Empty messages: Gmail push only provides `historyId`; fetch via `gog gmail history`.","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/gmail-pubsub.md","title":"Cleanup","content":"```bash\ngog gmail watch stop --account openclaw@gmail.com\ngcloud pubsub subscriptions delete gog-gmail-watch-push\ngcloud pubsub topics delete gog-gmail-watch\n```","url":"https://docs.openclaw.ai/automation/gmail-pubsub"},{"path":"automation/poll.md","title":"poll","content":"# Polls","url":"https://docs.openclaw.ai/automation/poll"},{"path":"automation/poll.md","title":"Supported channels","content":"- WhatsApp (web channel)\n- Discord\n- MS Teams (Adaptive Cards)","url":"https://docs.openclaw.ai/automation/poll"},{"path":"automation/poll.md","title":"CLI","content":"```bash\n# WhatsApp\nopenclaw message poll --target +15555550123 \\\n --poll-question \"Lunch today?\" --poll-option \"Yes\" --poll-option \"No\" --poll-option \"Maybe\"\nopenclaw message poll --target 123456789@g.us \\\n --poll-question \"Meeting time?\" --poll-option \"10am\" --poll-option \"2pm\" --poll-option \"4pm\" --poll-multi\n\n# Discord\nopenclaw message poll --channel discord --target channel:123456789 \\\n --poll-question \"Snack?\" --poll-option \"Pizza\" --poll-option \"Sushi\"\nopenclaw message poll --channel discord --target channel:123456789 \\\n --poll-question \"Plan?\" --poll-option \"A\" --poll-option \"B\" --poll-duration-hours 48\n\n# MS Teams\nopenclaw message poll --channel msteams --target conversation:19:abc@thread.tacv2 \\\n --poll-question \"Lunch?\" --poll-option \"Pizza\" --poll-option \"Sushi\"\n```\n\nOptions:\n\n- `--channel`: `whatsapp` (default), `discord`, or `msteams`\n- `--poll-multi`: allow selecting multiple options\n- `--poll-duration-hours`: Discord-only (defaults to 24 when omitted)","url":"https://docs.openclaw.ai/automation/poll"},{"path":"automation/poll.md","title":"Gateway RPC","content":"Method: `poll`\n\nParams:\n\n- `to` (string, required)\n- `question` (string, required)\n- `options` (string[], required)\n- `maxSelections` (number, optional)\n- `durationHours` (number, optional)\n- `channel` (string, optional, default: `whatsapp`)\n- `idempotencyKey` (string, required)","url":"https://docs.openclaw.ai/automation/poll"},{"path":"automation/poll.md","title":"Channel differences","content":"- WhatsApp: 2-12 options, `maxSelections` must be within option count, ignores `durationHours`.\n- Discord: 2-10 options, `durationHours` clamped to 1-768 hours (default 24). `maxSelections > 1` enables multi-select; Discord does not support a strict selection count.\n- MS Teams: Adaptive Card polls (OpenClaw-managed). No native poll API; `durationHours` is ignored.","url":"https://docs.openclaw.ai/automation/poll"},{"path":"automation/poll.md","title":"Agent tool (Message)","content":"Use the `message` tool with `poll` action (`to`, `pollQuestion`, `pollOption`, optional `pollMulti`, `pollDurationHours`, `channel`).\n\nNote: Discord has no “pick exactly N” mode; `pollMulti` maps to multi-select.\nTeams polls are rendered as Adaptive Cards and require the gateway to stay online\nto record votes in `~/.openclaw/msteams-polls.json`.","url":"https://docs.openclaw.ai/automation/poll"},{"path":"automation/webhook.md","title":"webhook","content":"# Webhooks\n\nGateway can expose a small HTTP webhook endpoint for external triggers.","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"automation/webhook.md","title":"Enable","content":"```json5\n{\n hooks: {\n enabled: true,\n token: \"shared-secret\",\n path: \"/hooks\",\n },\n}\n```\n\nNotes:\n\n- `hooks.token` is required when `hooks.enabled=true`.\n- `hooks.path` defaults to `/hooks`.","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"automation/webhook.md","title":"Auth","content":"Every request must include the hook token. Prefer headers:\n\n- `Authorization: Bearer ` (recommended)\n- `x-openclaw-token: `\n- `?token=` (deprecated; logs a warning and will be removed in a future major release)","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"automation/webhook.md","title":"Endpoints","content":"### `POST /hooks/wake`\n\nPayload:\n\n```json\n{ \"text\": \"System line\", \"mode\": \"now\" }\n```\n\n- `text` **required** (string): The description of the event (e.g., \"New email received\").\n- `mode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check.\n\nEffect:\n\n- Enqueues a system event for the **main** session\n- If `mode=now`, triggers an immediate heartbeat\n\n### `POST /hooks/agent`\n\nPayload:\n\n```json\n{\n \"message\": \"Run this\",\n \"name\": \"Email\",\n \"sessionKey\": \"hook:email:msg-123\",\n \"wakeMode\": \"now\",\n \"deliver\": true,\n \"channel\": \"last\",\n \"to\": \"+15551234567\",\n \"model\": \"openai/gpt-5.2-mini\",\n \"thinking\": \"low\",\n \"timeoutSeconds\": 120\n}\n```\n\n- `message` **required** (string): The prompt or message for the agent to process.\n- `name` optional (string): Human-readable name for the hook (e.g., \"GitHub\"), used as a prefix in session summaries.\n- `sessionKey` optional (string): The key used to identify the agent's session. Defaults to a random `hook:`. Using a consistent key allows for a multi-turn conversation within the hook context.\n- `wakeMode` optional (`now` | `next-heartbeat`): Whether to trigger an immediate heartbeat (default `now`) or wait for the next periodic check.\n- `deliver` optional (boolean): If `true`, the agent's response will be sent to the messaging channel. Defaults to `true`. Responses that are only heartbeat acknowledgments are automatically skipped.\n- `channel` optional (string): The messaging channel for delivery. One of: `last`, `whatsapp`, `telegram`, `discord`, `slack`, `mattermost` (plugin), `signal`, `imessage`, `msteams`. Defaults to `last`.\n- `to` optional (string): The recipient identifier for the channel (e.g., phone number for WhatsApp/Signal, chat ID for Telegram, channel ID for Discord/Slack/Mattermost (plugin), conversation ID for MS Teams). Defaults to the last recipient in the main session.\n- `model` optional (string): Model override (e.g., `anthropic/claude-3-5-sonnet` or an alias). Must be in the allowed model list if restricted.\n- `thinking` optional (string): Thinking level override (e.g., `low`, `medium`, `high`).\n- `timeoutSeconds` optional (number): Maximum duration for the agent run in seconds.\n\nEffect:\n\n- Runs an **isolated** agent turn (own session key)\n- Always posts a summary into the **main** session\n- If `wakeMode=now`, triggers an immediate heartbeat\n\n### `POST /hooks/` (mapped)\n\nCustom hook names are resolved via `hooks.mappings` (see configuration). A mapping can\nturn arbitrary payloads into `wake` or `agent` actions, with optional templates or\ncode transforms.\n\nMapping options (summary):\n\n- `hooks.presets: [\"gmail\"]` enables the built-in Gmail mapping.\n- `hooks.mappings` lets you define `match`, `action`, and templates in config.\n- `hooks.transformsDir` + `transform.module` loads a JS/TS module for custom logic.\n- Use `match.source` to keep a generic ingest endpoint (payload-driven routing).\n- TS transforms require a TS loader (e.g. `bun` or `tsx`) or precompiled `.js` at runtime.\n- Set `deliver: true` + `channel`/`to` on mappings to route replies to a chat surface\n (`channel` defaults to `last` and falls back to WhatsApp).\n- `allowUnsafeExternalContent: true` disables the external content safety wrapper for that hook\n (dangerous; only for trusted internal sources).\n- `openclaw webhooks gmail setup` writes `hooks.gmail` config for `openclaw webhooks gmail run`.\n See [Gmail Pub/Sub](/automation/gmail-pubsub) for the full Gmail watch flow.","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"automation/webhook.md","title":"Responses","content":"- `200` for `/hooks/wake`\n- `202` for `/hooks/agent` (async run started)\n- `401` on auth failure\n- `400` on invalid payload\n- `413` on oversized payloads","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"automation/webhook.md","title":"Examples","content":"```bash\ncurl -X POST http://127.0.0.1:18789/hooks/wake \\\n -H 'Authorization: Bearer SECRET' \\\n -H 'Content-Type: application/json' \\\n -d '{\"text\":\"New email received\",\"mode\":\"now\"}'\n```\n\n```bash\ncurl -X POST http://127.0.0.1:18789/hooks/agent \\\n -H 'x-openclaw-token: SECRET' \\\n -H 'Content-Type: application/json' \\\n -d '{\"message\":\"Summarize inbox\",\"name\":\"Email\",\"wakeMode\":\"next-heartbeat\"}'\n```\n\n### Use a different model\n\nAdd `model` to the agent payload (or mapping) to override the model for that run:\n\n```bash\ncurl -X POST http://127.0.0.1:18789/hooks/agent \\\n -H 'x-openclaw-token: SECRET' \\\n -H 'Content-Type: application/json' \\\n -d '{\"message\":\"Summarize inbox\",\"name\":\"Email\",\"model\":\"openai/gpt-5.2-mini\"}'\n```\n\nIf you enforce `agents.defaults.models`, make sure the override model is included there.\n\n```bash\ncurl -X POST http://127.0.0.1:18789/hooks/gmail \\\n -H 'Authorization: Bearer SECRET' \\\n -H 'Content-Type: application/json' \\\n -d '{\"source\":\"gmail\",\"messages\":[{\"from\":\"Ada\",\"subject\":\"Hello\",\"snippet\":\"Hi\"}]}'\n```","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"automation/webhook.md","title":"Security","content":"- Keep hook endpoints behind loopback, tailnet, or trusted reverse proxy.\n- Use a dedicated hook token; do not reuse gateway auth tokens.\n- Avoid including sensitive raw payloads in webhook logs.\n- Hook payloads are treated as untrusted and wrapped with safety boundaries by default.\n If you must disable this for a specific hook, set `allowUnsafeExternalContent: true`\n in that hook's mapping (dangerous).","url":"https://docs.openclaw.ai/automation/webhook"},{"path":"bedrock.md","title":"bedrock","content":"# Amazon Bedrock\n\nOpenClaw can use **Amazon Bedrock** models via pi‑ai’s **Bedrock Converse**\nstreaming provider. Bedrock auth uses the **AWS SDK default credential chain**,\nnot an API key.","url":"https://docs.openclaw.ai/bedrock"},{"path":"bedrock.md","title":"What pi‑ai supports","content":"- Provider: `amazon-bedrock`\n- API: `bedrock-converse-stream`\n- Auth: AWS credentials (env vars, shared config, or instance role)\n- Region: `AWS_REGION` or `AWS_DEFAULT_REGION` (default: `us-east-1`)","url":"https://docs.openclaw.ai/bedrock"},{"path":"bedrock.md","title":"Automatic model discovery","content":"If AWS credentials are detected, OpenClaw can automatically discover Bedrock\nmodels that support **streaming** and **text output**. Discovery uses\n`bedrock:ListFoundationModels` and is cached (default: 1 hour).\n\nConfig options live under `models.bedrockDiscovery`:\n\n```json5\n{\n models: {\n bedrockDiscovery: {\n enabled: true,\n region: \"us-east-1\",\n providerFilter: [\"anthropic\", \"amazon\"],\n refreshInterval: 3600,\n defaultContextWindow: 32000,\n defaultMaxTokens: 4096,\n },\n },\n}\n```\n\nNotes:\n\n- `enabled` defaults to `true` when AWS credentials are present.\n- `region` defaults to `AWS_REGION` or `AWS_DEFAULT_REGION`, then `us-east-1`.\n- `providerFilter` matches Bedrock provider names (for example `anthropic`).\n- `refreshInterval` is seconds; set to `0` to disable caching.\n- `defaultContextWindow` (default: `32000`) and `defaultMaxTokens` (default: `4096`)\n are used for discovered models (override if you know your model limits).","url":"https://docs.openclaw.ai/bedrock"},{"path":"bedrock.md","title":"Setup (manual)","content":"1. Ensure AWS credentials are available on the **gateway host**:\n\n```bash\nexport AWS_ACCESS_KEY_ID=\"AKIA...\"\nexport AWS_SECRET_ACCESS_KEY=\"...\"\nexport AWS_REGION=\"us-east-1\"\n# Optional:\nexport AWS_SESSION_TOKEN=\"...\"\nexport AWS_PROFILE=\"your-profile\"\n# Optional (Bedrock API key/bearer token):\nexport AWS_BEARER_TOKEN_BEDROCK=\"...\"\n```\n\n2. Add a Bedrock provider and model to your config (no `apiKey` required):\n\n```json5\n{\n models: {\n providers: {\n \"amazon-bedrock\": {\n baseUrl: \"https://bedrock-runtime.us-east-1.amazonaws.com\",\n api: \"bedrock-converse-stream\",\n auth: \"aws-sdk\",\n models: [\n {\n id: \"anthropic.claude-opus-4-5-20251101-v1:0\",\n name: \"Claude Opus 4.5 (Bedrock)\",\n reasoning: true,\n input: [\"text\", \"image\"],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 200000,\n maxTokens: 8192,\n },\n ],\n },\n },\n },\n agents: {\n defaults: {\n model: { primary: \"amazon-bedrock/anthropic.claude-opus-4-5-20251101-v1:0\" },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/bedrock"},{"path":"bedrock.md","title":"EC2 Instance Roles","content":"When running OpenClaw on an EC2 instance with an IAM role attached, the AWS SDK\nwill automatically use the instance metadata service (IMDS) for authentication.\nHowever, OpenClaw's credential detection currently only checks for environment\nvariables, not IMDS credentials.\n\n**Workaround:** Set `AWS_PROFILE=default` to signal that AWS credentials are\navailable. The actual authentication still uses the instance role via IMDS.\n\n```bash\n# Add to ~/.bashrc or your shell profile\nexport AWS_PROFILE=default\nexport AWS_REGION=us-east-1\n```\n\n**Required IAM permissions** for the EC2 instance role:\n\n- `bedrock:InvokeModel`\n- `bedrock:InvokeModelWithResponseStream`\n- `bedrock:ListFoundationModels` (for automatic discovery)\n\nOr attach the managed policy `AmazonBedrockFullAccess`.\n\n**Quick setup:**\n\n```bash\n# 1. Create IAM role and instance profile\naws iam create-role --role-name EC2-Bedrock-Access \\\n --assume-role-policy-document '{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Principal\": {\"Service\": \"ec2.amazonaws.com\"},\n \"Action\": \"sts:AssumeRole\"\n }]\n }'\n\naws iam attach-role-policy --role-name EC2-Bedrock-Access \\\n --policy-arn arn:aws:iam::aws:policy/AmazonBedrockFullAccess\n\naws iam create-instance-profile --instance-profile-name EC2-Bedrock-Access\naws iam add-role-to-instance-profile \\\n --instance-profile-name EC2-Bedrock-Access \\\n --role-name EC2-Bedrock-Access\n\n# 2. Attach to your EC2 instance\naws ec2 associate-iam-instance-profile \\\n --instance-id i-xxxxx \\\n --iam-instance-profile Name=EC2-Bedrock-Access\n\n# 3. On the EC2 instance, enable discovery\nopenclaw config set models.bedrockDiscovery.enabled true\nopenclaw config set models.bedrockDiscovery.region us-east-1\n\n# 4. Set the workaround env vars\necho 'export AWS_PROFILE=default' >> ~/.bashrc\necho 'export AWS_REGION=us-east-1' >> ~/.bashrc\nsource ~/.bashrc\n\n# 5. Verify models are discovered\nopenclaw models list\n```","url":"https://docs.openclaw.ai/bedrock"},{"path":"bedrock.md","title":"Notes","content":"- Bedrock requires **model access** enabled in your AWS account/region.\n- Automatic discovery needs the `bedrock:ListFoundationModels` permission.\n- If you use profiles, set `AWS_PROFILE` on the gateway host.\n- OpenClaw surfaces the credential source in this order: `AWS_BEARER_TOKEN_BEDROCK`,\n then `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`, then `AWS_PROFILE`, then the\n default AWS SDK chain.\n- Reasoning support depends on the model; check the Bedrock model card for\n current capabilities.\n- If you prefer a managed key flow, you can also place an OpenAI‑compatible\n proxy in front of Bedrock and configure it as an OpenAI provider instead.","url":"https://docs.openclaw.ai/bedrock"},{"path":"brave-search.md","title":"brave-search","content":"# Brave Search API\n\nOpenClaw uses Brave Search as the default provider for `web_search`.","url":"https://docs.openclaw.ai/brave-search"},{"path":"brave-search.md","title":"Get an API key","content":"1. Create a Brave Search API account at https://brave.com/search/api/\n2. In the dashboard, choose the **Data for Search** plan and generate an API key.\n3. Store the key in config (recommended) or set `BRAVE_API_KEY` in the Gateway environment.","url":"https://docs.openclaw.ai/brave-search"},{"path":"brave-search.md","title":"Config example","content":"```json5\n{\n tools: {\n web: {\n search: {\n provider: \"brave\",\n apiKey: \"BRAVE_API_KEY_HERE\",\n maxResults: 5,\n timeoutSeconds: 30,\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/brave-search"},{"path":"brave-search.md","title":"Notes","content":"- The Data for AI plan is **not** compatible with `web_search`.\n- Brave provides a free tier plus paid plans; check the Brave API portal for current limits.\n\nSee [Web tools](/tools/web) for the full web_search configuration.","url":"https://docs.openclaw.ai/brave-search"},{"path":"broadcast-groups.md","title":"broadcast-groups","content":"# Broadcast Groups\n\n**Status:** Experimental \n**Version:** Added in 2026.1.9","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Overview","content":"Broadcast Groups enable multiple agents to process and respond to the same message simultaneously. This allows you to create specialized agent teams that work together in a single WhatsApp group or DM — all using one phone number.\n\nCurrent scope: **WhatsApp only** (web channel).\n\nBroadcast groups are evaluated after channel allowlists and group activation rules. In WhatsApp groups, this means broadcasts happen when OpenClaw would normally reply (for example: on mention, depending on your group settings).","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Use Cases","content":"### 1. Specialized Agent Teams\n\nDeploy multiple agents with atomic, focused responsibilities:\n\n```\nGroup: \"Development Team\"\nAgents:\n - CodeReviewer (reviews code snippets)\n - DocumentationBot (generates docs)\n - SecurityAuditor (checks for vulnerabilities)\n - TestGenerator (suggests test cases)\n```\n\nEach agent processes the same message and provides its specialized perspective.\n\n### 2. Multi-Language Support\n\n```\nGroup: \"International Support\"\nAgents:\n - Agent_EN (responds in English)\n - Agent_DE (responds in German)\n - Agent_ES (responds in Spanish)\n```\n\n### 3. Quality Assurance Workflows\n\n```\nGroup: \"Customer Support\"\nAgents:\n - SupportAgent (provides answer)\n - QAAgent (reviews quality, only responds if issues found)\n```\n\n### 4. Task Automation\n\n```\nGroup: \"Project Management\"\nAgents:\n - TaskTracker (updates task database)\n - TimeLogger (logs time spent)\n - ReportGenerator (creates summaries)\n```","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Configuration","content":"### Basic Setup\n\nAdd a top-level `broadcast` section (next to `bindings`). Keys are WhatsApp peer ids:\n\n- group chats: group JID (e.g. `120363403215116621@g.us`)\n- DMs: E.164 phone number (e.g. `+15551234567`)\n\n```json\n{\n \"broadcast\": {\n \"120363403215116621@g.us\": [\"alfred\", \"baerbel\", \"assistant3\"]\n }\n}\n```\n\n**Result:** When OpenClaw would reply in this chat, it will run all three agents.\n\n### Processing Strategy\n\nControl how agents process messages:\n\n#### Parallel (Default)\n\nAll agents process simultaneously:\n\n```json\n{\n \"broadcast\": {\n \"strategy\": \"parallel\",\n \"120363403215116621@g.us\": [\"alfred\", \"baerbel\"]\n }\n}\n```\n\n#### Sequential\n\nAgents process in order (one waits for previous to finish):\n\n```json\n{\n \"broadcast\": {\n \"strategy\": \"sequential\",\n \"120363403215116621@g.us\": [\"alfred\", \"baerbel\"]\n }\n}\n```\n\n### Complete Example\n\n```json\n{\n \"agents\": {\n \"list\": [\n {\n \"id\": \"code-reviewer\",\n \"name\": \"Code Reviewer\",\n \"workspace\": \"/path/to/code-reviewer\",\n \"sandbox\": { \"mode\": \"all\" }\n },\n {\n \"id\": \"security-auditor\",\n \"name\": \"Security Auditor\",\n \"workspace\": \"/path/to/security-auditor\",\n \"sandbox\": { \"mode\": \"all\" }\n },\n {\n \"id\": \"docs-generator\",\n \"name\": \"Documentation Generator\",\n \"workspace\": \"/path/to/docs-generator\",\n \"sandbox\": { \"mode\": \"all\" }\n }\n ]\n },\n \"broadcast\": {\n \"strategy\": \"parallel\",\n \"120363403215116621@g.us\": [\"code-reviewer\", \"security-auditor\", \"docs-generator\"],\n \"120363424282127706@g.us\": [\"support-en\", \"support-de\"],\n \"+15555550123\": [\"assistant\", \"logger\"]\n }\n}\n```","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"How It Works","content":"### Message Flow\n\n1. **Incoming message** arrives in a WhatsApp group\n2. **Broadcast check**: System checks if peer ID is in `broadcast`\n3. **If in broadcast list**:\n - All listed agents process the message\n - Each agent has its own session key and isolated context\n - Agents process in parallel (default) or sequentially\n4. **If not in broadcast list**:\n - Normal routing applies (first matching binding)\n\nNote: broadcast groups do not bypass channel allowlists or group activation rules (mentions/commands/etc). They only change _which agents run_ when a message is eligible for processing.\n\n### Session Isolation\n\nEach agent in a broadcast group maintains completely separate:\n\n- **Session keys** (`agent:alfred:whatsapp:group:120363...` vs `agent:baerbel:whatsapp:group:120363...`)\n- **Conversation history** (agent doesn't see other agents' messages)\n- **Workspace** (separate sandboxes if configured)\n- **Tool access** (different allow/deny lists)\n- **Memory/context** (separate IDENTITY.md, SOUL.md, etc.)\n- **Group context buffer** (recent group messages used for context) is shared per peer, so all broadcast agents see the same context when triggered\n\nThis allows each agent to have:\n\n- Different personalities\n- Different tool access (e.g., read-only vs. read-write)\n- Different models (e.g., opus vs. sonnet)\n- Different skills installed\n\n### Example: Isolated Sessions\n\nIn group `120363403215116621@g.us` with agents `[\"alfred\", \"baerbel\"]`:\n\n**Alfred's context:**\n\n```\nSession: agent:alfred:whatsapp:group:120363403215116621@g.us\nHistory: [user message, alfred's previous responses]\nWorkspace: /Users/pascal/openclaw-alfred/\nTools: read, write, exec\n```\n\n**Bärbel's context:**\n\n```\nSession: agent:baerbel:whatsapp:group:120363403215116621@g.us\nHistory: [user message, baerbel's previous responses]\nWorkspace: /Users/pascal/openclaw-baerbel/\nTools: read only\n```","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Best Practices","content":"### 1. Keep Agents Focused\n\nDesign each agent with a single, clear responsibility:\n\n```json\n{\n \"broadcast\": {\n \"DEV_GROUP\": [\"formatter\", \"linter\", \"tester\"]\n }\n}\n```\n\n✅ **Good:** Each agent has one job \n❌ **Bad:** One generic \"dev-helper\" agent\n\n### 2. Use Descriptive Names\n\nMake it clear what each agent does:\n\n```json\n{\n \"agents\": {\n \"security-scanner\": { \"name\": \"Security Scanner\" },\n \"code-formatter\": { \"name\": \"Code Formatter\" },\n \"test-generator\": { \"name\": \"Test Generator\" }\n }\n}\n```\n\n### 3. Configure Different Tool Access\n\nGive agents only the tools they need:\n\n```json\n{\n \"agents\": {\n \"reviewer\": {\n \"tools\": { \"allow\": [\"read\", \"exec\"] } // Read-only\n },\n \"fixer\": {\n \"tools\": { \"allow\": [\"read\", \"write\", \"edit\", \"exec\"] } // Read-write\n }\n }\n}\n```\n\n### 4. Monitor Performance\n\nWith many agents, consider:\n\n- Using `\"strategy\": \"parallel\"` (default) for speed\n- Limiting broadcast groups to 5-10 agents\n- Using faster models for simpler agents\n\n### 5. Handle Failures Gracefully\n\nAgents fail independently. One agent's error doesn't block others:\n\n```\nMessage → [Agent A ✓, Agent B ✗ error, Agent C ✓]\nResult: Agent A and C respond, Agent B logs error\n```","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Compatibility","content":"### Providers\n\nBroadcast groups currently work with:\n\n- ✅ WhatsApp (implemented)\n- 🚧 Telegram (planned)\n- 🚧 Discord (planned)\n- 🚧 Slack (planned)\n\n### Routing\n\nBroadcast groups work alongside existing routing:\n\n```json\n{\n \"bindings\": [\n {\n \"match\": { \"channel\": \"whatsapp\", \"peer\": { \"kind\": \"group\", \"id\": \"GROUP_A\" } },\n \"agentId\": \"alfred\"\n }\n ],\n \"broadcast\": {\n \"GROUP_B\": [\"agent1\", \"agent2\"]\n }\n}\n```\n\n- `GROUP_A`: Only alfred responds (normal routing)\n- `GROUP_B`: agent1 AND agent2 respond (broadcast)\n\n**Precedence:** `broadcast` takes priority over `bindings`.","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Troubleshooting","content":"### Agents Not Responding\n\n**Check:**\n\n1. Agent IDs exist in `agents.list`\n2. Peer ID format is correct (e.g., `120363403215116621@g.us`)\n3. Agents are not in deny lists\n\n**Debug:**\n\n```bash\ntail -f ~/.openclaw/logs/gateway.log | grep broadcast\n```\n\n### Only One Agent Responding\n\n**Cause:** Peer ID might be in `bindings` but not `broadcast`.\n\n**Fix:** Add to broadcast config or remove from bindings.\n\n### Performance Issues\n\n**If slow with many agents:**\n\n- Reduce number of agents per group\n- Use lighter models (sonnet instead of opus)\n- Check sandbox startup time","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Examples","content":"### Example 1: Code Review Team\n\n```json\n{\n \"broadcast\": {\n \"strategy\": \"parallel\",\n \"120363403215116621@g.us\": [\n \"code-formatter\",\n \"security-scanner\",\n \"test-coverage\",\n \"docs-checker\"\n ]\n },\n \"agents\": {\n \"list\": [\n {\n \"id\": \"code-formatter\",\n \"workspace\": \"~/agents/formatter\",\n \"tools\": { \"allow\": [\"read\", \"write\"] }\n },\n {\n \"id\": \"security-scanner\",\n \"workspace\": \"~/agents/security\",\n \"tools\": { \"allow\": [\"read\", \"exec\"] }\n },\n {\n \"id\": \"test-coverage\",\n \"workspace\": \"~/agents/testing\",\n \"tools\": { \"allow\": [\"read\", \"exec\"] }\n },\n { \"id\": \"docs-checker\", \"workspace\": \"~/agents/docs\", \"tools\": { \"allow\": [\"read\"] } }\n ]\n }\n}\n```\n\n**User sends:** Code snippet \n**Responses:**\n\n- code-formatter: \"Fixed indentation and added type hints\"\n- security-scanner: \"⚠️ SQL injection vulnerability in line 12\"\n- test-coverage: \"Coverage is 45%, missing tests for error cases\"\n- docs-checker: \"Missing docstring for function `process_data`\"\n\n### Example 2: Multi-Language Support\n\n```json\n{\n \"broadcast\": {\n \"strategy\": \"sequential\",\n \"+15555550123\": [\"detect-language\", \"translator-en\", \"translator-de\"]\n },\n \"agents\": {\n \"list\": [\n { \"id\": \"detect-language\", \"workspace\": \"~/agents/lang-detect\" },\n { \"id\": \"translator-en\", \"workspace\": \"~/agents/translate-en\" },\n { \"id\": \"translator-de\", \"workspace\": \"~/agents/translate-de\" }\n ]\n }\n}\n```","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"API Reference","content":"### Config Schema\n\n```typescript\ninterface OpenClawConfig {\n broadcast?: {\n strategy?: \"parallel\" | \"sequential\";\n [peerId: string]: string[];\n };\n}\n```\n\n### Fields\n\n- `strategy` (optional): How to process agents\n - `\"parallel\"` (default): All agents process simultaneously\n - `\"sequential\"`: Agents process in array order\n- `[peerId]`: WhatsApp group JID, E.164 number, or other peer ID\n - Value: Array of agent IDs that should process messages","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Limitations","content":"1. **Max agents:** No hard limit, but 10+ agents may be slow\n2. **Shared context:** Agents don't see each other's responses (by design)\n3. **Message ordering:** Parallel responses may arrive in any order\n4. **Rate limits:** All agents count toward WhatsApp rate limits","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"Future Enhancements","content":"Planned features:\n\n- [ ] Shared context mode (agents see each other's responses)\n- [ ] Agent coordination (agents can signal each other)\n- [ ] Dynamic agent selection (choose agents based on message content)\n- [ ] Agent priorities (some agents respond before others)","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"broadcast-groups.md","title":"See Also","content":"- [Multi-Agent Configuration](/multi-agent-sandbox-tools)\n- [Routing Configuration](/concepts/channel-routing)\n- [Session Management](/concepts/sessions)","url":"https://docs.openclaw.ai/broadcast-groups"},{"path":"channels/bluebubbles.md","title":"bluebubbles","content":"# BlueBubbles (macOS REST)\n\nStatus: bundled plugin that talks to the BlueBubbles macOS server over HTTP. **Recommended for iMessage integration** due to its richer API and easier setup compared to the legacy imsg channel.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Overview","content":"- Runs on macOS via the BlueBubbles helper app ([bluebubbles.app](https://bluebubbles.app)).\n- Recommended/tested: macOS Sequoia (15). macOS Tahoe (26) works; edit is currently broken on Tahoe, and group icon updates may report success but not sync.\n- OpenClaw talks to it through its REST API (`GET /api/v1/ping`, `POST /message/text`, `POST /chat/:id/*`).\n- Incoming messages arrive via webhooks; outgoing replies, typing indicators, read receipts, and tapbacks are REST calls.\n- Attachments and stickers are ingested as inbound media (and surfaced to the agent when possible).\n- Pairing/allowlist works the same way as other channels (`/start/pairing` etc) with `channels.bluebubbles.allowFrom` + pairing codes.\n- Reactions are surfaced as system events just like Slack/Telegram so agents can \"mention\" them before replying.\n- Advanced features: edit, unsend, reply threading, message effects, group management.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Quick start","content":"1. Install the BlueBubbles server on your Mac (follow the instructions at [bluebubbles.app/install](https://bluebubbles.app/install)).\n2. In the BlueBubbles config, enable the web API and set a password.\n3. Run `openclaw onboard` and select BlueBubbles, or configure manually:\n ```json5\n {\n channels: {\n bluebubbles: {\n enabled: true,\n serverUrl: \"http://192.168.1.100:1234\",\n password: \"example-password\",\n webhookPath: \"/bluebubbles-webhook\",\n },\n },\n }\n ```\n4. Point BlueBubbles webhooks to your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=`).\n5. Start the gateway; it will register the webhook handler and start pairing.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Onboarding","content":"BlueBubbles is available in the interactive setup wizard:\n\n```\nopenclaw onboard\n```\n\nThe wizard prompts for:\n\n- **Server URL** (required): BlueBubbles server address (e.g., `http://192.168.1.100:1234`)\n- **Password** (required): API password from BlueBubbles Server settings\n- **Webhook path** (optional): Defaults to `/bluebubbles-webhook`\n- **DM policy**: pairing, allowlist, open, or disabled\n- **Allow list**: Phone numbers, emails, or chat targets\n\nYou can also add BlueBubbles via CLI:\n\n```\nopenclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --password \n```","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Access control (DMs + groups)","content":"DMs:\n\n- Default: `channels.bluebubbles.dmPolicy = \"pairing\"`.\n- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).\n- Approve via:\n - `openclaw pairing list bluebubbles`\n - `openclaw pairing approve bluebubbles `\n- Pairing is the default token exchange. Details: [Pairing](/start/pairing)\n\nGroups:\n\n- `channels.bluebubbles.groupPolicy = open | allowlist | disabled` (default: `allowlist`).\n- `channels.bluebubbles.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.\n\n### Mention gating (groups)\n\nBlueBubbles supports mention gating for group chats, matching iMessage/WhatsApp behavior:\n\n- Uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) to detect mentions.\n- When `requireMention` is enabled for a group, the agent only responds when mentioned.\n- Control commands from authorized senders bypass mention gating.\n\nPer-group configuration:\n\n```json5\n{\n channels: {\n bluebubbles: {\n groupPolicy: \"allowlist\",\n groupAllowFrom: [\"+15555550123\"],\n groups: {\n \"*\": { requireMention: true }, // default for all groups\n \"iMessage;-;chat123\": { requireMention: false }, // override for specific group\n },\n },\n },\n}\n```\n\n### Command gating\n\n- Control commands (e.g., `/config`, `/model`) require authorization.\n- Uses `allowFrom` and `groupAllowFrom` to determine command authorization.\n- Authorized senders can run control commands even without mentioning in groups.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Typing + read receipts","content":"- **Typing indicators**: Sent automatically before and during response generation.\n- **Read receipts**: Controlled by `channels.bluebubbles.sendReadReceipts` (default: `true`).\n- **Typing indicators**: OpenClaw sends typing start events; BlueBubbles clears typing automatically on send or timeout (manual stop via DELETE is unreliable).\n\n```json5\n{\n channels: {\n bluebubbles: {\n sendReadReceipts: false, // disable read receipts\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Advanced actions","content":"BlueBubbles supports advanced message actions when enabled in config:\n\n```json5\n{\n channels: {\n bluebubbles: {\n actions: {\n reactions: true, // tapbacks (default: true)\n edit: true, // edit sent messages (macOS 13+, broken on macOS 26 Tahoe)\n unsend: true, // unsend messages (macOS 13+)\n reply: true, // reply threading by message GUID\n sendWithEffect: true, // message effects (slam, loud, etc.)\n renameGroup: true, // rename group chats\n setGroupIcon: true, // set group chat icon/photo (flaky on macOS 26 Tahoe)\n addParticipant: true, // add participants to groups\n removeParticipant: true, // remove participants from groups\n leaveGroup: true, // leave group chats\n sendAttachment: true, // send attachments/media\n },\n },\n },\n}\n```\n\nAvailable actions:\n\n- **react**: Add/remove tapback reactions (`messageId`, `emoji`, `remove`)\n- **edit**: Edit a sent message (`messageId`, `text`)\n- **unsend**: Unsend a message (`messageId`)\n- **reply**: Reply to a specific message (`messageId`, `text`, `to`)\n- **sendWithEffect**: Send with iMessage effect (`text`, `to`, `effectId`)\n- **renameGroup**: Rename a group chat (`chatGuid`, `displayName`)\n- **setGroupIcon**: Set a group chat's icon/photo (`chatGuid`, `media`) — flaky on macOS 26 Tahoe (API may return success but the icon does not sync).\n- **addParticipant**: Add someone to a group (`chatGuid`, `address`)\n- **removeParticipant**: Remove someone from a group (`chatGuid`, `address`)\n- **leaveGroup**: Leave a group chat (`chatGuid`)\n- **sendAttachment**: Send media/files (`to`, `buffer`, `filename`, `asVoice`)\n - Voice memos: set `asVoice: true` with **MP3** or **CAF** audio to send as an iMessage voice message. BlueBubbles converts MP3 → CAF when sending voice memos.\n\n### Message IDs (short vs full)\n\nOpenClaw may surface _short_ message IDs (e.g., `1`, `2`) to save tokens.\n\n- `MessageSid` / `ReplyToId` can be short IDs.\n- `MessageSidFull` / `ReplyToIdFull` contain the provider full IDs.\n- Short IDs are in-memory; they can expire on restart or cache eviction.\n- Actions accept short or full `messageId`, but short IDs will error if no longer available.\n\nUse full IDs for durable automations and storage:\n\n- Templates: `{{MessageSidFull}}`, `{{ReplyToIdFull}}`\n- Context: `MessageSidFull` / `ReplyToIdFull` in inbound payloads\n\nSee [Configuration](/gateway/configuration) for template variables.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Block streaming","content":"Control whether responses are sent as a single message or streamed in blocks:\n\n```json5\n{\n channels: {\n bluebubbles: {\n blockStreaming: true, // enable block streaming (off by default)\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Media + limits","content":"- Inbound attachments are downloaded and stored in the media cache.\n- Media cap via `channels.bluebubbles.mediaMaxMb` (default: 8 MB).\n- Outbound text is chunked to `channels.bluebubbles.textChunkLimit` (default: 4000 chars).","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Configuration reference","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.bluebubbles.enabled`: Enable/disable the channel.\n- `channels.bluebubbles.serverUrl`: BlueBubbles REST API base URL.\n- `channels.bluebubbles.password`: API password.\n- `channels.bluebubbles.webhookPath`: Webhook endpoint path (default: `/bluebubbles-webhook`).\n- `channels.bluebubbles.dmPolicy`: `pairing | allowlist | open | disabled` (default: `pairing`).\n- `channels.bluebubbles.allowFrom`: DM allowlist (handles, emails, E.164 numbers, `chat_id:*`, `chat_guid:*`).\n- `channels.bluebubbles.groupPolicy`: `open | allowlist | disabled` (default: `allowlist`).\n- `channels.bluebubbles.groupAllowFrom`: Group sender allowlist.\n- `channels.bluebubbles.groups`: Per-group config (`requireMention`, etc.).\n- `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`).\n- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `false`; required for streaming replies).\n- `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000).\n- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking.\n- `channels.bluebubbles.mediaMaxMb`: Inbound media cap in MB (default: 8).\n- `channels.bluebubbles.historyLimit`: Max group messages for context (0 disables).\n- `channels.bluebubbles.dmHistoryLimit`: DM history limit.\n- `channels.bluebubbles.actions`: Enable/disable specific actions.\n- `channels.bluebubbles.accounts`: Multi-account configuration.\n\nRelated global options:\n\n- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).\n- `messages.responsePrefix`.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Addressing / delivery targets","content":"Prefer `chat_guid` for stable routing:\n\n- `chat_guid:iMessage;-;+15555550123` (preferred for groups)\n- `chat_id:123`\n- `chat_identifier:...`\n- Direct handles: `+15555550123`, `user@example.com`\n - If a direct handle does not have an existing DM chat, OpenClaw will create one via `POST /api/v1/chat/new`. This requires the BlueBubbles Private API to be enabled.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Security","content":"- Webhook requests are authenticated by comparing `guid`/`password` query params or headers against `channels.bluebubbles.password`. Requests from `localhost` are also accepted.\n- Keep the API password and webhook endpoint secret (treat them like credentials).\n- Localhost trust means a same-host reverse proxy can unintentionally bypass the password. If you proxy the gateway, require auth at the proxy and configure `gateway.trustedProxies`. See [Gateway security](/gateway/security#reverse-proxy-configuration).\n- Enable HTTPS + firewall rules on the BlueBubbles server if exposing it outside your LAN.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/bluebubbles.md","title":"Troubleshooting","content":"- If typing/read events stop working, check the BlueBubbles webhook logs and verify the gateway path matches `channels.bluebubbles.webhookPath`.\n- Pairing codes expire after one hour; use `openclaw pairing list bluebubbles` and `openclaw pairing approve bluebubbles `.\n- Reactions require the BlueBubbles private API (`POST /api/v1/message/react`); ensure the server version exposes it.\n- Edit/unsend require macOS 13+ and a compatible BlueBubbles server version. On macOS 26 (Tahoe), edit is currently broken due to private API changes.\n- Group icon updates can be flaky on macOS 26 (Tahoe): the API may return success but the new icon does not sync.\n- OpenClaw auto-hides known-broken actions based on the BlueBubbles server's macOS version. If edit still appears on macOS 26 (Tahoe), disable it manually with `channels.bluebubbles.actions.edit=false`.\n- For status/health info: `openclaw status --all` or `openclaw status --deep`.\n\nFor general channel workflow reference, see [Channels](/channels) and the [Plugins](/plugins) guide.","url":"https://docs.openclaw.ai/channels/bluebubbles"},{"path":"channels/discord.md","title":"discord","content":"# Discord (Bot API)\n\nStatus: ready for DM and guild text channels via the official Discord bot gateway.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Quick setup (beginner)","content":"1. Create a Discord bot and copy the bot token.\n2. In the Discord app settings, enable **Message Content Intent** (and **Server Members Intent** if you plan to use allowlists or name lookups).\n3. Set the token for OpenClaw:\n - Env: `DISCORD_BOT_TOKEN=...`\n - Or config: `channels.discord.token: \"...\"`.\n - If both are set, config takes precedence (env fallback is default-account only).\n4. Invite the bot to your server with message permissions (create a private server if you just want DMs).\n5. Start the gateway.\n6. DM access is pairing by default; approve the pairing code on first contact.\n\nMinimal config:\n\n```json5\n{\n channels: {\n discord: {\n enabled: true,\n token: \"YOUR_BOT_TOKEN\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Goals","content":"- Talk to OpenClaw via Discord DMs or guild channels.\n- Direct chats collapse into the agent's main session (default `agent:main:main`); guild channels stay isolated as `agent::discord:channel:` (display names use `discord:#`).\n- Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`.\n- Keep routing deterministic: replies always go back to the channel they arrived on.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"How it works","content":"1. Create a Discord application → Bot, enable the intents you need (DMs + guild messages + message content), and grab the bot token.\n2. Invite the bot to your server with the permissions required to read/send messages where you want to use it.\n3. Configure OpenClaw with `channels.discord.token` (or `DISCORD_BOT_TOKEN` as a fallback).\n4. Run the gateway; it auto-starts the Discord channel when a token is available (config first, env fallback) and `channels.discord.enabled` is not `false`.\n - If you prefer env vars, set `DISCORD_BOT_TOKEN` (a config block is optional).\n5. Direct chats: use `user:` (or a `<@id>` mention) when delivering; all turns land in the shared `main` session. Bare numeric IDs are ambiguous and rejected.\n6. Guild channels: use `channel:` for delivery. Mentions are required by default and can be set per guild or per channel.\n7. Direct chats: secure by default via `channels.discord.dm.policy` (default: `\"pairing\"`). Unknown senders get a pairing code (expires after 1 hour); approve via `openclaw pairing approve discord `.\n - To keep old “open to anyone” behavior: set `channels.discord.dm.policy=\"open\"` and `channels.discord.dm.allowFrom=[\"*\"]`.\n - To hard-allowlist: set `channels.discord.dm.policy=\"allowlist\"` and list senders in `channels.discord.dm.allowFrom`.\n - To ignore all DMs: set `channels.discord.dm.enabled=false` or `channels.discord.dm.policy=\"disabled\"`.\n8. Group DMs are ignored by default; enable via `channels.discord.dm.groupEnabled` and optionally restrict by `channels.discord.dm.groupChannels`.\n9. Optional guild rules: set `channels.discord.guilds` keyed by guild id (preferred) or slug, with per-channel rules.\n10. Optional native commands: `commands.native` defaults to `\"auto\"` (on for Discord/Telegram, off for Slack). Override with `channels.discord.commands.native: true|false|\"auto\"`; `false` clears previously registered commands. Text commands are controlled by `commands.text` and must be sent as standalone `/...` messages. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.\n - Full command list + config: [Slash commands](/tools/slash-commands)\n11. Optional guild context history: set `channels.discord.historyLimit` (default 20, falls back to `messages.groupChat.historyLimit`) to include the last N guild messages as context when replying to a mention. Set `0` to disable.\n12. Reactions: the agent can trigger reactions via the `discord` tool (gated by `channels.discord.actions.*`).\n - Reaction removal semantics: see [/tools/reactions](/tools/reactions).\n - The `discord` tool is only exposed when the current channel is Discord.\n13. Native commands use isolated session keys (`agent::discord:slash:`) rather than the shared `main` session.\n\nNote: Name → id resolution uses guild member search and requires Server Members Intent; if the bot can’t search members, use ids or `<@id>` mentions.\nNote: Slugs are lowercase with spaces replaced by `-`. Channel names are slugged without the leading `#`.\nNote: Guild context `[from:]` lines include `author.tag` + `id` to make ping-ready replies easy.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Config writes","content":"By default, Discord is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).\n\nDisable with:\n\n```json5\n{\n channels: { discord: { configWrites: false } },\n}\n```","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"How to create your own bot","content":"This is the “Discord Developer Portal” setup for running OpenClaw in a server (guild) channel like `#help`.\n\n### 1) Create the Discord app + bot user\n\n1. Discord Developer Portal → **Applications** → **New Application**\n2. In your app:\n - **Bot** → **Add Bot**\n - Copy the **Bot Token** (this is what you put in `DISCORD_BOT_TOKEN`)\n\n### 2) Enable the gateway intents OpenClaw needs\n\nDiscord blocks “privileged intents” unless you explicitly enable them.\n\nIn **Bot** → **Privileged Gateway Intents**, enable:\n\n- **Message Content Intent** (required to read message text in most guilds; without it you’ll see “Used disallowed intents” or the bot will connect but not react to messages)\n- **Server Members Intent** (recommended; required for some member/user lookups and allowlist matching in guilds)\n\nYou usually do **not** need **Presence Intent**.\n\n### 3) Generate an invite URL (OAuth2 URL Generator)\n\nIn your app: **OAuth2** → **URL Generator**\n\n**Scopes**\n\n- ✅ `bot`\n- ✅ `applications.commands` (required for native commands)\n\n**Bot Permissions** (minimal baseline)\n\n- ✅ View Channels\n- ✅ Send Messages\n- ✅ Read Message History\n- ✅ Embed Links\n- ✅ Attach Files\n- ✅ Add Reactions (optional but recommended)\n- ✅ Use External Emojis / Stickers (optional; only if you want them)\n\nAvoid **Administrator** unless you’re debugging and fully trust the bot.\n\nCopy the generated URL, open it, pick your server, and install the bot.\n\n### 4) Get the ids (guild/user/channel)\n\nDiscord uses numeric ids everywhere; OpenClaw config prefers ids.\n\n1. Discord (desktop/web) → **User Settings** → **Advanced** → enable **Developer Mode**\n2. Right-click:\n - Server name → **Copy Server ID** (guild id)\n - Channel (e.g. `#help`) → **Copy Channel ID**\n - Your user → **Copy User ID**\n\n### 5) Configure OpenClaw\n\n#### Token\n\nSet the bot token via env var (recommended on servers):\n\n- `DISCORD_BOT_TOKEN=...`\n\nOr via config:\n\n```json5\n{\n channels: {\n discord: {\n enabled: true,\n token: \"YOUR_BOT_TOKEN\",\n },\n },\n}\n```\n\nMulti-account support: use `channels.discord.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.\n\n#### Allowlist + channel routing\n\nExample “single server, only allow me, only allow #help”:\n\n```json5\n{\n channels: {\n discord: {\n enabled: true,\n dm: { enabled: false },\n guilds: {\n YOUR_GUILD_ID: {\n users: [\"YOUR_USER_ID\"],\n requireMention: true,\n channels: {\n help: { allow: true, requireMention: true },\n },\n },\n },\n retry: {\n attempts: 3,\n minDelayMs: 500,\n maxDelayMs: 30000,\n jitter: 0.1,\n },\n },\n },\n}\n```\n\nNotes:\n\n- `requireMention: true` means the bot only replies when mentioned (recommended for shared channels).\n- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions for guild messages.\n- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.\n- If `channels` is present, any channel not listed is denied by default.\n- Use a `\"*\"` channel entry to apply defaults across all channels; explicit channel entries override the wildcard.\n- Threads inherit parent channel config (allowlist, `requireMention`, skills, prompts, etc.) unless you add the thread channel id explicitly.\n- Bot-authored messages are ignored by default; set `channels.discord.allowBots=true` to allow them (own messages remain filtered).\n- Warning: If you allow replies to other bots (`channels.discord.allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.discord.guilds.*.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.\n\n### 6) Verify it works\n\n1. Start the gateway.\n2. In your server channel, send: `@Krill hello` (or whatever your bot name is).\n3. If nothing happens: check **Troubleshooting** below.\n\n### Troubleshooting\n\n- First: run `openclaw doctor` and `openclaw channels status --probe` (actionable warnings + quick audits).\n- **“Used disallowed intents”**: enable **Message Content Intent** (and likely **Server Members Intent**) in the Developer Portal, then restart the gateway.\n- **Bot connects but never replies in a guild channel**:\n - Missing **Message Content Intent**, or\n - The bot lacks channel permissions (View/Send/Read History), or\n - Your config requires mentions and you didn’t mention it, or\n - Your guild/channel allowlist denies the channel/user.\n- **`requireMention: false` but still no replies**:\n- `channels.discord.groupPolicy` defaults to **allowlist**; set it to `\"open\"` or add a guild entry under `channels.discord.guilds` (optionally list channels under `channels.discord.guilds..channels` to restrict).\n - If you only set `DISCORD_BOT_TOKEN` and never create a `channels.discord` section, the runtime\n defaults `groupPolicy` to `open`. Add `channels.discord.groupPolicy`,\n `channels.defaults.groupPolicy`, or a guild/channel allowlist to lock it down.\n- `requireMention` must live under `channels.discord.guilds` (or a specific channel). `channels.discord.requireMention` at the top level is ignored.\n- **Permission audits** (`channels status --probe`) only check numeric channel IDs. If you use slugs/names as `channels.discord.guilds.*.channels` keys, the audit can’t verify permissions.\n- **DMs don’t work**: `channels.discord.dm.enabled=false`, `channels.discord.dm.policy=\"disabled\"`, or you haven’t been approved yet (`channels.discord.dm.policy=\"pairing\"`).\n- **Exec approvals in Discord**: Discord supports a **button UI** for exec approvals in DMs (Allow once / Always allow / Deny). `/approve ...` is only for forwarded approvals and won’t resolve Discord’s button prompts. If you see `❌ Failed to submit approval: Error: unknown approval id` or the UI never shows up, check:\n - `channels.discord.execApprovals.enabled: true` in your config.\n - Your Discord user ID is listed in `channels.discord.execApprovals.approvers` (the UI is only sent to approvers).\n - Use the buttons in the DM prompt (**Allow once**, **Always allow**, **Deny**).\n - See [Exec approvals](/tools/exec-approvals) and [Slash commands](/tools/slash-commands) for the broader approvals and command flow.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Capabilities & limits","content":"- DMs and guild text channels (threads are treated as separate channels; voice not supported).\n- Typing indicators sent best-effort; message chunking uses `channels.discord.textChunkLimit` (default 2000) and splits tall replies by line count (`channels.discord.maxLinesPerMessage`, default 17).\n- Optional newline chunking: set `channels.discord.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n- File uploads supported up to the configured `channels.discord.mediaMaxMb` (default 8 MB).\n- Mention-gated guild replies by default to avoid noisy bots.\n- Reply context is injected when a message references another message (quoted content + ids).\n- Native reply threading is **off by default**; enable with `channels.discord.replyToMode` and reply tags.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Retry policy","content":"Outbound Discord API calls retry on rate limits (429) using Discord `retry_after` when available, with exponential backoff and jitter. Configure via `channels.discord.retry`. See [Retry policy](/concepts/retry).","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Config","content":"```json5\n{\n channels: {\n discord: {\n enabled: true,\n token: \"abc.123\",\n groupPolicy: \"allowlist\",\n guilds: {\n \"*\": {\n channels: {\n general: { allow: true },\n },\n },\n },\n mediaMaxMb: 8,\n actions: {\n reactions: true,\n stickers: true,\n emojiUploads: true,\n stickerUploads: true,\n polls: true,\n permissions: true,\n messages: true,\n threads: true,\n pins: true,\n search: true,\n memberInfo: true,\n roleInfo: true,\n roles: false,\n channelInfo: true,\n channels: true,\n voiceStatus: true,\n events: true,\n moderation: false,\n },\n replyToMode: \"off\",\n dm: {\n enabled: true,\n policy: \"pairing\", // pairing | allowlist | open | disabled\n allowFrom: [\"123456789012345678\", \"steipete\"],\n groupEnabled: false,\n groupChannels: [\"openclaw-dm\"],\n },\n guilds: {\n \"*\": { requireMention: true },\n \"123456789012345678\": {\n slug: \"friends-of-openclaw\",\n requireMention: false,\n reactionNotifications: \"own\",\n users: [\"987654321098765432\", \"steipete\"],\n channels: {\n general: { allow: true },\n help: {\n allow: true,\n requireMention: true,\n users: [\"987654321098765432\"],\n skills: [\"search\", \"docs\"],\n systemPrompt: \"Keep answers short.\",\n },\n },\n },\n },\n },\n },\n}\n```\n\nAck reactions are controlled globally via `messages.ackReaction` +\n`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the\nack reaction after the bot replies.\n\n- `dm.enabled`: set `false` to ignore all DMs (default `true`).\n- `dm.policy`: DM access control (`pairing` recommended). `\"open\"` requires `dm.allowFrom=[\"*\"]`.\n- `dm.allowFrom`: DM allowlist (user ids or names). Used by `dm.policy=\"allowlist\"` and for `dm.policy=\"open\"` validation. The wizard accepts usernames and resolves them to ids when the bot can search members.\n- `dm.groupEnabled`: enable group DMs (default `false`).\n- `dm.groupChannels`: optional allowlist for group DM channel ids or slugs.\n- `groupPolicy`: controls guild channel handling (`open|disabled|allowlist`); `allowlist` requires channel allowlists.\n- `guilds`: per-guild rules keyed by guild id (preferred) or slug.\n- `guilds.\"*\"`: default per-guild settings applied when no explicit entry exists.\n- `guilds..slug`: optional friendly slug used for display names.\n- `guilds..users`: optional per-guild user allowlist (ids or names).\n- `guilds..tools`: optional per-guild tool policy overrides (`allow`/`deny`/`alsoAllow`) used when the channel override is missing.\n- `guilds..toolsBySender`: optional per-sender tool policy overrides at the guild level (applies when the channel override is missing; `\"*\"` wildcard supported).\n- `guilds..channels..allow`: allow/deny the channel when `groupPolicy=\"allowlist\"`.\n- `guilds..channels..requireMention`: mention gating for the channel.\n- `guilds..channels..tools`: optional per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`).\n- `guilds..channels..toolsBySender`: optional per-sender tool policy overrides within the channel (`\"*\"` wildcard supported).\n- `guilds..channels..users`: optional per-channel user allowlist.\n- `guilds..channels..skills`: skill filter (omit = all skills, empty = none).\n- `guilds..channels..systemPrompt`: extra system prompt for the channel (combined with channel topic).\n- `guilds..channels..enabled`: set `false` to disable the channel.\n- `guilds..channels`: channel rules (keys are channel slugs or ids).\n- `guilds..requireMention`: per-guild mention requirement (overridable per channel).\n- `guilds..reactionNotifications`: reaction system event mode (`off`, `own`, `all`, `allowlist`).\n- `textChunkLimit`: outbound text chunk size (chars). Default: 2000.\n- `chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking.\n- `maxLinesPerMessage`: soft max line count per message. Default: 17.\n- `mediaMaxMb`: clamp inbound media saved to disk.\n- `historyLimit`: number of recent guild messages to include as context when replying to a mention (default 20; falls back to `messages.groupChat.historyLimit`; `0` disables).\n- `dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `dms[\"\"].historyLimit`.\n- `retry`: retry policy for outbound Discord API calls (attempts, minDelayMs, maxDelayMs, jitter).\n- `pluralkit`: resolve PluralKit proxied messages so system members appear as distinct senders.\n- `actions`: per-action tool gates; omit to allow all (set `false` to disable).\n - `reactions` (covers react + read reactions)\n - `stickers`, `emojiUploads`, `stickerUploads`, `polls`, `permissions`, `messages`, `threads`, `pins`, `search`\n - `memberInfo`, `roleInfo`, `channelInfo`, `voiceStatus`, `events`\n - `channels` (create/edit/delete channels + categories + permissions)\n - `roles` (role add/remove, default `false`)\n - `moderation` (timeout/kick/ban, default `false`)\n- `execApprovals`: Discord-only exec approval DMs (button UI). Supports `enabled`, `approvers`, `agentFilter`, `sessionFilter`.\n\nReaction notifications use `guilds..reactionNotifications`:\n\n- `off`: no reaction events.\n- `own`: reactions on the bot's own messages (default).\n- `all`: all reactions on all messages.\n- `allowlist`: reactions from `guilds..users` on all messages (empty list disables).\n\n### PluralKit (PK) support\n\nEnable PK lookups so proxied messages resolve to the underlying system + member.\nWhen enabled, OpenClaw uses the member identity for allowlists and labels the\nsender as `Member (PK:System)` to avoid accidental Discord pings.\n\n```json5\n{\n channels: {\n discord: {\n pluralkit: {\n enabled: true,\n token: \"pk_live_...\", // optional; required for private systems\n },\n },\n },\n}\n```\n\nAllowlist notes (PK-enabled):\n\n- Use `pk:` in `dm.allowFrom`, `guilds..users`, or per-channel `users`.\n- Member display names are also matched by name/slug.\n- Lookups use the **original** Discord message ID (the pre-proxy message), so\n the PK API only resolves it within its 30-minute window.\n- If PK lookups fail (e.g., private system without a token), proxied messages\n are treated as bot messages and are dropped unless `channels.discord.allowBots=true`.\n\n### Tool action defaults\n\n| Action group | Default | Notes |\n| -------------- | -------- | ---------------------------------- |\n| reactions | enabled | React + list reactions + emojiList |\n| stickers | enabled | Send stickers |\n| emojiUploads | enabled | Upload emojis |\n| stickerUploads | enabled | Upload stickers |\n| polls | enabled | Create polls |\n| permissions | enabled | Channel permission snapshot |\n| messages | enabled | Read/send/edit/delete |\n| threads | enabled | Create/list/reply |\n| pins | enabled | Pin/unpin/list |\n| search | enabled | Message search (preview feature) |\n| memberInfo | enabled | Member info |\n| roleInfo | enabled | Role list |\n| channelInfo | enabled | Channel info + list |\n| channels | enabled | Channel/category management |\n| voiceStatus | enabled | Voice state lookup |\n| events | enabled | List/create scheduled events |\n| roles | disabled | Role add/remove |\n| moderation | disabled | Timeout/kick/ban |\n\n- `replyToMode`: `off` (default), `first`, or `all`. Applies only when the model includes a reply tag.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Reply tags","content":"To request a threaded reply, the model can include one tag in its output:\n\n- `[[reply_to_current]]` — reply to the triggering Discord message.\n- `[[reply_to:]]` — reply to a specific message id from context/history.\n Current message ids are appended to prompts as `[message_id: …]`; history entries already include ids.\n\nBehavior is controlled by `channels.discord.replyToMode`:\n\n- `off`: ignore tags.\n- `first`: only the first outbound chunk/attachment is a reply.\n- `all`: every outbound chunk/attachment is a reply.\n\nAllowlist matching notes:\n\n- `allowFrom`/`users`/`groupChannels` accept ids, names, tags, or mentions like `<@id>`.\n- Prefixes like `discord:`/`user:` (users) and `channel:` (group DMs) are supported.\n- Use `*` to allow any sender/channel.\n- When `guilds..channels` is present, channels not listed are denied by default.\n- When `guilds..channels` is omitted, all channels in the allowlisted guild are allowed.\n- To allow **no channels**, set `channels.discord.groupPolicy: \"disabled\"` (or keep an empty allowlist).\n- The configure wizard accepts `Guild/Channel` names (public + private) and resolves them to IDs when possible.\n- On startup, OpenClaw resolves channel/user names in allowlists to IDs (when the bot can search members)\n and logs the mapping; unresolved entries are kept as typed.\n\nNative command notes:\n\n- The registered commands mirror OpenClaw’s chat commands.\n- Native commands honor the same allowlists as DMs/guild messages (`channels.discord.dm.allowFrom`, `channels.discord.guilds`, per-channel rules).\n- Slash commands may still be visible in Discord UI to users who aren’t allowlisted; OpenClaw enforces allowlists on execution and replies “not authorized”.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Tool actions","content":"The agent can call `discord` with actions like:\n\n- `react` / `reactions` (add or list reactions)\n- `sticker`, `poll`, `permissions`\n- `readMessages`, `sendMessage`, `editMessage`, `deleteMessage`\n- Read/search/pin tool payloads include normalized `timestampMs` (UTC epoch ms) and `timestampUtc` alongside raw Discord `timestamp`.\n- `threadCreate`, `threadList`, `threadReply`\n- `pinMessage`, `unpinMessage`, `listPins`\n- `searchMessages`, `memberInfo`, `roleInfo`, `roleAdd`, `roleRemove`, `emojiList`\n- `channelInfo`, `channelList`, `voiceStatus`, `eventList`, `eventCreate`\n- `timeout`, `kick`, `ban`\n\nDiscord message ids are surfaced in the injected context (`[discord message id: …]` and history lines) so the agent can target them.\nEmoji can be unicode (e.g., `✅`) or custom emoji syntax like `<:party_blob:1234567890>`.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/discord.md","title":"Safety & ops","content":"- Treat the bot token like a password; prefer the `DISCORD_BOT_TOKEN` env var on supervised hosts or lock down the config file permissions.\n- Only grant the bot permissions it needs (typically Read/Send Messages).\n- If the bot is stuck or rate limited, restart the gateway (`openclaw gateway --force`) after confirming no other processes own the Discord session.","url":"https://docs.openclaw.ai/channels/discord"},{"path":"channels/googlechat.md","title":"googlechat","content":"# Google Chat (Chat API)\n\nStatus: ready for DMs + spaces via Google Chat API webhooks (HTTP only).","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"Quick setup (beginner)","content":"1. Create a Google Cloud project and enable the **Google Chat API**.\n - Go to: [Google Chat API Credentials](https://console.cloud.google.com/apis/api/chat.googleapis.com/credentials)\n - Enable the API if it is not already enabled.\n2. Create a **Service Account**:\n - Press **Create Credentials** > **Service Account**.\n - Name it whatever you want (e.g., `openclaw-chat`).\n - Leave permissions blank (press **Continue**).\n - Leave principals with access blank (press **Done**).\n3. Create and download the **JSON Key**:\n - In the list of service accounts, click on the one you just created.\n - Go to the **Keys** tab.\n - Click **Add Key** > **Create new key**.\n - Select **JSON** and press **Create**.\n4. Store the downloaded JSON file on your gateway host (e.g., `~/.openclaw/googlechat-service-account.json`).\n5. Create a Google Chat app in the [Google Cloud Console Chat Configuration](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat):\n - Fill in the **Application info**:\n - **App name**: (e.g. `OpenClaw`)\n - **Avatar URL**: (e.g. `https://openclaw.ai/logo.png`)\n - **Description**: (e.g. `Personal AI Assistant`)\n - Enable **Interactive features**.\n - Under **Functionality**, check **Join spaces and group conversations**.\n - Under **Connection settings**, select **HTTP endpoint URL**.\n - Under **Triggers**, select **Use a common HTTP endpoint URL for all triggers** and set it to your gateway's public URL followed by `/googlechat`.\n - _Tip: Run `openclaw status` to find your gateway's public URL._\n - Under **Visibility**, check **Make this Chat app available to specific people and groups in <Your Domain>**.\n - Enter your email address (e.g. `user@example.com`) in the text box.\n - Click **Save** at the bottom.\n6. **Enable the app status**:\n - After saving, **refresh the page**.\n - Look for the **App status** section (usually near the top or bottom after saving).\n - Change the status to **Live - available to users**.\n - Click **Save** again.\n7. Configure OpenClaw with the service account path + webhook audience:\n - Env: `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE=/path/to/service-account.json`\n - Or config: `channels.googlechat.serviceAccountFile: \"/path/to/service-account.json\"`.\n8. Set the webhook audience type + value (matches your Chat app config).\n9. Start the gateway. Google Chat will POST to your webhook path.","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"Add to Google Chat","content":"Once the gateway is running and your email is added to the visibility list:\n\n1. Go to [Google Chat](https://chat.google.com/).\n2. Click the **+** (plus) icon next to **Direct Messages**.\n3. In the search bar (where you usually add people), type the **App name** you configured in the Google Cloud Console.\n - **Note**: The bot will _not_ appear in the \"Marketplace\" browse list because it is a private app. You must search for it by name.\n4. Select your bot from the results.\n5. Click **Add** or **Chat** to start a 1:1 conversation.\n6. Send \"Hello\" to trigger the assistant!","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"Public URL (Webhook-only)","content":"Google Chat webhooks require a public HTTPS endpoint. For security, **only expose the `/googlechat` path** to the internet. Keep the OpenClaw dashboard and other sensitive endpoints on your private network.\n\n### Option A: Tailscale Funnel (Recommended)\n\nUse Tailscale Serve for the private dashboard and Funnel for the public webhook path. This keeps `/` private while exposing only `/googlechat`.\n\n1. **Check what address your gateway is bound to:**\n\n ```bash\n ss -tlnp | grep 18789\n ```\n\n Note the IP address (e.g., `127.0.0.1`, `0.0.0.0`, or your Tailscale IP like `100.x.x.x`).\n\n2. **Expose the dashboard to the tailnet only (port 8443):**\n\n ```bash\n # If bound to localhost (127.0.0.1 or 0.0.0.0):\n tailscale serve --bg --https 8443 http://127.0.0.1:18789\n\n # If bound to Tailscale IP only (e.g., 100.106.161.80):\n tailscale serve --bg --https 8443 http://100.106.161.80:18789\n ```\n\n3. **Expose only the webhook path publicly:**\n\n ```bash\n # If bound to localhost (127.0.0.1 or 0.0.0.0):\n tailscale funnel --bg --set-path /googlechat http://127.0.0.1:18789/googlechat\n\n # If bound to Tailscale IP only (e.g., 100.106.161.80):\n tailscale funnel --bg --set-path /googlechat http://100.106.161.80:18789/googlechat\n ```\n\n4. **Authorize the node for Funnel access:**\n If prompted, visit the authorization URL shown in the output to enable Funnel for this node in your tailnet policy.\n\n5. **Verify the configuration:**\n ```bash\n tailscale serve status\n tailscale funnel status\n ```\n\nYour public webhook URL will be:\n`https://..ts.net/googlechat`\n\nYour private dashboard stays tailnet-only:\n`https://..ts.net:8443/`\n\nUse the public URL (without `:8443`) in the Google Chat app config.\n\n> Note: This configuration persists across reboots. To remove it later, run `tailscale funnel reset` and `tailscale serve reset`.\n\n### Option B: Reverse Proxy (Caddy)\n\nIf you use a reverse proxy like Caddy, only proxy the specific path:\n\n```caddy\nyour-domain.com {\n reverse_proxy /googlechat* localhost:18789\n}\n```\n\nWith this config, any request to `your-domain.com/` will be ignored or returned as 404, while `your-domain.com/googlechat` is safely routed to OpenClaw.\n\n### Option C: Cloudflare Tunnel\n\nConfigure your tunnel's ingress rules to only route the webhook path:\n\n- **Path**: `/googlechat` -> `http://localhost:18789/googlechat`\n- **Default Rule**: HTTP 404 (Not Found)","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"How it works","content":"1. Google Chat sends webhook POSTs to the gateway. Each request includes an `Authorization: Bearer ` header.\n2. OpenClaw verifies the token against the configured `audienceType` + `audience`:\n - `audienceType: \"app-url\"` → audience is your HTTPS webhook URL.\n - `audienceType: \"project-number\"` → audience is the Cloud project number.\n3. Messages are routed by space:\n - DMs use session key `agent::googlechat:dm:`.\n - Spaces use session key `agent::googlechat:group:`.\n4. DM access is pairing by default. Unknown senders receive a pairing code; approve with:\n - `openclaw pairing approve googlechat `\n5. Group spaces require @-mention by default. Use `botUser` if mention detection needs the app’s user name.","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"Targets","content":"Use these identifiers for delivery and allowlists:\n\n- Direct messages: `users/` or `users/` (email addresses are accepted).\n- Spaces: `spaces/`.","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"Config highlights","content":"```json5\n{\n channels: {\n googlechat: {\n enabled: true,\n serviceAccountFile: \"/path/to/service-account.json\",\n audienceType: \"app-url\",\n audience: \"https://gateway.example.com/googlechat\",\n webhookPath: \"/googlechat\",\n botUser: \"users/1234567890\", // optional; helps mention detection\n dm: {\n policy: \"pairing\",\n allowFrom: [\"users/1234567890\", \"name@example.com\"],\n },\n groupPolicy: \"allowlist\",\n groups: {\n \"spaces/AAAA\": {\n allow: true,\n requireMention: true,\n users: [\"users/1234567890\"],\n systemPrompt: \"Short answers only.\",\n },\n },\n actions: { reactions: true },\n typingIndicator: \"message\",\n mediaMaxMb: 20,\n },\n },\n}\n```\n\nNotes:\n\n- Service account credentials can also be passed inline with `serviceAccount` (JSON string).\n- Default webhook path is `/googlechat` if `webhookPath` isn’t set.\n- Reactions are available via the `reactions` tool and `channels action` when `actions.reactions` is enabled.\n- `typingIndicator` supports `none`, `message` (default), and `reaction` (reaction requires user OAuth).\n- Attachments are downloaded through the Chat API and stored in the media pipeline (size capped by `mediaMaxMb`).","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/googlechat.md","title":"Troubleshooting","content":"### 405 Method Not Allowed\n\nIf Google Cloud Logs Explorer shows errors like:\n\n```\nstatus code: 405, reason phrase: HTTP error response: HTTP/1.1 405 Method Not Allowed\n```\n\nThis means the webhook handler isn't registered. Common causes:\n\n1. **Channel not configured**: The `channels.googlechat` section is missing from your config. Verify with:\n\n ```bash\n openclaw config get channels.googlechat\n ```\n\n If it returns \"Config path not found\", add the configuration (see [Config highlights](#config-highlights)).\n\n2. **Plugin not enabled**: Check plugin status:\n\n ```bash\n openclaw plugins list | grep googlechat\n ```\n\n If it shows \"disabled\", add `plugins.entries.googlechat.enabled: true` to your config.\n\n3. **Gateway not restarted**: After adding config, restart the gateway:\n ```bash\n openclaw gateway restart\n ```\n\nVerify the channel is running:\n\n```bash\nopenclaw channels status\n# Should show: Google Chat default: enabled, configured, ...\n```\n\n### Other issues\n\n- Check `openclaw channels status --probe` for auth errors or missing audience config.\n- If no messages arrive, confirm the Chat app's webhook URL + event subscriptions.\n- If mention gating blocks replies, set `botUser` to the app's user resource name and verify `requireMention`.\n- Use `openclaw logs --follow` while sending a test message to see if requests reach the gateway.\n\nRelated docs:\n\n- [Gateway configuration](/gateway/configuration)\n- [Security](/gateway/security)\n- [Reactions](/tools/reactions)","url":"https://docs.openclaw.ai/channels/googlechat"},{"path":"channels/grammy.md","title":"grammy","content":"# grammY Integration (Telegram Bot API)\n\n# Why grammY\n\n- TS-first Bot API client with built-in long-poll + webhook helpers, middleware, error handling, rate limiter.\n- Cleaner media helpers than hand-rolling fetch + FormData; supports all Bot API methods.\n- Extensible: proxy support via custom fetch, session middleware (optional), type-safe context.\n\n# What we shipped\n\n- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default.\n- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`.\n- **Proxy:** optional `channels.telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`.\n- **Webhook support:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown. Gateway enables webhook mode when `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` are set (otherwise it long-polls).\n- **Sessions:** direct chats collapse into the agent main session (`agent::`); groups use `agent::telegram:group:`; replies route back to the same channel.\n- **Config knobs:** `channels.telegram.botToken`, `channels.telegram.dmPolicy`, `channels.telegram.groups` (allowlist + mention defaults), `channels.telegram.allowFrom`, `channels.telegram.groupAllowFrom`, `channels.telegram.groupPolicy`, `channels.telegram.mediaMaxMb`, `channels.telegram.linkPreview`, `channels.telegram.proxy`, `channels.telegram.webhookSecret`, `channels.telegram.webhookUrl`.\n- **Draft streaming:** optional `channels.telegram.streamMode` uses `sendMessageDraft` in private topic chats (Bot API 9.3+). This is separate from channel block streaming.\n- **Tests:** grammy mocks cover DM + group mention gating and outbound send; more media/webhook fixtures still welcome.\n\nOpen questions\n\n- Optional grammY plugins (throttler) if we hit Bot API 429s.\n- Add more structured media tests (stickers, voice notes).\n- Make webhook listen port configurable (currently fixed to 8787 unless wired through the gateway).","url":"https://docs.openclaw.ai/channels/grammy"},{"path":"channels/imessage.md","title":"imessage","content":"# iMessage (imsg)\n\nStatus: external CLI integration. Gateway spawns `imsg rpc` (JSON-RPC over stdio).","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Quick setup (beginner)","content":"1. Ensure Messages is signed in on this Mac.\n2. Install `imsg`:\n - `brew install steipete/tap/imsg`\n3. Configure OpenClaw with `channels.imessage.cliPath` and `channels.imessage.dbPath`.\n4. Start the gateway and approve any macOS prompts (Automation + Full Disk Access).\n\nMinimal config:\n\n```json5\n{\n channels: {\n imessage: {\n enabled: true,\n cliPath: \"/usr/local/bin/imsg\",\n dbPath: \"/Users//Library/Messages/chat.db\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"What it is","content":"- iMessage channel backed by `imsg` on macOS.\n- Deterministic routing: replies always go back to iMessage.\n- DMs share the agent's main session; groups are isolated (`agent::imessage:group:`).\n- If a multi-participant thread arrives with `is_group=false`, you can still isolate it by `chat_id` using `channels.imessage.groups` (see “Group-ish threads” below).","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Config writes","content":"By default, iMessage is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).\n\nDisable with:\n\n```json5\n{\n channels: { imessage: { configWrites: false } },\n}\n```","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Requirements","content":"- macOS with Messages signed in.\n- Full Disk Access for OpenClaw + `imsg` (Messages DB access).\n- Automation permission when sending.\n- `channels.imessage.cliPath` can point to any command that proxies stdin/stdout (for example, a wrapper script that SSHes to another Mac and runs `imsg rpc`).","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Setup (fast path)","content":"1. Ensure Messages is signed in on this Mac.\n2. Configure iMessage and start the gateway.\n\n### Dedicated bot macOS user (for isolated identity)\n\nIf you want the bot to send from a **separate iMessage identity** (and keep your personal Messages clean), use a dedicated Apple ID + a dedicated macOS user.\n\n1. Create a dedicated Apple ID (example: `my-cool-bot@icloud.com`).\n - Apple may require a phone number for verification / 2FA.\n2. Create a macOS user (example: `openclawhome`) and sign into it.\n3. Open Messages in that macOS user and sign into iMessage using the bot Apple ID.\n4. Enable Remote Login (System Settings → General → Sharing → Remote Login).\n5. Install `imsg`:\n - `brew install steipete/tap/imsg`\n6. Set up SSH so `ssh @localhost true` works without a password.\n7. Point `channels.imessage.accounts.bot.cliPath` at an SSH wrapper that runs `imsg` as the bot user.\n\nFirst-run note: sending/receiving may require GUI approvals (Automation + Full Disk Access) in the _bot macOS user_. If `imsg rpc` looks stuck or exits, log into that user (Screen Sharing helps), run a one-time `imsg chats --limit 1` / `imsg send ...`, approve prompts, then retry.\n\nExample wrapper (`chmod +x`). Replace `` with your actual macOS username:\n\n```bash\n#!/usr/bin/env bash\nset -euo pipefail\n\n# Run an interactive SSH once first to accept host keys:\n# ssh @localhost true\nexec /usr/bin/ssh -o BatchMode=yes -o ConnectTimeout=5 -T @localhost \\\n \"/usr/local/bin/imsg\" \"$@\"\n```\n\nExample config:\n\n```json5\n{\n channels: {\n imessage: {\n enabled: true,\n accounts: {\n bot: {\n name: \"Bot\",\n enabled: true,\n cliPath: \"/path/to/imsg-bot\",\n dbPath: \"/Users//Library/Messages/chat.db\",\n },\n },\n },\n },\n}\n```\n\nFor single-account setups, use flat options (`channels.imessage.cliPath`, `channels.imessage.dbPath`) instead of the `accounts` map.\n\n### Remote/SSH variant (optional)\n\nIf you want iMessage on another Mac, set `channels.imessage.cliPath` to a wrapper that runs `imsg` on the remote macOS host over SSH. OpenClaw only needs stdio.\n\nExample wrapper:\n\n```bash\n#!/usr/bin/env bash\nexec ssh -T gateway-host imsg \"$@\"\n```\n\n**Remote attachments:** When `cliPath` points to a remote host via SSH, attachment paths in the Messages database reference files on the remote machine. OpenClaw can automatically fetch these over SCP by setting `channels.imessage.remoteHost`:\n\n```json5\n{\n channels: {\n imessage: {\n cliPath: \"~/imsg-ssh\", // SSH wrapper to remote Mac\n remoteHost: \"user@gateway-host\", // for SCP file transfer\n includeAttachments: true,\n },\n },\n}\n```\n\nIf `remoteHost` is not set, OpenClaw attempts to auto-detect it by parsing the SSH command in your wrapper script. Explicit configuration is recommended for reliability.\n\n#### Remote Mac via Tailscale (example)\n\nIf the Gateway runs on a Linux host/VM but iMessage must run on a Mac, Tailscale is the simplest bridge: the Gateway talks to the Mac over the tailnet, runs `imsg` via SSH, and SCPs attachments back.\n\nArchitecture:\n\n```\n┌──────────────────────────────┐ SSH (imsg rpc) ┌──────────────────────────┐\n│ Gateway host (Linux/VM) │──────────────────────────────────▶│ Mac with Messages + imsg │\n│ - openclaw gateway │ SCP (attachments) │ - Messages signed in │\n│ - channels.imessage.cliPath │◀──────────────────────────────────│ - Remote Login enabled │\n└──────────────────────────────┘ └──────────────────────────┘\n ▲\n │ Tailscale tailnet (hostname or 100.x.y.z)\n ▼\n user@gateway-host\n```\n\nConcrete config example (Tailscale hostname):\n\n```json5\n{\n channels: {\n imessage: {\n enabled: true,\n cliPath: \"~/.openclaw/scripts/imsg-ssh\",\n remoteHost: \"bot@mac-mini.tailnet-1234.ts.net\",\n includeAttachments: true,\n dbPath: \"/Users/bot/Library/Messages/chat.db\",\n },\n },\n}\n```\n\nExample wrapper (`~/.openclaw/scripts/imsg-ssh`):\n\n```bash\n#!/usr/bin/env bash\nexec ssh -T bot@mac-mini.tailnet-1234.ts.net imsg \"$@\"\n```\n\nNotes:\n\n- Ensure the Mac is signed in to Messages, and Remote Login is enabled.\n- Use SSH keys so `ssh bot@mac-mini.tailnet-1234.ts.net` works without prompts.\n- `remoteHost` should match the SSH target so SCP can fetch attachments.\n\nMulti-account support: use `channels.imessage.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern. Don't commit `~/.openclaw/openclaw.json` (it often contains tokens).","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Access control (DMs + groups)","content":"DMs:\n\n- Default: `channels.imessage.dmPolicy = \"pairing\"`.\n- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).\n- Approve via:\n - `openclaw pairing list imessage`\n - `openclaw pairing approve imessage `\n- Pairing is the default token exchange for iMessage DMs. Details: [Pairing](/start/pairing)\n\nGroups:\n\n- `channels.imessage.groupPolicy = open | allowlist | disabled`.\n- `channels.imessage.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.\n- Mention gating uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) because iMessage has no native mention metadata.\n- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"How it works (behavior)","content":"- `imsg` streams message events; the gateway normalizes them into the shared channel envelope.\n- Replies always route back to the same chat id or handle.","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Group-ish threads (`is_group=false`)","content":"Some iMessage threads can have multiple participants but still arrive with `is_group=false` depending on how Messages stores the chat identifier.\n\nIf you explicitly configure a `chat_id` under `channels.imessage.groups`, OpenClaw treats that thread as a “group” for:\n\n- session isolation (separate `agent::imessage:group:` session key)\n- group allowlisting / mention gating behavior\n\nExample:\n\n```json5\n{\n channels: {\n imessage: {\n groupPolicy: \"allowlist\",\n groupAllowFrom: [\"+15555550123\"],\n groups: {\n \"42\": { requireMention: false },\n },\n },\n },\n}\n```\n\nThis is useful when you want an isolated personality/model for a specific thread (see [Multi-agent routing](/concepts/multi-agent)). For filesystem isolation, see [Sandboxing](/gateway/sandboxing).","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Media + limits","content":"- Optional attachment ingestion via `channels.imessage.includeAttachments`.\n- Media cap via `channels.imessage.mediaMaxMb`.","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Limits","content":"- Outbound text is chunked to `channels.imessage.textChunkLimit` (default 4000).\n- Optional newline chunking: set `channels.imessage.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n- Media uploads are capped by `channels.imessage.mediaMaxMb` (default 16).","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Addressing / delivery targets","content":"Prefer `chat_id` for stable routing:\n\n- `chat_id:123` (preferred)\n- `chat_guid:...`\n- `chat_identifier:...`\n- direct handles: `imessage:+1555` / `sms:+1555` / `user@example.com`\n\nList chats:\n\n```\nimsg chats --limit 20\n```","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/imessage.md","title":"Configuration reference (iMessage)","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.imessage.enabled`: enable/disable channel startup.\n- `channels.imessage.cliPath`: path to `imsg`.\n- `channels.imessage.dbPath`: Messages DB path.\n- `channels.imessage.remoteHost`: SSH host for SCP attachment transfer when `cliPath` points to a remote Mac (e.g., `user@gateway-host`). Auto-detected from SSH wrapper if not set.\n- `channels.imessage.service`: `imessage | sms | auto`.\n- `channels.imessage.region`: SMS region.\n- `channels.imessage.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).\n- `channels.imessage.allowFrom`: DM allowlist (handles, emails, E.164 numbers, or `chat_id:*`). `open` requires `\"*\"`. iMessage has no usernames; use handles or chat targets.\n- `channels.imessage.groupPolicy`: `open | allowlist | disabled` (default: allowlist).\n- `channels.imessage.groupAllowFrom`: group sender allowlist.\n- `channels.imessage.historyLimit` / `channels.imessage.accounts.*.historyLimit`: max group messages to include as context (0 disables).\n- `channels.imessage.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.imessage.dms[\"\"].historyLimit`.\n- `channels.imessage.groups`: per-group defaults + allowlist (use `\"*\"` for global defaults).\n- `channels.imessage.includeAttachments`: ingest attachments into context.\n- `channels.imessage.mediaMaxMb`: inbound/outbound media cap (MB).\n- `channels.imessage.textChunkLimit`: outbound chunk size (chars).\n- `channels.imessage.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n\nRelated global options:\n\n- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).\n- `messages.responsePrefix`.","url":"https://docs.openclaw.ai/channels/imessage"},{"path":"channels/index.md","title":"index","content":"# Chat Channels\n\nOpenClaw can talk to you on any chat app you already use. Each channel connects via the Gateway.\nText is supported everywhere; media and reactions vary by channel.","url":"https://docs.openclaw.ai/channels/index"},{"path":"channels/index.md","title":"Supported channels","content":"- [WhatsApp](/channels/whatsapp) — Most popular; uses Baileys and requires QR pairing.\n- [Telegram](/channels/telegram) — Bot API via grammY; supports groups.\n- [Discord](/channels/discord) — Discord Bot API + Gateway; supports servers, channels, and DMs.\n- [Slack](/channels/slack) — Bolt SDK; workspace apps.\n- [Google Chat](/channels/googlechat) — Google Chat API app via HTTP webhook.\n- [Mattermost](/channels/mattermost) — Bot API + WebSocket; channels, groups, DMs (plugin, installed separately).\n- [Signal](/channels/signal) — signal-cli; privacy-focused.\n- [BlueBubbles](/channels/bluebubbles) — **Recommended for iMessage**; uses the BlueBubbles macOS server REST API with full feature support (edit, unsend, effects, reactions, group management — edit currently broken on macOS 26 Tahoe).\n- [iMessage](/channels/imessage) — macOS only; native integration via imsg (legacy, consider BlueBubbles for new setups).\n- [Microsoft Teams](/channels/msteams) — Bot Framework; enterprise support (plugin, installed separately).\n- [LINE](/channels/line) — LINE Messaging API bot (plugin, installed separately).\n- [Nextcloud Talk](/channels/nextcloud-talk) — Self-hosted chat via Nextcloud Talk (plugin, installed separately).\n- [Matrix](/channels/matrix) — Matrix protocol (plugin, installed separately).\n- [Nostr](/channels/nostr) — Decentralized DMs via NIP-04 (plugin, installed separately).\n- [Tlon](/channels/tlon) — Urbit-based messenger (plugin, installed separately).\n- [Twitch](/channels/twitch) — Twitch chat via IRC connection (plugin, installed separately).\n- [Zalo](/channels/zalo) — Zalo Bot API; Vietnam's popular messenger (plugin, installed separately).\n- [Zalo Personal](/channels/zalouser) — Zalo personal account via QR login (plugin, installed separately).\n- [WebChat](/web/webchat) — Gateway WebChat UI over WebSocket.","url":"https://docs.openclaw.ai/channels/index"},{"path":"channels/index.md","title":"Notes","content":"- Channels can run simultaneously; configure multiple and OpenClaw will route per chat.\n- Fastest setup is usually **Telegram** (simple bot token). WhatsApp requires QR pairing and\n stores more state on disk.\n- Group behavior varies by channel; see [Groups](/concepts/groups).\n- DM pairing and allowlists are enforced for safety; see [Security](/gateway/security).\n- Telegram internals: [grammY notes](/channels/grammy).\n- Troubleshooting: [Channel troubleshooting](/channels/troubleshooting).\n- Model providers are documented separately; see [Model Providers](/providers/models).","url":"https://docs.openclaw.ai/channels/index"},{"path":"channels/line.md","title":"line","content":"# LINE (plugin)\n\nLINE connects to OpenClaw via the LINE Messaging API. The plugin runs as a webhook\nreceiver on the gateway and uses your channel access token + channel secret for\nauthentication.\n\nStatus: supported via plugin. Direct messages, group chats, media, locations, Flex\nmessages, template messages, and quick replies are supported. Reactions and threads\nare not supported.","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Plugin required","content":"Install the LINE plugin:\n\n```bash\nopenclaw plugins install @openclaw/line\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/line\n```","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Setup","content":"1. Create a LINE Developers account and open the Console:\n https://developers.line.biz/console/\n2. Create (or pick) a Provider and add a **Messaging API** channel.\n3. Copy the **Channel access token** and **Channel secret** from the channel settings.\n4. Enable **Use webhook** in the Messaging API settings.\n5. Set the webhook URL to your gateway endpoint (HTTPS required):\n\n```\nhttps://gateway-host/line/webhook\n```\n\nThe gateway responds to LINE’s webhook verification (GET) and inbound events (POST).\nIf you need a custom path, set `channels.line.webhookPath` or\n`channels.line.accounts..webhookPath` and update the URL accordingly.","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Configure","content":"Minimal config:\n\n```json5\n{\n channels: {\n line: {\n enabled: true,\n channelAccessToken: \"LINE_CHANNEL_ACCESS_TOKEN\",\n channelSecret: \"LINE_CHANNEL_SECRET\",\n dmPolicy: \"pairing\",\n },\n },\n}\n```\n\nEnv vars (default account only):\n\n- `LINE_CHANNEL_ACCESS_TOKEN`\n- `LINE_CHANNEL_SECRET`\n\nToken/secret files:\n\n```json5\n{\n channels: {\n line: {\n tokenFile: \"/path/to/line-token.txt\",\n secretFile: \"/path/to/line-secret.txt\",\n },\n },\n}\n```\n\nMultiple accounts:\n\n```json5\n{\n channels: {\n line: {\n accounts: {\n marketing: {\n channelAccessToken: \"...\",\n channelSecret: \"...\",\n webhookPath: \"/line/marketing\",\n },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Access control","content":"Direct messages default to pairing. Unknown senders get a pairing code and their\nmessages are ignored until approved.\n\n```bash\nopenclaw pairing list line\nopenclaw pairing approve line \n```\n\nAllowlists and policies:\n\n- `channels.line.dmPolicy`: `pairing | allowlist | open | disabled`\n- `channels.line.allowFrom`: allowlisted LINE user IDs for DMs\n- `channels.line.groupPolicy`: `allowlist | open | disabled`\n- `channels.line.groupAllowFrom`: allowlisted LINE user IDs for groups\n- Per-group overrides: `channels.line.groups..allowFrom`\n\nLINE IDs are case-sensitive. Valid IDs look like:\n\n- User: `U` + 32 hex chars\n- Group: `C` + 32 hex chars\n- Room: `R` + 32 hex chars","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Message behavior","content":"- Text is chunked at 5000 characters.\n- Markdown formatting is stripped; code blocks and tables are converted into Flex\n cards when possible.\n- Streaming responses are buffered; LINE receives full chunks with a loading\n animation while the agent works.\n- Media downloads are capped by `channels.line.mediaMaxMb` (default 10).","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Channel data (rich messages)","content":"Use `channelData.line` to send quick replies, locations, Flex cards, or template\nmessages.\n\n```json5\n{\n text: \"Here you go\",\n channelData: {\n line: {\n quickReplies: [\"Status\", \"Help\"],\n location: {\n title: \"Office\",\n address: \"123 Main St\",\n latitude: 35.681236,\n longitude: 139.767125,\n },\n flexMessage: {\n altText: \"Status card\",\n contents: {\n /* Flex payload */\n },\n },\n templateMessage: {\n type: \"confirm\",\n text: \"Proceed?\",\n confirmLabel: \"Yes\",\n confirmData: \"yes\",\n cancelLabel: \"No\",\n cancelData: \"no\",\n },\n },\n },\n}\n```\n\nThe LINE plugin also ships a `/card` command for Flex message presets:\n\n```\n/card info \"Welcome\" \"Thanks for joining!\"\n```","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/line.md","title":"Troubleshooting","content":"- **Webhook verification fails:** ensure the webhook URL is HTTPS and the\n `channelSecret` matches the LINE console.\n- **No inbound events:** confirm the webhook path matches `channels.line.webhookPath`\n and that the gateway is reachable from LINE.\n- **Media download errors:** raise `channels.line.mediaMaxMb` if media exceeds the\n default limit.","url":"https://docs.openclaw.ai/channels/line"},{"path":"channels/location.md","title":"location","content":"# Channel location parsing\n\nOpenClaw normalizes shared locations from chat channels into:\n\n- human-readable text appended to the inbound body, and\n- structured fields in the auto-reply context payload.\n\nCurrently supported:\n\n- **Telegram** (location pins + venues + live locations)\n- **WhatsApp** (locationMessage + liveLocationMessage)\n- **Matrix** (`m.location` with `geo_uri`)","url":"https://docs.openclaw.ai/channels/location"},{"path":"channels/location.md","title":"Text formatting","content":"Locations are rendered as friendly lines without brackets:\n\n- Pin:\n - `📍 48.858844, 2.294351 ±12m`\n- Named place:\n - `📍 Eiffel Tower — Champ de Mars, Paris (48.858844, 2.294351 ±12m)`\n- Live share:\n - `🛰 Live location: 48.858844, 2.294351 ±12m`\n\nIf the channel includes a caption/comment, it is appended on the next line:\n\n```\n📍 48.858844, 2.294351 ±12m\nMeet here\n```","url":"https://docs.openclaw.ai/channels/location"},{"path":"channels/location.md","title":"Context fields","content":"When a location is present, these fields are added to `ctx`:\n\n- `LocationLat` (number)\n- `LocationLon` (number)\n- `LocationAccuracy` (number, meters; optional)\n- `LocationName` (string; optional)\n- `LocationAddress` (string; optional)\n- `LocationSource` (`pin | place | live`)\n- `LocationIsLive` (boolean)","url":"https://docs.openclaw.ai/channels/location"},{"path":"channels/location.md","title":"Channel notes","content":"- **Telegram**: venues map to `LocationName/LocationAddress`; live locations use `live_period`.\n- **WhatsApp**: `locationMessage.comment` and `liveLocationMessage.caption` are appended as the caption line.\n- **Matrix**: `geo_uri` is parsed as a pin location; altitude is ignored and `LocationIsLive` is always false.","url":"https://docs.openclaw.ai/channels/location"},{"path":"channels/matrix.md","title":"matrix","content":"# Matrix (plugin)\n\nMatrix is an open, decentralized messaging protocol. OpenClaw connects as a Matrix **user**\non any homeserver, so you need a Matrix account for the bot. Once it is logged in, you can DM\nthe bot directly or invite it to rooms (Matrix \"groups\"). Beeper is a valid client option too,\nbut it requires E2EE to be enabled.\n\nStatus: supported via plugin (@vector-im/matrix-bot-sdk). Direct messages, rooms, threads, media, reactions,\npolls (send + poll-start as text), location, and E2EE (with crypto support).","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Plugin required","content":"Matrix ships as a plugin and is not bundled with the core install.\n\nInstall via CLI (npm registry):\n\n```bash\nopenclaw plugins install @openclaw/matrix\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/matrix\n```\n\nIf you choose Matrix during configure/onboarding and a git checkout is detected,\nOpenClaw will offer the local install path automatically.\n\nDetails: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Setup","content":"1. Install the Matrix plugin:\n - From npm: `openclaw plugins install @openclaw/matrix`\n - From a local checkout: `openclaw plugins install ./extensions/matrix`\n2. Create a Matrix account on a homeserver:\n - Browse hosting options at [https://matrix.org/ecosystem/hosting/](https://matrix.org/ecosystem/hosting/)\n - Or host it yourself.\n3. Get an access token for the bot account:\n - Use the Matrix login API with `curl` at your home server:\n\n ```bash\n curl --request POST \\\n --url https://matrix.example.org/_matrix/client/v3/login \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"type\": \"m.login.password\",\n \"identifier\": {\n \"type\": \"m.id.user\",\n \"user\": \"your-user-name\"\n },\n \"password\": \"your-password\"\n }'\n ```\n\n - Replace `matrix.example.org` with your homeserver URL.\n - Or set `channels.matrix.userId` + `channels.matrix.password`: OpenClaw calls the same\n login endpoint, stores the access token in `~/.openclaw/credentials/matrix/credentials.json`,\n and reuses it on next start.\n\n4. Configure credentials:\n - Env: `MATRIX_HOMESERVER`, `MATRIX_ACCESS_TOKEN` (or `MATRIX_USER_ID` + `MATRIX_PASSWORD`)\n - Or config: `channels.matrix.*`\n - If both are set, config takes precedence.\n - With access token: user ID is fetched automatically via `/whoami`.\n - When set, `channels.matrix.userId` should be the full Matrix ID (example: `@bot:example.org`).\n5. Restart the gateway (or finish onboarding).\n6. Start a DM with the bot or invite it to a room from any Matrix client\n (Element, Beeper, etc.; see https://matrix.org/ecosystem/clients/). Beeper requires E2EE,\n so set `channels.matrix.encryption: true` and verify the device.\n\nMinimal config (access token, user ID auto-fetched):\n\n```json5\n{\n channels: {\n matrix: {\n enabled: true,\n homeserver: \"https://matrix.example.org\",\n accessToken: \"syt_***\",\n dm: { policy: \"pairing\" },\n },\n },\n}\n```\n\nE2EE config (end to end encryption enabled):\n\n```json5\n{\n channels: {\n matrix: {\n enabled: true,\n homeserver: \"https://matrix.example.org\",\n accessToken: \"syt_***\",\n encryption: true,\n dm: { policy: \"pairing\" },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Encryption (E2EE)","content":"End-to-end encryption is **supported** via the Rust crypto SDK.\n\nEnable with `channels.matrix.encryption: true`:\n\n- If the crypto module loads, encrypted rooms are decrypted automatically.\n- Outbound media is encrypted when sending to encrypted rooms.\n- On first connection, OpenClaw requests device verification from your other sessions.\n- Verify the device in another Matrix client (Element, etc.) to enable key sharing.\n- If the crypto module cannot be loaded, E2EE is disabled and encrypted rooms will not decrypt;\n OpenClaw logs a warning.\n- If you see missing crypto module errors (for example, `@matrix-org/matrix-sdk-crypto-nodejs-*`),\n allow build scripts for `@matrix-org/matrix-sdk-crypto-nodejs` and run\n `pnpm rebuild @matrix-org/matrix-sdk-crypto-nodejs` or fetch the binary with\n `node node_modules/@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js`.\n\nCrypto state is stored per account + access token in\n`~/.openclaw/matrix/accounts//__//crypto/`\n(SQLite database). Sync state lives alongside it in `bot-storage.json`.\nIf the access token (device) changes, a new store is created and the bot must be\nre-verified for encrypted rooms.\n\n**Device verification:**\nWhen E2EE is enabled, the bot will request verification from your other sessions on startup.\nOpen Element (or another client) and approve the verification request to establish trust.\nOnce verified, the bot can decrypt messages in encrypted rooms.","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Routing model","content":"- Replies always go back to Matrix.\n- DMs share the agent's main session; rooms map to group sessions.","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Access control (DMs)","content":"- Default: `channels.matrix.dm.policy = \"pairing\"`. Unknown senders get a pairing code.\n- Approve via:\n - `openclaw pairing list matrix`\n - `openclaw pairing approve matrix `\n- Public DMs: `channels.matrix.dm.policy=\"open\"` plus `channels.matrix.dm.allowFrom=[\"*\"]`.\n- `channels.matrix.dm.allowFrom` accepts user IDs or display names. The wizard resolves display names to user IDs when directory search is available.","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Rooms (groups)","content":"- Default: `channels.matrix.groupPolicy = \"allowlist\"` (mention-gated). Use `channels.defaults.groupPolicy` to override the default when unset.\n- Allowlist rooms with `channels.matrix.groups` (room IDs, aliases, or names):\n\n```json5\n{\n channels: {\n matrix: {\n groupPolicy: \"allowlist\",\n groups: {\n \"!roomId:example.org\": { allow: true },\n \"#alias:example.org\": { allow: true },\n },\n groupAllowFrom: [\"@owner:example.org\"],\n },\n },\n}\n```\n\n- `requireMention: false` enables auto-reply in that room.\n- `groups.\"*\"` can set defaults for mention gating across rooms.\n- `groupAllowFrom` restricts which senders can trigger the bot in rooms (optional).\n- Per-room `users` allowlists can further restrict senders inside a specific room.\n- The configure wizard prompts for room allowlists (room IDs, aliases, or names) and resolves names when possible.\n- On startup, OpenClaw resolves room/user names in allowlists to IDs and logs the mapping; unresolved entries are kept as typed.\n- Invites are auto-joined by default; control with `channels.matrix.autoJoin` and `channels.matrix.autoJoinAllowlist`.\n- To allow **no rooms**, set `channels.matrix.groupPolicy: \"disabled\"` (or keep an empty allowlist).\n- Legacy key: `channels.matrix.rooms` (same shape as `groups`).","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Threads","content":"- Reply threading is supported.\n- `channels.matrix.threadReplies` controls whether replies stay in threads:\n - `off`, `inbound` (default), `always`\n- `channels.matrix.replyToMode` controls reply-to metadata when not replying in a thread:\n - `off` (default), `first`, `all`","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Capabilities","content":"| Feature | Status |\n| --------------- | ------------------------------------------------------------------------------------- |\n| Direct messages | ✅ Supported |\n| Rooms | ✅ Supported |\n| Threads | ✅ Supported |\n| Media | ✅ Supported |\n| E2EE | ✅ Supported (crypto module required) |\n| Reactions | ✅ Supported (send/read via tools) |\n| Polls | ✅ Send supported; inbound poll starts are converted to text (responses/ends ignored) |\n| Location | ✅ Supported (geo URI; altitude ignored) |\n| Native commands | ✅ Supported |","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/matrix.md","title":"Configuration reference (Matrix)","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.matrix.enabled`: enable/disable channel startup.\n- `channels.matrix.homeserver`: homeserver URL.\n- `channels.matrix.userId`: Matrix user ID (optional with access token).\n- `channels.matrix.accessToken`: access token.\n- `channels.matrix.password`: password for login (token stored).\n- `channels.matrix.deviceName`: device display name.\n- `channels.matrix.encryption`: enable E2EE (default: false).\n- `channels.matrix.initialSyncLimit`: initial sync limit.\n- `channels.matrix.threadReplies`: `off | inbound | always` (default: inbound).\n- `channels.matrix.textChunkLimit`: outbound text chunk size (chars).\n- `channels.matrix.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n- `channels.matrix.dm.policy`: `pairing | allowlist | open | disabled` (default: pairing).\n- `channels.matrix.dm.allowFrom`: DM allowlist (user IDs or display names). `open` requires `\"*\"`. The wizard resolves names to IDs when possible.\n- `channels.matrix.groupPolicy`: `allowlist | open | disabled` (default: allowlist).\n- `channels.matrix.groupAllowFrom`: allowlisted senders for group messages.\n- `channels.matrix.allowlistOnly`: force allowlist rules for DMs + rooms.\n- `channels.matrix.groups`: group allowlist + per-room settings map.\n- `channels.matrix.rooms`: legacy group allowlist/config.\n- `channels.matrix.replyToMode`: reply-to mode for threads/tags.\n- `channels.matrix.mediaMaxMb`: inbound/outbound media cap (MB).\n- `channels.matrix.autoJoin`: invite handling (`always | allowlist | off`, default: always).\n- `channels.matrix.autoJoinAllowlist`: allowed room IDs/aliases for auto-join.\n- `channels.matrix.actions`: per-action tool gating (reactions/messages/pins/memberInfo/channelInfo).","url":"https://docs.openclaw.ai/channels/matrix"},{"path":"channels/mattermost.md","title":"mattermost","content":"# Mattermost (plugin)\n\nStatus: supported via plugin (bot token + WebSocket events). Channels, groups, and DMs are supported.\nMattermost is a self-hostable team messaging platform; see the official site at\n[mattermost.com](https://mattermost.com) for product details and downloads.","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Plugin required","content":"Mattermost ships as a plugin and is not bundled with the core install.\n\nInstall via CLI (npm registry):\n\n```bash\nopenclaw plugins install @openclaw/mattermost\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/mattermost\n```\n\nIf you choose Mattermost during configure/onboarding and a git checkout is detected,\nOpenClaw will offer the local install path automatically.\n\nDetails: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Quick setup","content":"1. Install the Mattermost plugin.\n2. Create a Mattermost bot account and copy the **bot token**.\n3. Copy the Mattermost **base URL** (e.g., `https://chat.example.com`).\n4. Configure OpenClaw and start the gateway.\n\nMinimal config:\n\n```json5\n{\n channels: {\n mattermost: {\n enabled: true,\n botToken: \"mm-token\",\n baseUrl: \"https://chat.example.com\",\n dmPolicy: \"pairing\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Environment variables (default account)","content":"Set these on the gateway host if you prefer env vars:\n\n- `MATTERMOST_BOT_TOKEN=...`\n- `MATTERMOST_URL=https://chat.example.com`\n\nEnv vars apply only to the **default** account (`default`). Other accounts must use config values.","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Chat modes","content":"Mattermost responds to DMs automatically. Channel behavior is controlled by `chatmode`:\n\n- `oncall` (default): respond only when @mentioned in channels.\n- `onmessage`: respond to every channel message.\n- `onchar`: respond when a message starts with a trigger prefix.\n\nConfig example:\n\n```json5\n{\n channels: {\n mattermost: {\n chatmode: \"onchar\",\n oncharPrefixes: [\">\", \"!\"],\n },\n },\n}\n```\n\nNotes:\n\n- `onchar` still responds to explicit @mentions.\n- `channels.mattermost.requireMention` is honored for legacy configs but `chatmode` is preferred.","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Access control (DMs)","content":"- Default: `channels.mattermost.dmPolicy = \"pairing\"` (unknown senders get a pairing code).\n- Approve via:\n - `openclaw pairing list mattermost`\n - `openclaw pairing approve mattermost `\n- Public DMs: `channels.mattermost.dmPolicy=\"open\"` plus `channels.mattermost.allowFrom=[\"*\"]`.","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Channels (groups)","content":"- Default: `channels.mattermost.groupPolicy = \"allowlist\"` (mention-gated).\n- Allowlist senders with `channels.mattermost.groupAllowFrom` (user IDs or `@username`).\n- Open channels: `channels.mattermost.groupPolicy=\"open\"` (mention-gated).","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Targets for outbound delivery","content":"Use these target formats with `openclaw message send` or cron/webhooks:\n\n- `channel:` for a channel\n- `user:` for a DM\n- `@username` for a DM (resolved via the Mattermost API)\n\nBare IDs are treated as channels.","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Multi-account","content":"Mattermost supports multiple accounts under `channels.mattermost.accounts`:\n\n```json5\n{\n channels: {\n mattermost: {\n accounts: {\n default: { name: \"Primary\", botToken: \"mm-token\", baseUrl: \"https://chat.example.com\" },\n alerts: { name: \"Alerts\", botToken: \"mm-token-2\", baseUrl: \"https://alerts.example.com\" },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/mattermost.md","title":"Troubleshooting","content":"- No replies in channels: ensure the bot is in the channel and mention it (oncall), use a trigger prefix (onchar), or set `chatmode: \"onmessage\"`.\n- Auth errors: check the bot token, base URL, and whether the account is enabled.\n- Multi-account issues: env vars only apply to the `default` account.","url":"https://docs.openclaw.ai/channels/mattermost"},{"path":"channels/msteams.md","title":"msteams","content":"# Microsoft Teams (plugin)\n\n> \"Abandon all hope, ye who enter here.\"\n\nUpdated: 2026-01-21\n\nStatus: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Plugin required","content":"Microsoft Teams ships as a plugin and is not bundled with the core install.\n\n**Breaking change (2026.1.15):** MS Teams moved out of core. If you use it, you must install the plugin.\n\nExplainable: keeps core installs lighter and lets MS Teams dependencies update independently.\n\nInstall via CLI (npm registry):\n\n```bash\nopenclaw plugins install @openclaw/msteams\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/msteams\n```\n\nIf you choose Teams during configure/onboarding and a git checkout is detected,\nOpenClaw will offer the local install path automatically.\n\nDetails: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Quick setup (beginner)","content":"1. Install the Microsoft Teams plugin.\n2. Create an **Azure Bot** (App ID + client secret + tenant ID).\n3. Configure OpenClaw with those credentials.\n4. Expose `/api/messages` (port 3978 by default) via a public URL or tunnel.\n5. Install the Teams app package and start the gateway.\n\nMinimal config:\n\n```json5\n{\n channels: {\n msteams: {\n enabled: true,\n appId: \"\",\n appPassword: \"\",\n tenantId: \"\",\n webhook: { port: 3978, path: \"/api/messages\" },\n },\n },\n}\n```\n\nNote: group chats are blocked by default (`channels.msteams.groupPolicy: \"allowlist\"`). To allow group replies, set `channels.msteams.groupAllowFrom` (or use `groupPolicy: \"open\"` to allow any member, mention-gated).","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Goals","content":"- Talk to OpenClaw via Teams DMs, group chats, or channels.\n- Keep routing deterministic: replies always go back to the channel they arrived on.\n- Default to safe channel behavior (mentions required unless configured otherwise).","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Config writes","content":"By default, Microsoft Teams is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).\n\nDisable with:\n\n```json5\n{\n channels: { msteams: { configWrites: false } },\n}\n```","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Access control (DMs + groups)","content":"**DM access**\n\n- Default: `channels.msteams.dmPolicy = \"pairing\"`. Unknown senders are ignored until approved.\n- `channels.msteams.allowFrom` accepts AAD object IDs, UPNs, or display names. The wizard resolves names to IDs via Microsoft Graph when credentials allow.\n\n**Group access**\n\n- Default: `channels.msteams.groupPolicy = \"allowlist\"` (blocked unless you add `groupAllowFrom`). Use `channels.defaults.groupPolicy` to override the default when unset.\n- `channels.msteams.groupAllowFrom` controls which senders can trigger in group chats/channels (falls back to `channels.msteams.allowFrom`).\n- Set `groupPolicy: \"open\"` to allow any member (still mention‑gated by default).\n- To allow **no channels**, set `channels.msteams.groupPolicy: \"disabled\"`.\n\nExample:\n\n```json5\n{\n channels: {\n msteams: {\n groupPolicy: \"allowlist\",\n groupAllowFrom: [\"user@org.com\"],\n },\n },\n}\n```\n\n**Teams + channel allowlist**\n\n- Scope group/channel replies by listing teams and channels under `channels.msteams.teams`.\n- Keys can be team IDs or names; channel keys can be conversation IDs or names.\n- When `groupPolicy=\"allowlist\"` and a teams allowlist is present, only listed teams/channels are accepted (mention‑gated).\n- The configure wizard accepts `Team/Channel` entries and stores them for you.\n- On startup, OpenClaw resolves team/channel and user allowlist names to IDs (when Graph permissions allow)\n and logs the mapping; unresolved entries are kept as typed.\n\nExample:\n\n```json5\n{\n channels: {\n msteams: {\n groupPolicy: \"allowlist\",\n teams: {\n \"My Team\": {\n channels: {\n General: { requireMention: true },\n },\n },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"How it works","content":"1. Install the Microsoft Teams plugin.\n2. Create an **Azure Bot** (App ID + secret + tenant ID).\n3. Build a **Teams app package** that references the bot and includes the RSC permissions below.\n4. Upload/install the Teams app into a team (or personal scope for DMs).\n5. Configure `msteams` in `~/.openclaw/openclaw.json` (or env vars) and start the gateway.\n6. The gateway listens for Bot Framework webhook traffic on `/api/messages` by default.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Azure Bot Setup (Prerequisites)","content":"Before configuring OpenClaw, you need to create an Azure Bot resource.\n\n### Step 1: Create Azure Bot\n\n1. Go to [Create Azure Bot](https://portal.azure.com/#create/Microsoft.AzureBot)\n2. Fill in the **Basics** tab:\n\n | Field | Value |\n | ------------------ | -------------------------------------------------------- |\n | **Bot handle** | Your bot name, e.g., `openclaw-msteams` (must be unique) |\n | **Subscription** | Select your Azure subscription |\n | **Resource group** | Create new or use existing |\n | **Pricing tier** | **Free** for dev/testing |\n | **Type of App** | **Single Tenant** (recommended - see note below) |\n | **Creation type** | **Create new Microsoft App ID** |\n\n> **Deprecation notice:** Creation of new multi-tenant bots was deprecated after 2025-07-31. Use **Single Tenant** for new bots.\n\n3. Click **Review + create** → **Create** (wait ~1-2 minutes)\n\n### Step 2: Get Credentials\n\n1. Go to your Azure Bot resource → **Configuration**\n2. Copy **Microsoft App ID** → this is your `appId`\n3. Click **Manage Password** → go to the App Registration\n4. Under **Certificates & secrets** → **New client secret** → copy the **Value** → this is your `appPassword`\n5. Go to **Overview** → copy **Directory (tenant) ID** → this is your `tenantId`\n\n### Step 3: Configure Messaging Endpoint\n\n1. In Azure Bot → **Configuration**\n2. Set **Messaging endpoint** to your webhook URL:\n - Production: `https://your-domain.com/api/messages`\n - Local dev: Use a tunnel (see [Local Development](#local-development-tunneling) below)\n\n### Step 4: Enable Teams Channel\n\n1. In Azure Bot → **Channels**\n2. Click **Microsoft Teams** → Configure → Save\n3. Accept the Terms of Service","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Local Development (Tunneling)","content":"Teams can't reach `localhost`. Use a tunnel for local development:\n\n**Option A: ngrok**\n\n```bash\nngrok http 3978\n# Copy the https URL, e.g., https://abc123.ngrok.io\n# Set messaging endpoint to: https://abc123.ngrok.io/api/messages\n```\n\n**Option B: Tailscale Funnel**\n\n```bash\ntailscale funnel 3978\n# Use your Tailscale funnel URL as the messaging endpoint\n```","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Teams Developer Portal (Alternative)","content":"Instead of manually creating a manifest ZIP, you can use the [Teams Developer Portal](https://dev.teams.microsoft.com/apps):\n\n1. Click **+ New app**\n2. Fill in basic info (name, description, developer info)\n3. Go to **App features** → **Bot**\n4. Select **Enter a bot ID manually** and paste your Azure Bot App ID\n5. Check scopes: **Personal**, **Team**, **Group Chat**\n6. Click **Distribute** → **Download app package**\n7. In Teams: **Apps** → **Manage your apps** → **Upload a custom app** → select the ZIP\n\nThis is often easier than hand-editing JSON manifests.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Testing the Bot","content":"**Option A: Azure Web Chat (verify webhook first)**\n\n1. In Azure Portal → your Azure Bot resource → **Test in Web Chat**\n2. Send a message - you should see a response\n3. This confirms your webhook endpoint works before Teams setup\n\n**Option B: Teams (after app installation)**\n\n1. Install the Teams app (sideload or org catalog)\n2. Find the bot in Teams and send a DM\n3. Check gateway logs for incoming activity","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Setup (minimal text-only)","content":"1. **Install the Microsoft Teams plugin**\n - From npm: `openclaw plugins install @openclaw/msteams`\n - From a local checkout: `openclaw plugins install ./extensions/msteams`\n\n2. **Bot registration**\n - Create an Azure Bot (see above) and note:\n - App ID\n - Client secret (App password)\n - Tenant ID (single-tenant)\n\n3. **Teams app manifest**\n - Include a `bot` entry with `botId = `.\n - Scopes: `personal`, `team`, `groupChat`.\n - `supportsFiles: true` (required for personal scope file handling).\n - Add RSC permissions (below).\n - Create icons: `outline.png` (32x32) and `color.png` (192x192).\n - Zip all three files together: `manifest.json`, `outline.png`, `color.png`.\n\n4. **Configure OpenClaw**\n\n ```json\n {\n \"msteams\": {\n \"enabled\": true,\n \"appId\": \"\",\n \"appPassword\": \"\",\n \"tenantId\": \"\",\n \"webhook\": { \"port\": 3978, \"path\": \"/api/messages\" }\n }\n }\n ```\n\n You can also use environment variables instead of config keys:\n - `MSTEAMS_APP_ID`\n - `MSTEAMS_APP_PASSWORD`\n - `MSTEAMS_TENANT_ID`\n\n5. **Bot endpoint**\n - Set the Azure Bot Messaging Endpoint to:\n - `https://:3978/api/messages` (or your chosen path/port).\n\n6. **Run the gateway**\n - The Teams channel starts automatically when the plugin is installed and `msteams` config exists with credentials.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"History context","content":"- `channels.msteams.historyLimit` controls how many recent channel/group messages are wrapped into the prompt.\n- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).\n- DM history can be limited with `channels.msteams.dmHistoryLimit` (user turns). Per-user overrides: `channels.msteams.dms[\"\"].historyLimit`.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Current Teams RSC Permissions (Manifest)","content":"These are the **existing resourceSpecific permissions** in our Teams app manifest. They only apply inside the team/chat where the app is installed.\n\n**For channels (team scope):**\n\n- `ChannelMessage.Read.Group` (Application) - receive all channel messages without @mention\n- `ChannelMessage.Send.Group` (Application)\n- `Member.Read.Group` (Application)\n- `Owner.Read.Group` (Application)\n- `ChannelSettings.Read.Group` (Application)\n- `TeamMember.Read.Group` (Application)\n- `TeamSettings.Read.Group` (Application)\n\n**For group chats:**\n\n- `ChatMessage.Read.Chat` (Application) - receive all group chat messages without @mention","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Example Teams Manifest (redacted)","content":"Minimal, valid example with the required fields. Replace IDs and URLs.\n\n```json\n{\n \"$schema\": \"https://developer.microsoft.com/en-us/json-schemas/teams/v1.23/MicrosoftTeams.schema.json\",\n \"manifestVersion\": \"1.23\",\n \"version\": \"1.0.0\",\n \"id\": \"00000000-0000-0000-0000-000000000000\",\n \"name\": { \"short\": \"OpenClaw\" },\n \"developer\": {\n \"name\": \"Your Org\",\n \"websiteUrl\": \"https://example.com\",\n \"privacyUrl\": \"https://example.com/privacy\",\n \"termsOfUseUrl\": \"https://example.com/terms\"\n },\n \"description\": { \"short\": \"OpenClaw in Teams\", \"full\": \"OpenClaw in Teams\" },\n \"icons\": { \"outline\": \"outline.png\", \"color\": \"color.png\" },\n \"accentColor\": \"#5B6DEF\",\n \"bots\": [\n {\n \"botId\": \"11111111-1111-1111-1111-111111111111\",\n \"scopes\": [\"personal\", \"team\", \"groupChat\"],\n \"isNotificationOnly\": false,\n \"supportsCalling\": false,\n \"supportsVideo\": false,\n \"supportsFiles\": true\n }\n ],\n \"webApplicationInfo\": {\n \"id\": \"11111111-1111-1111-1111-111111111111\"\n },\n \"authorization\": {\n \"permissions\": {\n \"resourceSpecific\": [\n { \"name\": \"ChannelMessage.Read.Group\", \"type\": \"Application\" },\n { \"name\": \"ChannelMessage.Send.Group\", \"type\": \"Application\" },\n { \"name\": \"Member.Read.Group\", \"type\": \"Application\" },\n { \"name\": \"Owner.Read.Group\", \"type\": \"Application\" },\n { \"name\": \"ChannelSettings.Read.Group\", \"type\": \"Application\" },\n { \"name\": \"TeamMember.Read.Group\", \"type\": \"Application\" },\n { \"name\": \"TeamSettings.Read.Group\", \"type\": \"Application\" },\n { \"name\": \"ChatMessage.Read.Chat\", \"type\": \"Application\" }\n ]\n }\n }\n}\n```\n\n### Manifest caveats (must-have fields)\n\n- `bots[].botId` **must** match the Azure Bot App ID.\n- `webApplicationInfo.id` **must** match the Azure Bot App ID.\n- `bots[].scopes` must include the surfaces you plan to use (`personal`, `team`, `groupChat`).\n- `bots[].supportsFiles: true` is required for file handling in personal scope.\n- `authorization.permissions.resourceSpecific` must include channel read/send if you want channel traffic.\n\n### Updating an existing app\n\nTo update an already-installed Teams app (e.g., to add RSC permissions):\n\n1. Update your `manifest.json` with the new settings\n2. **Increment the `version` field** (e.g., `1.0.0` → `1.1.0`)\n3. **Re-zip** the manifest with icons (`manifest.json`, `outline.png`, `color.png`)\n4. Upload the new zip:\n - **Option A (Teams Admin Center):** Teams Admin Center → Teams apps → Manage apps → find your app → Upload new version\n - **Option B (Sideload):** In Teams → Apps → Manage your apps → Upload a custom app\n5. **For team channels:** Reinstall the app in each team for new permissions to take effect\n6. **Fully quit and relaunch Teams** (not just close the window) to clear cached app metadata","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Capabilities: RSC only vs Graph","content":"### With **Teams RSC only** (app installed, no Graph API permissions)\n\nWorks:\n\n- Read channel message **text** content.\n- Send channel message **text** content.\n- Receive **personal (DM)** file attachments.\n\nDoes NOT work:\n\n- Channel/group **image or file contents** (payload only includes HTML stub).\n- Downloading attachments stored in SharePoint/OneDrive.\n- Reading message history (beyond the live webhook event).\n\n### With **Teams RSC + Microsoft Graph Application permissions**\n\nAdds:\n\n- Downloading hosted contents (images pasted into messages).\n- Downloading file attachments stored in SharePoint/OneDrive.\n- Reading channel/chat message history via Graph.\n\n### RSC vs Graph API\n\n| Capability | RSC Permissions | Graph API |\n| ----------------------- | -------------------- | ----------------------------------- |\n| **Real-time messages** | Yes (via webhook) | No (polling only) |\n| **Historical messages** | No | Yes (can query history) |\n| **Setup complexity** | App manifest only | Requires admin consent + token flow |\n| **Works offline** | No (must be running) | Yes (query anytime) |\n\n**Bottom line:** RSC is for real-time listening; Graph API is for historical access. For catching up on missed messages while offline, you need Graph API with `ChannelMessage.Read.All` (requires admin consent).","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Graph-enabled media + history (required for channels)","content":"If you need images/files in **channels** or want to fetch **message history**, you must enable Microsoft Graph permissions and grant admin consent.\n\n1. In Entra ID (Azure AD) **App Registration**, add Microsoft Graph **Application permissions**:\n - `ChannelMessage.Read.All` (channel attachments + history)\n - `Chat.Read.All` or `ChatMessage.Read.All` (group chats)\n2. **Grant admin consent** for the tenant.\n3. Bump the Teams app **manifest version**, re-upload, and **reinstall the app in Teams**.\n4. **Fully quit and relaunch Teams** to clear cached app metadata.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Known Limitations","content":"### Webhook timeouts\n\nTeams delivers messages via HTTP webhook. If processing takes too long (e.g., slow LLM responses), you may see:\n\n- Gateway timeouts\n- Teams retrying the message (causing duplicates)\n- Dropped replies\n\nOpenClaw handles this by returning quickly and sending replies proactively, but very slow responses may still cause issues.\n\n### Formatting\n\nTeams markdown is more limited than Slack or Discord:\n\n- Basic formatting works: **bold**, _italic_, `code`, links\n- Complex markdown (tables, nested lists) may not render correctly\n- Adaptive Cards are supported for polls and arbitrary card sends (see below)","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Configuration","content":"Key settings (see `/gateway/configuration` for shared channel patterns):\n\n- `channels.msteams.enabled`: enable/disable the channel.\n- `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: bot credentials.\n- `channels.msteams.webhook.port` (default `3978`)\n- `channels.msteams.webhook.path` (default `/api/messages`)\n- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing)\n- `channels.msteams.allowFrom`: allowlist for DMs (AAD object IDs, UPNs, or display names). The wizard resolves names to IDs during setup when Graph access is available.\n- `channels.msteams.textChunkLimit`: outbound text chunk size.\n- `channels.msteams.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n- `channels.msteams.mediaAllowHosts`: allowlist for inbound attachment hosts (defaults to Microsoft/Teams domains).\n- `channels.msteams.mediaAuthAllowHosts`: allowlist for attaching Authorization headers on media retries (defaults to Graph + Bot Framework hosts).\n- `channels.msteams.requireMention`: require @mention in channels/groups (default true).\n- `channels.msteams.replyStyle`: `thread | top-level` (see [Reply Style](#reply-style-threads-vs-posts)).\n- `channels.msteams.teams..replyStyle`: per-team override.\n- `channels.msteams.teams..requireMention`: per-team override.\n- `channels.msteams.teams..tools`: default per-team tool policy overrides (`allow`/`deny`/`alsoAllow`) used when a channel override is missing.\n- `channels.msteams.teams..toolsBySender`: default per-team per-sender tool policy overrides (`\"*\"` wildcard supported).\n- `channels.msteams.teams..channels..replyStyle`: per-channel override.\n- `channels.msteams.teams..channels..requireMention`: per-channel override.\n- `channels.msteams.teams..channels..tools`: per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`).\n- `channels.msteams.teams..channels..toolsBySender`: per-channel per-sender tool policy overrides (`\"*\"` wildcard supported).\n- `channels.msteams.sharePointSiteId`: SharePoint site ID for file uploads in group chats/channels (see [Sending files in group chats](#sending-files-in-group-chats)).","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Routing & Sessions","content":"- Session keys follow the standard agent format (see [/concepts/session](/concepts/session)):\n - Direct messages share the main session (`agent::`).\n - Channel/group messages use conversation id:\n - `agent::msteams:channel:`\n - `agent::msteams:group:`","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Reply Style: Threads vs Posts","content":"Teams recently introduced two channel UI styles over the same underlying data model:\n\n| Style | Description | Recommended `replyStyle` |\n| ------------------------ | --------------------------------------------------------- | ------------------------ |\n| **Posts** (classic) | Messages appear as cards with threaded replies underneath | `thread` (default) |\n| **Threads** (Slack-like) | Messages flow linearly, more like Slack | `top-level` |\n\n**The problem:** The Teams API does not expose which UI style a channel uses. If you use the wrong `replyStyle`:\n\n- `thread` in a Threads-style channel → replies appear nested awkwardly\n- `top-level` in a Posts-style channel → replies appear as separate top-level posts instead of in-thread\n\n**Solution:** Configure `replyStyle` per-channel based on how the channel is set up:\n\n```json\n{\n \"msteams\": {\n \"replyStyle\": \"thread\",\n \"teams\": {\n \"19:abc...@thread.tacv2\": {\n \"channels\": {\n \"19:xyz...@thread.tacv2\": {\n \"replyStyle\": \"top-level\"\n }\n }\n }\n }\n }\n}\n```","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Attachments & Images","content":"**Current limitations:**\n\n- **DMs:** Images and file attachments work via Teams bot file APIs.\n- **Channels/groups:** Attachments live in M365 storage (SharePoint/OneDrive). The webhook payload only includes an HTML stub, not the actual file bytes. **Graph API permissions are required** to download channel attachments.\n\nWithout Graph permissions, channel messages with images will be received as text-only (the image content is not accessible to the bot).\nBy default, OpenClaw only downloads media from Microsoft/Teams hostnames. Override with `channels.msteams.mediaAllowHosts` (use `[\"*\"]` to allow any host).\nAuthorization headers are only attached for hosts in `channels.msteams.mediaAuthAllowHosts` (defaults to Graph + Bot Framework hosts). Keep this list strict (avoid multi-tenant suffixes).","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Sending files in group chats","content":"Bots can send files in DMs using the FileConsentCard flow (built-in). However, **sending files in group chats/channels** requires additional setup:\n\n| Context | How files are sent | Setup needed |\n| ------------------------ | -------------------------------------------- | ----------------------------------------------- |\n| **DMs** | FileConsentCard → user accepts → bot uploads | Works out of the box |\n| **Group chats/channels** | Upload to SharePoint → share link | Requires `sharePointSiteId` + Graph permissions |\n| **Images (any context)** | Base64-encoded inline | Works out of the box |\n\n### Why group chats need SharePoint\n\nBots don't have a personal OneDrive drive (the `/me/drive` Graph API endpoint doesn't work for application identities). To send files in group chats/channels, the bot uploads to a **SharePoint site** and creates a sharing link.\n\n### Setup\n\n1. **Add Graph API permissions** in Entra ID (Azure AD) → App Registration:\n - `Sites.ReadWrite.All` (Application) - upload files to SharePoint\n - `Chat.Read.All` (Application) - optional, enables per-user sharing links\n\n2. **Grant admin consent** for the tenant.\n\n3. **Get your SharePoint site ID:**\n\n ```bash\n # Via Graph Explorer or curl with a valid token:\n curl -H \"Authorization: Bearer $TOKEN\" \\\n \"https://graph.microsoft.com/v1.0/sites/{hostname}:/{site-path}\"\n\n # Example: for a site at \"contoso.sharepoint.com/sites/BotFiles\"\n curl -H \"Authorization: Bearer $TOKEN\" \\\n \"https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/BotFiles\"\n\n # Response includes: \"id\": \"contoso.sharepoint.com,guid1,guid2\"\n ```\n\n4. **Configure OpenClaw:**\n ```json5\n {\n channels: {\n msteams: {\n // ... other config ...\n sharePointSiteId: \"contoso.sharepoint.com,guid1,guid2\",\n },\n },\n }\n ```\n\n### Sharing behavior\n\n| Permission | Sharing behavior |\n| --------------------------------------- | --------------------------------------------------------- |\n| `Sites.ReadWrite.All` only | Organization-wide sharing link (anyone in org can access) |\n| `Sites.ReadWrite.All` + `Chat.Read.All` | Per-user sharing link (only chat members can access) |\n\nPer-user sharing is more secure as only the chat participants can access the file. If `Chat.Read.All` permission is missing, the bot falls back to organization-wide sharing.\n\n### Fallback behavior\n\n| Scenario | Result |\n| ------------------------------------------------- | -------------------------------------------------- |\n| Group chat + file + `sharePointSiteId` configured | Upload to SharePoint, send sharing link |\n| Group chat + file + no `sharePointSiteId` | Attempt OneDrive upload (may fail), send text only |\n| Personal chat + file | FileConsentCard flow (works without SharePoint) |\n| Any context + image | Base64-encoded inline (works without SharePoint) |\n\n### Files stored location\n\nUploaded files are stored in a `/OpenClawShared/` folder in the configured SharePoint site's default document library.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Polls (Adaptive Cards)","content":"OpenClaw sends Teams polls as Adaptive Cards (there is no native Teams poll API).\n\n- CLI: `openclaw message poll --channel msteams --target conversation: ...`\n- Votes are recorded by the gateway in `~/.openclaw/msteams-polls.json`.\n- The gateway must stay online to record votes.\n- Polls do not auto-post result summaries yet (inspect the store file if needed).","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Adaptive Cards (arbitrary)","content":"Send any Adaptive Card JSON to Teams users or conversations using the `message` tool or CLI.\n\nThe `card` parameter accepts an Adaptive Card JSON object. When `card` is provided, the message text is optional.\n\n**Agent tool:**\n\n```json\n{\n \"action\": \"send\",\n \"channel\": \"msteams\",\n \"target\": \"user:\",\n \"card\": {\n \"type\": \"AdaptiveCard\",\n \"version\": \"1.5\",\n \"body\": [{ \"type\": \"TextBlock\", \"text\": \"Hello!\" }]\n }\n}\n```\n\n**CLI:**\n\n```bash\nopenclaw message send --channel msteams \\\n --target \"conversation:19:abc...@thread.tacv2\" \\\n --card '{\"type\":\"AdaptiveCard\",\"version\":\"1.5\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"Hello!\"}]}'\n```\n\nSee [Adaptive Cards documentation](https://adaptivecards.io/) for card schema and examples. For target format details, see [Target formats](#target-formats) below.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Target formats","content":"MSTeams targets use prefixes to distinguish between users and conversations:\n\n| Target type | Format | Example |\n| ------------------- | -------------------------------- | --------------------------------------------------- |\n| User (by ID) | `user:` | `user:40a1a0ed-4ff2-4164-a219-55518990c197` |\n| User (by name) | `user:` | `user:John Smith` (requires Graph API) |\n| Group/channel | `conversation:` | `conversation:19:abc123...@thread.tacv2` |\n| Group/channel (raw) | `` | `19:abc123...@thread.tacv2` (if contains `@thread`) |\n\n**CLI examples:**\n\n```bash\n# Send to a user by ID\nopenclaw message send --channel msteams --target \"user:40a1a0ed-...\" --message \"Hello\"\n\n# Send to a user by display name (triggers Graph API lookup)\nopenclaw message send --channel msteams --target \"user:John Smith\" --message \"Hello\"\n\n# Send to a group chat or channel\nopenclaw message send --channel msteams --target \"conversation:19:abc...@thread.tacv2\" --message \"Hello\"\n\n# Send an Adaptive Card to a conversation\nopenclaw message send --channel msteams --target \"conversation:19:abc...@thread.tacv2\" \\\n --card '{\"type\":\"AdaptiveCard\",\"version\":\"1.5\",\"body\":[{\"type\":\"TextBlock\",\"text\":\"Hello\"}]}'\n```\n\n**Agent tool examples:**\n\n```json\n{\n \"action\": \"send\",\n \"channel\": \"msteams\",\n \"target\": \"user:John Smith\",\n \"message\": \"Hello!\"\n}\n```\n\n```json\n{\n \"action\": \"send\",\n \"channel\": \"msteams\",\n \"target\": \"conversation:19:abc...@thread.tacv2\",\n \"card\": {\n \"type\": \"AdaptiveCard\",\n \"version\": \"1.5\",\n \"body\": [{ \"type\": \"TextBlock\", \"text\": \"Hello\" }]\n }\n}\n```\n\nNote: Without the `user:` prefix, names default to group/team resolution. Always use `user:` when targeting people by display name.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Proactive messaging","content":"- Proactive messages are only possible **after** a user has interacted, because we store conversation references at that point.\n- See `/gateway/configuration` for `dmPolicy` and allowlist gating.","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Team and Channel IDs (Common Gotcha)","content":"The `groupId` query parameter in Teams URLs is **NOT** the team ID used for configuration. Extract IDs from the URL path instead:\n\n**Team URL:**\n\n```\nhttps://teams.microsoft.com/l/team/19%3ABk4j...%40thread.tacv2/conversations?groupId=...\n └────────────────────────────┘\n Team ID (URL-decode this)\n```\n\n**Channel URL:**\n\n```\nhttps://teams.microsoft.com/l/channel/19%3A15bc...%40thread.tacv2/ChannelName?groupId=...\n └─────────────────────────┘\n Channel ID (URL-decode this)\n```\n\n**For config:**\n\n- Team ID = path segment after `/team/` (URL-decoded, e.g., `19:Bk4j...@thread.tacv2`)\n- Channel ID = path segment after `/channel/` (URL-decoded)\n- **Ignore** the `groupId` query parameter","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Private Channels","content":"Bots have limited support in private channels:\n\n| Feature | Standard Channels | Private Channels |\n| ---------------------------- | ----------------- | ---------------------- |\n| Bot installation | Yes | Limited |\n| Real-time messages (webhook) | Yes | May not work |\n| RSC permissions | Yes | May behave differently |\n| @mentions | Yes | If bot is accessible |\n| Graph API history | Yes | Yes (with permissions) |\n\n**Workarounds if private channels don't work:**\n\n1. Use standard channels for bot interactions\n2. Use DMs - users can always message the bot directly\n3. Use Graph API for historical access (requires `ChannelMessage.Read.All`)","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"Troubleshooting","content":"### Common issues\n\n- **Images not showing in channels:** Graph permissions or admin consent missing. Reinstall the Teams app and fully quit/reopen Teams.\n- **No responses in channel:** mentions are required by default; set `channels.msteams.requireMention=false` or configure per team/channel.\n- **Version mismatch (Teams still shows old manifest):** remove + re-add the app and fully quit Teams to refresh.\n- **401 Unauthorized from webhook:** Expected when testing manually without Azure JWT - means endpoint is reachable but auth failed. Use Azure Web Chat to test properly.\n\n### Manifest upload errors\n\n- **\"Icon file cannot be empty\":** The manifest references icon files that are 0 bytes. Create valid PNG icons (32x32 for `outline.png`, 192x192 for `color.png`).\n- **\"webApplicationInfo.Id already in use\":** The app is still installed in another team/chat. Find and uninstall it first, or wait 5-10 minutes for propagation.\n- **\"Something went wrong\" on upload:** Upload via https://admin.teams.microsoft.com instead, open browser DevTools (F12) → Network tab, and check the response body for the actual error.\n- **Sideload failing:** Try \"Upload an app to your org's app catalog\" instead of \"Upload a custom app\" - this often bypasses sideload restrictions.\n\n### RSC permissions not working\n\n1. Verify `webApplicationInfo.id` matches your bot's App ID exactly\n2. Re-upload the app and reinstall in the team/chat\n3. Check if your org admin has blocked RSC permissions\n4. Confirm you're using the right scope: `ChannelMessage.Read.Group` for teams, `ChatMessage.Read.Chat` for group chats","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/msteams.md","title":"References","content":"- [Create Azure Bot](https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration) - Azure Bot setup guide\n- [Teams Developer Portal](https://dev.teams.microsoft.com/apps) - create/manage Teams apps\n- [Teams app manifest schema](https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema)\n- [Receive channel messages with RSC](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/channel-messages-with-rsc)\n- [RSC permissions reference](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent)\n- [Teams bot file handling](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/bots-filesv4) (channel/group requires Graph)\n- [Proactive messaging](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages)","url":"https://docs.openclaw.ai/channels/msteams"},{"path":"channels/nextcloud-talk.md","title":"nextcloud-talk","content":"# Nextcloud Talk (plugin)\n\nStatus: supported via plugin (webhook bot). Direct messages, rooms, reactions, and markdown messages are supported.","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Plugin required","content":"Nextcloud Talk ships as a plugin and is not bundled with the core install.\n\nInstall via CLI (npm registry):\n\n```bash\nopenclaw plugins install @openclaw/nextcloud-talk\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/nextcloud-talk\n```\n\nIf you choose Nextcloud Talk during configure/onboarding and a git checkout is detected,\nOpenClaw will offer the local install path automatically.\n\nDetails: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Quick setup (beginner)","content":"1. Install the Nextcloud Talk plugin.\n2. On your Nextcloud server, create a bot:\n ```bash\n ./occ talk:bot:install \"OpenClaw\" \"\" \"\" --feature reaction\n ```\n3. Enable the bot in the target room settings.\n4. Configure OpenClaw:\n - Config: `channels.nextcloud-talk.baseUrl` + `channels.nextcloud-talk.botSecret`\n - Or env: `NEXTCLOUD_TALK_BOT_SECRET` (default account only)\n5. Restart the gateway (or finish onboarding).\n\nMinimal config:\n\n```json5\n{\n channels: {\n \"nextcloud-talk\": {\n enabled: true,\n baseUrl: \"https://cloud.example.com\",\n botSecret: \"shared-secret\",\n dmPolicy: \"pairing\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Notes","content":"- Bots cannot initiate DMs. The user must message the bot first.\n- Webhook URL must be reachable by the Gateway; set `webhookPublicUrl` if behind a proxy.\n- Media uploads are not supported by the bot API; media is sent as URLs.\n- The webhook payload does not distinguish DMs vs rooms; set `apiUser` + `apiPassword` to enable room-type lookups (otherwise DMs are treated as rooms).","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Access control (DMs)","content":"- Default: `channels.nextcloud-talk.dmPolicy = \"pairing\"`. Unknown senders get a pairing code.\n- Approve via:\n - `openclaw pairing list nextcloud-talk`\n - `openclaw pairing approve nextcloud-talk `\n- Public DMs: `channels.nextcloud-talk.dmPolicy=\"open\"` plus `channels.nextcloud-talk.allowFrom=[\"*\"]`.","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Rooms (groups)","content":"- Default: `channels.nextcloud-talk.groupPolicy = \"allowlist\"` (mention-gated).\n- Allowlist rooms with `channels.nextcloud-talk.rooms`:\n\n```json5\n{\n channels: {\n \"nextcloud-talk\": {\n rooms: {\n \"room-token\": { requireMention: true },\n },\n },\n },\n}\n```\n\n- To allow no rooms, keep the allowlist empty or set `channels.nextcloud-talk.groupPolicy=\"disabled\"`.","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Capabilities","content":"| Feature | Status |\n| --------------- | ------------- |\n| Direct messages | Supported |\n| Rooms | Supported |\n| Threads | Not supported |\n| Media | URL-only |\n| Reactions | Supported |\n| Native commands | Not supported |","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nextcloud-talk.md","title":"Configuration reference (Nextcloud Talk)","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.nextcloud-talk.enabled`: enable/disable channel startup.\n- `channels.nextcloud-talk.baseUrl`: Nextcloud instance URL.\n- `channels.nextcloud-talk.botSecret`: bot shared secret.\n- `channels.nextcloud-talk.botSecretFile`: secret file path.\n- `channels.nextcloud-talk.apiUser`: API user for room lookups (DM detection).\n- `channels.nextcloud-talk.apiPassword`: API/app password for room lookups.\n- `channels.nextcloud-talk.apiPasswordFile`: API password file path.\n- `channels.nextcloud-talk.webhookPort`: webhook listener port (default: 8788).\n- `channels.nextcloud-talk.webhookHost`: webhook host (default: 0.0.0.0).\n- `channels.nextcloud-talk.webhookPath`: webhook path (default: /nextcloud-talk-webhook).\n- `channels.nextcloud-talk.webhookPublicUrl`: externally reachable webhook URL.\n- `channels.nextcloud-talk.dmPolicy`: `pairing | allowlist | open | disabled`.\n- `channels.nextcloud-talk.allowFrom`: DM allowlist (user IDs). `open` requires `\"*\"`.\n- `channels.nextcloud-talk.groupPolicy`: `allowlist | open | disabled`.\n- `channels.nextcloud-talk.groupAllowFrom`: group allowlist (user IDs).\n- `channels.nextcloud-talk.rooms`: per-room settings and allowlist.\n- `channels.nextcloud-talk.historyLimit`: group history limit (0 disables).\n- `channels.nextcloud-talk.dmHistoryLimit`: DM history limit (0 disables).\n- `channels.nextcloud-talk.dms`: per-DM overrides (historyLimit).\n- `channels.nextcloud-talk.textChunkLimit`: outbound text chunk size (chars).\n- `channels.nextcloud-talk.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n- `channels.nextcloud-talk.blockStreaming`: disable block streaming for this channel.\n- `channels.nextcloud-talk.blockStreamingCoalesce`: block streaming coalesce tuning.\n- `channels.nextcloud-talk.mediaMaxMb`: inbound media cap (MB).","url":"https://docs.openclaw.ai/channels/nextcloud-talk"},{"path":"channels/nostr.md","title":"nostr","content":"# Nostr\n\n**Status:** Optional plugin (disabled by default).\n\nNostr is a decentralized protocol for social networking. This channel enables OpenClaw to receive and respond to encrypted direct messages (DMs) via NIP-04.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Install (on demand)","content":"### Onboarding (recommended)\n\n- The onboarding wizard (`openclaw onboard`) and `openclaw channels add` list optional channel plugins.\n- Selecting Nostr prompts you to install the plugin on demand.\n\nInstall defaults:\n\n- **Dev channel + git checkout available:** uses the local plugin path.\n- **Stable/Beta:** downloads from npm.\n\nYou can always override the choice in the prompt.\n\n### Manual install\n\n```bash\nopenclaw plugins install @openclaw/nostr\n```\n\nUse a local checkout (dev workflows):\n\n```bash\nopenclaw plugins install --link /extensions/nostr\n```\n\nRestart the Gateway after installing or enabling plugins.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Quick setup","content":"1. Generate a Nostr keypair (if needed):\n\n```bash\n# Using nak\nnak key generate\n```\n\n2. Add to config:\n\n```json\n{\n \"channels\": {\n \"nostr\": {\n \"privateKey\": \"${NOSTR_PRIVATE_KEY}\"\n }\n }\n}\n```\n\n3. Export the key:\n\n```bash\nexport NOSTR_PRIVATE_KEY=\"nsec1...\"\n```\n\n4. Restart the Gateway.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Configuration reference","content":"| Key | Type | Default | Description |\n| ------------ | -------- | ------------------------------------------- | ----------------------------------- |\n| `privateKey` | string | required | Private key in `nsec` or hex format |\n| `relays` | string[] | `['wss://relay.damus.io', 'wss://nos.lol']` | Relay URLs (WebSocket) |\n| `dmPolicy` | string | `pairing` | DM access policy |\n| `allowFrom` | string[] | `[]` | Allowed sender pubkeys |\n| `enabled` | boolean | `true` | Enable/disable channel |\n| `name` | string | - | Display name |\n| `profile` | object | - | NIP-01 profile metadata |","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Profile metadata","content":"Profile data is published as a NIP-01 `kind:0` event. You can manage it from the Control UI (Channels -> Nostr -> Profile) or set it directly in config.\n\nExample:\n\n```json\n{\n \"channels\": {\n \"nostr\": {\n \"privateKey\": \"${NOSTR_PRIVATE_KEY}\",\n \"profile\": {\n \"name\": \"openclaw\",\n \"displayName\": \"OpenClaw\",\n \"about\": \"Personal assistant DM bot\",\n \"picture\": \"https://example.com/avatar.png\",\n \"banner\": \"https://example.com/banner.png\",\n \"website\": \"https://example.com\",\n \"nip05\": \"openclaw@example.com\",\n \"lud16\": \"openclaw@example.com\"\n }\n }\n }\n}\n```\n\nNotes:\n\n- Profile URLs must use `https://`.\n- Importing from relays merges fields and preserves local overrides.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Access control","content":"### DM policies\n\n- **pairing** (default): unknown senders get a pairing code.\n- **allowlist**: only pubkeys in `allowFrom` can DM.\n- **open**: public inbound DMs (requires `allowFrom: [\"*\"]`).\n- **disabled**: ignore inbound DMs.\n\n### Allowlist example\n\n```json\n{\n \"channels\": {\n \"nostr\": {\n \"privateKey\": \"${NOSTR_PRIVATE_KEY}\",\n \"dmPolicy\": \"allowlist\",\n \"allowFrom\": [\"npub1abc...\", \"npub1xyz...\"]\n }\n }\n}\n```","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Key formats","content":"Accepted formats:\n\n- **Private key:** `nsec...` or 64-char hex\n- **Pubkeys (`allowFrom`):** `npub...` or hex","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Relays","content":"Defaults: `relay.damus.io` and `nos.lol`.\n\n```json\n{\n \"channels\": {\n \"nostr\": {\n \"privateKey\": \"${NOSTR_PRIVATE_KEY}\",\n \"relays\": [\"wss://relay.damus.io\", \"wss://relay.primal.net\", \"wss://nostr.wine\"]\n }\n }\n}\n```\n\nTips:\n\n- Use 2-3 relays for redundancy.\n- Avoid too many relays (latency, duplication).\n- Paid relays can improve reliability.\n- Local relays are fine for testing (`ws://localhost:7777`).","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Protocol support","content":"| NIP | Status | Description |\n| ------ | --------- | ------------------------------------- |\n| NIP-01 | Supported | Basic event format + profile metadata |\n| NIP-04 | Supported | Encrypted DMs (`kind:4`) |\n| NIP-17 | Planned | Gift-wrapped DMs |\n| NIP-44 | Planned | Versioned encryption |","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Testing","content":"### Local relay\n\n```bash\n# Start strfry\ndocker run -p 7777:7777 ghcr.io/hoytech/strfry\n```\n\n```json\n{\n \"channels\": {\n \"nostr\": {\n \"privateKey\": \"${NOSTR_PRIVATE_KEY}\",\n \"relays\": [\"ws://localhost:7777\"]\n }\n }\n}\n```\n\n### Manual test\n\n1. Note the bot pubkey (npub) from logs.\n2. Open a Nostr client (Damus, Amethyst, etc.).\n3. DM the bot pubkey.\n4. Verify the response.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Troubleshooting","content":"### Not receiving messages\n\n- Verify the private key is valid.\n- Ensure relay URLs are reachable and use `wss://` (or `ws://` for local).\n- Confirm `enabled` is not `false`.\n- Check Gateway logs for relay connection errors.\n\n### Not sending responses\n\n- Check relay accepts writes.\n- Verify outbound connectivity.\n- Watch for relay rate limits.\n\n### Duplicate responses\n\n- Expected when using multiple relays.\n- Messages are deduplicated by event ID; only the first delivery triggers a response.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Security","content":"- Never commit private keys.\n- Use environment variables for keys.\n- Consider `allowlist` for production bots.","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/nostr.md","title":"Limitations (MVP)","content":"- Direct messages only (no group chats).\n- No media attachments.\n- NIP-04 only (NIP-17 gift-wrap planned).","url":"https://docs.openclaw.ai/channels/nostr"},{"path":"channels/signal.md","title":"signal","content":"# Signal (signal-cli)\n\nStatus: external CLI integration. Gateway talks to `signal-cli` over HTTP JSON-RPC + SSE.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Quick setup (beginner)","content":"1. Use a **separate Signal number** for the bot (recommended).\n2. Install `signal-cli` (Java required).\n3. Link the bot device and start the daemon:\n - `signal-cli link -n \"OpenClaw\"`\n4. Configure OpenClaw and start the gateway.\n\nMinimal config:\n\n```json5\n{\n channels: {\n signal: {\n enabled: true,\n account: \"+15551234567\",\n cliPath: \"signal-cli\",\n dmPolicy: \"pairing\",\n allowFrom: [\"+15557654321\"],\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"What it is","content":"- Signal channel via `signal-cli` (not embedded libsignal).\n- Deterministic routing: replies always go back to Signal.\n- DMs share the agent's main session; groups are isolated (`agent::signal:group:`).","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Config writes","content":"By default, Signal is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).\n\nDisable with:\n\n```json5\n{\n channels: { signal: { configWrites: false } },\n}\n```","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"The number model (important)","content":"- The gateway connects to a **Signal device** (the `signal-cli` account).\n- If you run the bot on **your personal Signal account**, it will ignore your own messages (loop protection).\n- For \"I text the bot and it replies,\" use a **separate bot number**.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Setup (fast path)","content":"1. Install `signal-cli` (Java required).\n2. Link a bot account:\n - `signal-cli link -n \"OpenClaw\"` then scan the QR in Signal.\n3. Configure Signal and start the gateway.\n\nExample:\n\n```json5\n{\n channels: {\n signal: {\n enabled: true,\n account: \"+15551234567\",\n cliPath: \"signal-cli\",\n dmPolicy: \"pairing\",\n allowFrom: [\"+15557654321\"],\n },\n },\n}\n```\n\nMulti-account support: use `channels.signal.accounts` with per-account config and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"External daemon mode (httpUrl)","content":"If you want to manage `signal-cli` yourself (slow JVM cold starts, container init, or shared CPUs), run the daemon separately and point OpenClaw at it:\n\n```json5\n{\n channels: {\n signal: {\n httpUrl: \"http://127.0.0.1:8080\",\n autoStart: false,\n },\n },\n}\n```\n\nThis skips auto-spawn and the startup wait inside OpenClaw. For slow starts when auto-spawning, set `channels.signal.startupTimeoutMs`.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Access control (DMs + groups)","content":"DMs:\n\n- Default: `channels.signal.dmPolicy = \"pairing\"`.\n- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).\n- Approve via:\n - `openclaw pairing list signal`\n - `openclaw pairing approve signal `\n- Pairing is the default token exchange for Signal DMs. Details: [Pairing](/start/pairing)\n- UUID-only senders (from `sourceUuid`) are stored as `uuid:` in `channels.signal.allowFrom`.\n\nGroups:\n\n- `channels.signal.groupPolicy = open | allowlist | disabled`.\n- `channels.signal.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"How it works (behavior)","content":"- `signal-cli` runs as a daemon; the gateway reads events via SSE.\n- Inbound messages are normalized into the shared channel envelope.\n- Replies always route back to the same number or group.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Media + limits","content":"- Outbound text is chunked to `channels.signal.textChunkLimit` (default 4000).\n- Optional newline chunking: set `channels.signal.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n- Attachments supported (base64 fetched from `signal-cli`).\n- Default media cap: `channels.signal.mediaMaxMb` (default 8).\n- Use `channels.signal.ignoreAttachments` to skip downloading media.\n- Group history context uses `channels.signal.historyLimit` (or `channels.signal.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Typing + read receipts","content":"- **Typing indicators**: OpenClaw sends typing signals via `signal-cli sendTyping` and refreshes them while a reply is running.\n- **Read receipts**: when `channels.signal.sendReadReceipts` is true, OpenClaw forwards read receipts for allowed DMs.\n- Signal-cli does not expose read receipts for groups.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Reactions (message tool)","content":"- Use `message action=react` with `channel=signal`.\n- Targets: sender E.164 or UUID (use `uuid:` from pairing output; bare UUID works too).\n- `messageId` is the Signal timestamp for the message you’re reacting to.\n- Group reactions require `targetAuthor` or `targetAuthorUuid`.\n\nExamples:\n\n```\nmessage action=react channel=signal target=uuid:123e4567-e89b-12d3-a456-426614174000 messageId=1737630212345 emoji=🔥\nmessage action=react channel=signal target=+15551234567 messageId=1737630212345 emoji=🔥 remove=true\nmessage action=react channel=signal target=signal:group: targetAuthor=uuid: messageId=1737630212345 emoji=✅\n```\n\nConfig:\n\n- `channels.signal.actions.reactions`: enable/disable reaction actions (default true).\n- `channels.signal.reactionLevel`: `off | ack | minimal | extensive`.\n - `off`/`ack` disables agent reactions (message tool `react` will error).\n - `minimal`/`extensive` enables agent reactions and sets the guidance level.\n- Per-account overrides: `channels.signal.accounts..actions.reactions`, `channels.signal.accounts..reactionLevel`.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Delivery targets (CLI/cron)","content":"- DMs: `signal:+15551234567` (or plain E.164).\n- UUID DMs: `uuid:` (or bare UUID).\n- Groups: `signal:group:`.\n- Usernames: `username:` (if supported by your Signal account).","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/signal.md","title":"Configuration reference (Signal)","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.signal.enabled`: enable/disable channel startup.\n- `channels.signal.account`: E.164 for the bot account.\n- `channels.signal.cliPath`: path to `signal-cli`.\n- `channels.signal.httpUrl`: full daemon URL (overrides host/port).\n- `channels.signal.httpHost`, `channels.signal.httpPort`: daemon bind (default 127.0.0.1:8080).\n- `channels.signal.autoStart`: auto-spawn daemon (default true if `httpUrl` unset).\n- `channels.signal.startupTimeoutMs`: startup wait timeout in ms (cap 120000).\n- `channels.signal.receiveMode`: `on-start | manual`.\n- `channels.signal.ignoreAttachments`: skip attachment downloads.\n- `channels.signal.ignoreStories`: ignore stories from the daemon.\n- `channels.signal.sendReadReceipts`: forward read receipts.\n- `channels.signal.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).\n- `channels.signal.allowFrom`: DM allowlist (E.164 or `uuid:`). `open` requires `\"*\"`. Signal has no usernames; use phone/UUID ids.\n- `channels.signal.groupPolicy`: `open | allowlist | disabled` (default: allowlist).\n- `channels.signal.groupAllowFrom`: group sender allowlist.\n- `channels.signal.historyLimit`: max group messages to include as context (0 disables).\n- `channels.signal.dmHistoryLimit`: DM history limit in user turns. Per-user overrides: `channels.signal.dms[\"\"].historyLimit`.\n- `channels.signal.textChunkLimit`: outbound chunk size (chars).\n- `channels.signal.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n- `channels.signal.mediaMaxMb`: inbound/outbound media cap (MB).\n\nRelated global options:\n\n- `agents.list[].groupChat.mentionPatterns` (Signal does not support native mentions).\n- `messages.groupChat.mentionPatterns` (global fallback).\n- `messages.responsePrefix`.","url":"https://docs.openclaw.ai/channels/signal"},{"path":"channels/slack.md","title":"slack","content":"# Slack","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Socket mode (default)","content":"### Quick setup (beginner)\n\n1. Create a Slack app and enable **Socket Mode**.\n2. Create an **App Token** (`xapp-...`) and **Bot Token** (`xoxb-...`).\n3. Set tokens for OpenClaw and start the gateway.\n\nMinimal config:\n\n```json5\n{\n channels: {\n slack: {\n enabled: true,\n appToken: \"xapp-...\",\n botToken: \"xoxb-...\",\n },\n },\n}\n```\n\n### Setup\n\n1. Create a Slack app (From scratch) in https://api.slack.com/apps.\n2. **Socket Mode** → toggle on. Then go to **Basic Information** → **App-Level Tokens** → **Generate Token and Scopes** with scope `connections:write`. Copy the **App Token** (`xapp-...`).\n3. **OAuth & Permissions** → add bot token scopes (use the manifest below). Click **Install to Workspace**. Copy the **Bot User OAuth Token** (`xoxb-...`).\n4. Optional: **OAuth & Permissions** → add **User Token Scopes** (see the read-only list below). Reinstall the app and copy the **User OAuth Token** (`xoxp-...`).\n5. **Event Subscriptions** → enable events and subscribe to:\n - `message.*` (includes edits/deletes/thread broadcasts)\n - `app_mention`\n - `reaction_added`, `reaction_removed`\n - `member_joined_channel`, `member_left_channel`\n - `channel_rename`\n - `pin_added`, `pin_removed`\n6. Invite the bot to channels you want it to read.\n7. Slash Commands → create `/openclaw` if you use `channels.slack.slashCommand`. If you enable native commands, add one slash command per built-in command (same names as `/help`). Native defaults to off for Slack unless you set `channels.slack.commands.native: true` (global `commands.native` is `\"auto\"` which leaves Slack off).\n8. App Home → enable the **Messages Tab** so users can DM the bot.\n\nUse the manifest below so scopes and events stay in sync.\n\nMulti-account support: use `channels.slack.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.\n\n### OpenClaw config (minimal)\n\nSet tokens via env vars (recommended):\n\n- `SLACK_APP_TOKEN=xapp-...`\n- `SLACK_BOT_TOKEN=xoxb-...`\n\nOr via config:\n\n```json5\n{\n channels: {\n slack: {\n enabled: true,\n appToken: \"xapp-...\",\n botToken: \"xoxb-...\",\n },\n },\n}\n```\n\n### User token (optional)\n\nOpenClaw can use a Slack user token (`xoxp-...`) for read operations (history,\npins, reactions, emoji, member info). By default this stays read-only: reads\nprefer the user token when present, and writes still use the bot token unless\nyou explicitly opt in. Even with `userTokenReadOnly: false`, the bot token stays\npreferred for writes when it is available.\n\nUser tokens are configured in the config file (no env var support). For\nmulti-account, set `channels.slack.accounts..userToken`.\n\nExample with bot + app + user tokens:\n\n```json5\n{\n channels: {\n slack: {\n enabled: true,\n appToken: \"xapp-...\",\n botToken: \"xoxb-...\",\n userToken: \"xoxp-...\",\n },\n },\n}\n```\n\nExample with userTokenReadOnly explicitly set (allow user token writes):\n\n```json5\n{\n channels: {\n slack: {\n enabled: true,\n appToken: \"xapp-...\",\n botToken: \"xoxb-...\",\n userToken: \"xoxp-...\",\n userTokenReadOnly: false,\n },\n },\n}\n```\n\n#### Token usage\n\n- Read operations (history, reactions list, pins list, emoji list, member info,\n search) prefer the user token when configured, otherwise the bot token.\n- Write operations (send/edit/delete messages, add/remove reactions, pin/unpin,\n file uploads) use the bot token by default. If `userTokenReadOnly: false` and\n no bot token is available, OpenClaw falls back to the user token.\n\n### History context\n\n- `channels.slack.historyLimit` (or `channels.slack.accounts.*.historyLimit`) controls how many recent channel/group messages are wrapped into the prompt.\n- Falls back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"HTTP mode (Events API)","content":"Use HTTP webhook mode when your Gateway is reachable by Slack over HTTPS (typical for server deployments).\nHTTP mode uses the Events API + Interactivity + Slash Commands with a shared request URL.\n\n### Setup\n\n1. Create a Slack app and **disable Socket Mode** (optional if you only use HTTP).\n2. **Basic Information** → copy the **Signing Secret**.\n3. **OAuth & Permissions** → install the app and copy the **Bot User OAuth Token** (`xoxb-...`).\n4. **Event Subscriptions** → enable events and set the **Request URL** to your gateway webhook path (default `/slack/events`).\n5. **Interactivity & Shortcuts** → enable and set the same **Request URL**.\n6. **Slash Commands** → set the same **Request URL** for your command(s).\n\nExample request URL:\n`https://gateway-host/slack/events`\n\n### OpenClaw config (minimal)\n\n```json5\n{\n channels: {\n slack: {\n enabled: true,\n mode: \"http\",\n botToken: \"xoxb-...\",\n signingSecret: \"your-signing-secret\",\n webhookPath: \"/slack/events\",\n },\n },\n}\n```\n\nMulti-account HTTP mode: set `channels.slack.accounts..mode = \"http\"` and provide a unique\n`webhookPath` per account so each Slack app can point to its own URL.\n\n### Manifest (optional)\n\nUse this Slack app manifest to create the app quickly (adjust the name/command if you want). Include the\nuser scopes if you plan to configure a user token.\n\n```json\n{\n \"display_information\": {\n \"name\": \"OpenClaw\",\n \"description\": \"Slack connector for OpenClaw\"\n },\n \"features\": {\n \"bot_user\": {\n \"display_name\": \"OpenClaw\",\n \"always_online\": false\n },\n \"app_home\": {\n \"messages_tab_enabled\": true,\n \"messages_tab_read_only_enabled\": false\n },\n \"slash_commands\": [\n {\n \"command\": \"/openclaw\",\n \"description\": \"Send a message to OpenClaw\",\n \"should_escape\": false\n }\n ]\n },\n \"oauth_config\": {\n \"scopes\": {\n \"bot\": [\n \"chat:write\",\n \"channels:history\",\n \"channels:read\",\n \"groups:history\",\n \"groups:read\",\n \"groups:write\",\n \"im:history\",\n \"im:read\",\n \"im:write\",\n \"mpim:history\",\n \"mpim:read\",\n \"mpim:write\",\n \"users:read\",\n \"app_mentions:read\",\n \"reactions:read\",\n \"reactions:write\",\n \"pins:read\",\n \"pins:write\",\n \"emoji:read\",\n \"commands\",\n \"files:read\",\n \"files:write\"\n ],\n \"user\": [\n \"channels:history\",\n \"channels:read\",\n \"groups:history\",\n \"groups:read\",\n \"im:history\",\n \"im:read\",\n \"mpim:history\",\n \"mpim:read\",\n \"users:read\",\n \"reactions:read\",\n \"pins:read\",\n \"emoji:read\",\n \"search:read\"\n ]\n }\n },\n \"settings\": {\n \"socket_mode_enabled\": true,\n \"event_subscriptions\": {\n \"bot_events\": [\n \"app_mention\",\n \"message.channels\",\n \"message.groups\",\n \"message.im\",\n \"message.mpim\",\n \"reaction_added\",\n \"reaction_removed\",\n \"member_joined_channel\",\n \"member_left_channel\",\n \"channel_rename\",\n \"pin_added\",\n \"pin_removed\"\n ]\n }\n }\n}\n```\n\nIf you enable native commands, add one `slash_commands` entry per command you want to expose (matching the `/help` list). Override with `channels.slack.commands.native`.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Scopes (current vs optional)","content":"Slack's Conversations API is type-scoped: you only need the scopes for the\nconversation types you actually touch (channels, groups, im, mpim). See\nhttps://docs.slack.dev/apis/web-api/using-the-conversations-api/ for the overview.\n\n### Bot token scopes (required)\n\n- `chat:write` (send/update/delete messages via `chat.postMessage`)\n https://docs.slack.dev/reference/methods/chat.postMessage\n- `im:write` (open DMs via `conversations.open` for user DMs)\n https://docs.slack.dev/reference/methods/conversations.open\n- `channels:history`, `groups:history`, `im:history`, `mpim:history`\n https://docs.slack.dev/reference/methods/conversations.history\n- `channels:read`, `groups:read`, `im:read`, `mpim:read`\n https://docs.slack.dev/reference/methods/conversations.info\n- `users:read` (user lookup)\n https://docs.slack.dev/reference/methods/users.info\n- `reactions:read`, `reactions:write` (`reactions.get` / `reactions.add`)\n https://docs.slack.dev/reference/methods/reactions.get\n https://docs.slack.dev/reference/methods/reactions.add\n- `pins:read`, `pins:write` (`pins.list` / `pins.add` / `pins.remove`)\n https://docs.slack.dev/reference/scopes/pins.read\n https://docs.slack.dev/reference/scopes/pins.write\n- `emoji:read` (`emoji.list`)\n https://docs.slack.dev/reference/scopes/emoji.read\n- `files:write` (uploads via `files.uploadV2`)\n https://docs.slack.dev/messaging/working-with-files/#upload\n\n### User token scopes (optional, read-only by default)\n\nAdd these under **User Token Scopes** if you configure `channels.slack.userToken`.\n\n- `channels:history`, `groups:history`, `im:history`, `mpim:history`\n- `channels:read`, `groups:read`, `im:read`, `mpim:read`\n- `users:read`\n- `reactions:read`\n- `pins:read`\n- `emoji:read`\n- `search:read`\n\n### Not needed today (but likely future)\n\n- `mpim:write` (only if we add group-DM open/DM start via `conversations.open`)\n- `groups:write` (only if we add private-channel management: create/rename/invite/archive)\n- `chat:write.public` (only if we want to post to channels the bot isn't in)\n https://docs.slack.dev/reference/scopes/chat.write.public\n- `users:read.email` (only if we need email fields from `users.info`)\n https://docs.slack.dev/changelog/2017-04-narrowing-email-access\n- `files:read` (only if we start listing/reading file metadata)","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Config","content":"Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens:\n\n```json\n{\n \"slack\": {\n \"enabled\": true,\n \"botToken\": \"xoxb-...\",\n \"appToken\": \"xapp-...\",\n \"groupPolicy\": \"allowlist\",\n \"dm\": {\n \"enabled\": true,\n \"policy\": \"pairing\",\n \"allowFrom\": [\"U123\", \"U456\", \"*\"],\n \"groupEnabled\": false,\n \"groupChannels\": [\"G123\"],\n \"replyToMode\": \"all\"\n },\n \"channels\": {\n \"C123\": { \"allow\": true, \"requireMention\": true },\n \"#general\": {\n \"allow\": true,\n \"requireMention\": true,\n \"users\": [\"U123\"],\n \"skills\": [\"search\", \"docs\"],\n \"systemPrompt\": \"Keep answers short.\"\n }\n },\n \"reactionNotifications\": \"own\",\n \"reactionAllowlist\": [\"U123\"],\n \"replyToMode\": \"off\",\n \"actions\": {\n \"reactions\": true,\n \"messages\": true,\n \"pins\": true,\n \"memberInfo\": true,\n \"emojiList\": true\n },\n \"slashCommand\": {\n \"enabled\": true,\n \"name\": \"openclaw\",\n \"sessionPrefix\": \"slack:slash\",\n \"ephemeral\": true\n },\n \"textChunkLimit\": 4000,\n \"mediaMaxMb\": 20\n }\n}\n```\n\nTokens can also be supplied via env vars:\n\n- `SLACK_BOT_TOKEN`\n- `SLACK_APP_TOKEN`\n\nAck reactions are controlled globally via `messages.ackReaction` +\n`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the\nack reaction after the bot replies.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Limits","content":"- Outbound text is chunked to `channels.slack.textChunkLimit` (default 4000).\n- Optional newline chunking: set `channels.slack.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n- Media uploads are capped by `channels.slack.mediaMaxMb` (default 20).","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Reply threading","content":"By default, OpenClaw replies in the main channel. Use `channels.slack.replyToMode` to control automatic threading:\n\n| Mode | Behavior |\n| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `off` | **Default.** Reply in main channel. Only thread if the triggering message was already in a thread. |\n| `first` | First reply goes to thread (under the triggering message), subsequent replies go to main channel. Useful for keeping context visible while avoiding thread clutter. |\n| `all` | All replies go to thread. Keeps conversations contained but may reduce visibility. |\n\nThe mode applies to both auto-replies and agent tool calls (`slack sendMessage`).\n\n### Per-chat-type threading\n\nYou can configure different threading behavior per chat type by setting `channels.slack.replyToModeByChatType`:\n\n```json5\n{\n channels: {\n slack: {\n replyToMode: \"off\", // default for channels\n replyToModeByChatType: {\n direct: \"all\", // DMs always thread\n group: \"first\", // group DMs/MPIM thread first reply\n },\n },\n },\n}\n```\n\nSupported chat types:\n\n- `direct`: 1:1 DMs (Slack `im`)\n- `group`: group DMs / MPIMs (Slack `mpim`)\n- `channel`: standard channels (public/private)\n\nPrecedence:\n\n1. `replyToModeByChatType.`\n2. `replyToMode`\n3. Provider default (`off`)\n\nLegacy `channels.slack.dm.replyToMode` is still accepted as a fallback for `direct` when no chat-type override is set.\n\nExamples:\n\nThread DMs only:\n\n```json5\n{\n channels: {\n slack: {\n replyToMode: \"off\",\n replyToModeByChatType: { direct: \"all\" },\n },\n },\n}\n```\n\nThread group DMs but keep channels in the root:\n\n```json5\n{\n channels: {\n slack: {\n replyToMode: \"off\",\n replyToModeByChatType: { group: \"first\" },\n },\n },\n}\n```\n\nMake channels thread, keep DMs in the root:\n\n```json5\n{\n channels: {\n slack: {\n replyToMode: \"first\",\n replyToModeByChatType: { direct: \"off\", group: \"off\" },\n },\n },\n}\n```\n\n### Manual threading tags\n\nFor fine-grained control, use these tags in agent responses:\n\n- `[[reply_to_current]]` — reply to the triggering message (start/continue thread).\n- `[[reply_to:]]` — reply to a specific message id.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Sessions + routing","content":"- DMs share the `main` session (like WhatsApp/Telegram).\n- Channels map to `agent::slack:channel:` sessions.\n- Slash commands use `agent::slack:slash:` sessions (prefix configurable via `channels.slack.slashCommand.sessionPrefix`).\n- If Slack doesn’t provide `channel_type`, OpenClaw infers it from the channel ID prefix (`D`, `C`, `G`) and defaults to `channel` to keep session keys stable.\n- Native command registration uses `commands.native` (global default `\"auto\"` → Slack off) and can be overridden per-workspace with `channels.slack.commands.native`. Text commands require standalone `/...` messages and can be disabled with `commands.text: false`. Slack slash commands are managed in the Slack app and are not removed automatically. Use `commands.useAccessGroups: false` to bypass access-group checks for commands.\n- Full command list + config: [Slash commands](/tools/slash-commands)","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"DM security (pairing)","content":"- Default: `channels.slack.dm.policy=\"pairing\"` — unknown DM senders get a pairing code (expires after 1 hour).\n- Approve via: `openclaw pairing approve slack `.\n- To allow anyone: set `channels.slack.dm.policy=\"open\"` and `channels.slack.dm.allowFrom=[\"*\"]`.\n- `channels.slack.dm.allowFrom` accepts user IDs, @handles, or emails (resolved at startup when tokens allow). The wizard accepts usernames and resolves them to ids during setup when tokens allow.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Group policy","content":"- `channels.slack.groupPolicy` controls channel handling (`open|disabled|allowlist`).\n- `allowlist` requires channels to be listed in `channels.slack.channels`.\n- If you only set `SLACK_BOT_TOKEN`/`SLACK_APP_TOKEN` and never create a `channels.slack` section,\n the runtime defaults `groupPolicy` to `open`. Add `channels.slack.groupPolicy`,\n `channels.defaults.groupPolicy`, or a channel allowlist to lock it down.\n- The configure wizard accepts `#channel` names and resolves them to IDs when possible\n (public + private); if multiple matches exist, it prefers the active channel.\n- On startup, OpenClaw resolves channel/user names in allowlists to IDs (when tokens allow)\n and logs the mapping; unresolved entries are kept as typed.\n- To allow **no channels**, set `channels.slack.groupPolicy: \"disabled\"` (or keep an empty allowlist).\n\nChannel options (`channels.slack.channels.` or `channels.slack.channels.`):\n\n- `allow`: allow/deny the channel when `groupPolicy=\"allowlist\"`.\n- `requireMention`: mention gating for the channel.\n- `tools`: optional per-channel tool policy overrides (`allow`/`deny`/`alsoAllow`).\n- `toolsBySender`: optional per-sender tool policy overrides within the channel (keys are sender ids/@handles/emails; `\"*\"` wildcard supported).\n- `allowBots`: allow bot-authored messages in this channel (default: false).\n- `users`: optional per-channel user allowlist.\n- `skills`: skill filter (omit = all skills, empty = none).\n- `systemPrompt`: extra system prompt for the channel (combined with topic/purpose).\n- `enabled`: set `false` to disable the channel.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Delivery targets","content":"Use these with cron/CLI sends:\n\n- `user:` for DMs\n- `channel:` for channels","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Tool actions","content":"Slack tool actions can be gated with `channels.slack.actions.*`:\n\n| Action group | Default | Notes |\n| ------------ | ------- | ---------------------- |\n| reactions | enabled | React + list reactions |\n| messages | enabled | Read/send/edit/delete |\n| pins | enabled | Pin/unpin/list |\n| memberInfo | enabled | Member info |\n| emojiList | enabled | Custom emoji list |","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Security notes","content":"- Writes default to the bot token so state-changing actions stay scoped to the\n app's bot permissions and identity.\n- Setting `userTokenReadOnly: false` allows the user token to be used for write\n operations when a bot token is unavailable, which means actions run with the\n installing user's access. Treat the user token as highly privileged and keep\n action gates and allowlists tight.\n- If you enable user-token writes, make sure the user token includes the write\n scopes you expect (`chat:write`, `reactions:write`, `pins:write`,\n `files:write`) or those operations will fail.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/slack.md","title":"Notes","content":"- Mention gating is controlled via `channels.slack.channels` (set `requireMention` to `true`); `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) also count as mentions.\n- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.\n- Reaction notifications follow `channels.slack.reactionNotifications` (use `reactionAllowlist` with mode `allowlist`).\n- Bot-authored messages are ignored by default; enable via `channels.slack.allowBots` or `channels.slack.channels..allowBots`.\n- Warning: If you allow replies to other bots (`channels.slack.allowBots=true` or `channels.slack.channels..allowBots=true`), prevent bot-to-bot reply loops with `requireMention`, `channels.slack.channels..users` allowlists, and/or clear guardrails in `AGENTS.md` and `SOUL.md`.\n- For the Slack tool, reaction removal semantics are in [/tools/reactions](/tools/reactions).\n- Attachments are downloaded to the media store when permitted and under the size limit.","url":"https://docs.openclaw.ai/channels/slack"},{"path":"channels/telegram.md","title":"telegram","content":"# Telegram (Bot API)\n\nStatus: production-ready for bot DMs + groups via grammY. Long-polling by default; webhook optional.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Quick setup (beginner)","content":"1. Create a bot with **@BotFather** ([direct link](https://t.me/BotFather)). Confirm the handle is exactly `@BotFather`, then copy the token.\n2. Set the token:\n - Env: `TELEGRAM_BOT_TOKEN=...`\n - Or config: `channels.telegram.botToken: \"...\"`.\n - If both are set, config takes precedence (env fallback is default-account only).\n3. Start the gateway.\n4. DM access is pairing by default; approve the pairing code on first contact.\n\nMinimal config:\n\n```json5\n{\n channels: {\n telegram: {\n enabled: true,\n botToken: \"123:abc\",\n dmPolicy: \"pairing\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"What it is","content":"- A Telegram Bot API channel owned by the Gateway.\n- Deterministic routing: replies go back to Telegram; the model never chooses channels.\n- DMs share the agent's main session; groups stay isolated (`agent::telegram:group:`).","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Setup (fast path)","content":"### 1) Create a bot token (BotFather)\n\n1. Open Telegram and chat with **@BotFather** ([direct link](https://t.me/BotFather)). Confirm the handle is exactly `@BotFather`.\n2. Run `/newbot`, then follow the prompts (name + username ending in `bot`).\n3. Copy the token and store it safely.\n\nOptional BotFather settings:\n\n- `/setjoingroups` — allow/deny adding the bot to groups.\n- `/setprivacy` — control whether the bot sees all group messages.\n\n### 2) Configure the token (env or config)\n\nExample:\n\n```json5\n{\n channels: {\n telegram: {\n enabled: true,\n botToken: \"123:abc\",\n dmPolicy: \"pairing\",\n groups: { \"*\": { requireMention: true } },\n },\n },\n}\n```\n\nEnv option: `TELEGRAM_BOT_TOKEN=...` (works for the default account).\nIf both env and config are set, config takes precedence.\n\nMulti-account support: use `channels.telegram.accounts` with per-account tokens and optional `name`. See [`gateway/configuration`](/gateway/configuration#telegramaccounts--discordaccounts--slackaccounts--signalaccounts--imessageaccounts) for the shared pattern.\n\n3. Start the gateway. Telegram starts when a token is resolved (config first, env fallback).\n4. DM access defaults to pairing. Approve the code when the bot is first contacted.\n5. For groups: add the bot, decide privacy/admin behavior (below), then set `channels.telegram.groups` to control mention gating + allowlists.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Token + privacy + permissions (Telegram side)","content":"### Token creation (BotFather)\n\n- `/newbot` creates the bot and returns the token (keep it secret).\n- If a token leaks, revoke/regenerate it via @BotFather and update your config.\n\n### Group message visibility (Privacy Mode)\n\nTelegram bots default to **Privacy Mode**, which limits which group messages they receive.\nIf your bot must see _all_ group messages, you have two options:\n\n- Disable privacy mode with `/setprivacy` **or**\n- Add the bot as a group **admin** (admin bots receive all messages).\n\n**Note:** When you toggle privacy mode, Telegram requires removing + re‑adding the bot\nto each group for the change to take effect.\n\n### Group permissions (admin rights)\n\nAdmin status is set inside the group (Telegram UI). Admin bots always receive all\ngroup messages, so use admin if you need full visibility.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"How it works (behavior)","content":"- Inbound messages are normalized into the shared channel envelope with reply context and media placeholders.\n- Group replies require a mention by default (native @mention or `agents.list[].groupChat.mentionPatterns` / `messages.groupChat.mentionPatterns`).\n- Multi-agent override: set per-agent patterns on `agents.list[].groupChat.mentionPatterns`.\n- Replies always route back to the same Telegram chat.\n- Long-polling uses grammY runner with per-chat sequencing; overall concurrency is capped by `agents.defaults.maxConcurrent`.\n- Telegram Bot API does not support read receipts; there is no `sendReadReceipts` option.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Draft streaming","content":"OpenClaw can stream partial replies in Telegram DMs using `sendMessageDraft`.\n\nRequirements:\n\n- Threaded Mode enabled for the bot in @BotFather (forum topic mode).\n- Private chat threads only (Telegram includes `message_thread_id` on inbound messages).\n- `channels.telegram.streamMode` not set to `\"off\"` (default: `\"partial\"`, `\"block\"` enables chunked draft updates).\n\nDraft streaming is DM-only; Telegram does not support it in groups or channels.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Formatting (Telegram HTML)","content":"- Outbound Telegram text uses `parse_mode: \"HTML\"` (Telegram’s supported tag subset).\n- Markdown-ish input is rendered into **Telegram-safe HTML** (bold/italic/strike/code/links); block elements are flattened to text with newlines/bullets.\n- Raw HTML from models is escaped to avoid Telegram parse errors.\n- If Telegram rejects the HTML payload, OpenClaw retries the same message as plain text.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Commands (native + custom)","content":"OpenClaw registers native commands (like `/status`, `/reset`, `/model`) with Telegram’s bot menu on startup.\nYou can add custom commands to the menu via config:\n\n```json5\n{\n channels: {\n telegram: {\n customCommands: [\n { command: \"backup\", description: \"Git backup\" },\n { command: \"generate\", description: \"Create an image\" },\n ],\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Troubleshooting","content":"- `setMyCommands failed` in logs usually means outbound HTTPS/DNS is blocked to `api.telegram.org`.\n- If you see `sendMessage` or `sendChatAction` failures, check IPv6 routing and DNS.\n\nMore help: [Channel troubleshooting](/channels/troubleshooting).\n\nNotes:\n\n- Custom commands are **menu entries only**; OpenClaw does not implement them unless you handle them elsewhere.\n- Command names are normalized (leading `/` stripped, lowercased) and must match `a-z`, `0-9`, `_` (1–32 chars).\n- Custom commands **cannot override native commands**. Conflicts are ignored and logged.\n- If `commands.native` is disabled, only custom commands are registered (or cleared if none).","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Limits","content":"- Outbound text is chunked to `channels.telegram.textChunkLimit` (default 4000).\n- Optional newline chunking: set `channels.telegram.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n- Media downloads/uploads are capped by `channels.telegram.mediaMaxMb` (default 5).\n- Telegram Bot API requests time out after `channels.telegram.timeoutSeconds` (default 500 via grammY). Set lower to avoid long hangs.\n- Group history context uses `channels.telegram.historyLimit` (or `channels.telegram.accounts.*.historyLimit`), falling back to `messages.groupChat.historyLimit`. Set `0` to disable (default 50).\n- DM history can be limited with `channels.telegram.dmHistoryLimit` (user turns). Per-user overrides: `channels.telegram.dms[\"\"].historyLimit`.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Group activation modes","content":"By default, the bot only responds to mentions in groups (`@botname` or patterns in `agents.list[].groupChat.mentionPatterns`). To change this behavior:\n\n### Via config (recommended)\n\n```json5\n{\n channels: {\n telegram: {\n groups: {\n \"-1001234567890\": { requireMention: false }, // always respond in this group\n },\n },\n },\n}\n```\n\n**Important:** Setting `channels.telegram.groups` creates an **allowlist** - only listed groups (or `\"*\"`) will be accepted.\nForum topics inherit their parent group config (allowFrom, requireMention, skills, prompts) unless you add per-topic overrides under `channels.telegram.groups..topics.`.\n\nTo allow all groups with always-respond:\n\n```json5\n{\n channels: {\n telegram: {\n groups: {\n \"*\": { requireMention: false }, // all groups, always respond\n },\n },\n },\n}\n```\n\nTo keep mention-only for all groups (default behavior):\n\n```json5\n{\n channels: {\n telegram: {\n groups: {\n \"*\": { requireMention: true }, // or omit groups entirely\n },\n },\n },\n}\n```\n\n### Via command (session-level)\n\nSend in the group:\n\n- `/activation always` - respond to all messages\n- `/activation mention` - require mentions (default)\n\n**Note:** Commands update session state only. For persistent behavior across restarts, use config.\n\n### Getting the group chat ID\n\nForward any message from the group to `@userinfobot` or `@getidsbot` on Telegram to see the chat ID (negative number like `-1001234567890`).\n\n**Tip:** For your own user ID, DM the bot and it will reply with your user ID (pairing message), or use `/whoami` once commands are enabled.\n\n**Privacy note:** `@userinfobot` is a third-party bot. If you prefer, add the bot to the group, send a message, and use `openclaw logs --follow` to read `chat.id`, or use the Bot API `getUpdates`.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Config writes","content":"By default, Telegram is allowed to write config updates triggered by channel events or `/config set|unset`.\n\nThis happens when:\n\n- A group is upgraded to a supergroup and Telegram emits `migrate_to_chat_id` (chat ID changes). OpenClaw can migrate `channels.telegram.groups` automatically.\n- You run `/config set` or `/config unset` in a Telegram chat (requires `commands.config: true`).\n\nDisable with:\n\n```json5\n{\n channels: { telegram: { configWrites: false } },\n}\n```","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Topics (forum supergroups)","content":"Telegram forum topics include a `message_thread_id` per message. OpenClaw:\n\n- Appends `:topic:` to the Telegram group session key so each topic is isolated.\n- Sends typing indicators and replies with `message_thread_id` so responses stay in the topic.\n- General topic (thread id `1`) is special: message sends omit `message_thread_id` (Telegram rejects it), but typing indicators still include it.\n- Exposes `MessageThreadId` + `IsForum` in template context for routing/templating.\n- Topic-specific configuration is available under `channels.telegram.groups..topics.` (skills, allowlists, auto-reply, system prompts, disable).\n- Topic configs inherit group settings (requireMention, allowlists, skills, prompts, enabled) unless overridden per topic.\n\nPrivate chats can include `message_thread_id` in some edge cases. OpenClaw keeps the DM session key unchanged, but still uses the thread id for replies/draft streaming when it is present.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Inline Buttons","content":"Telegram supports inline keyboards with callback buttons.\n\n```json5\n{\n channels: {\n telegram: {\n capabilities: {\n inlineButtons: \"allowlist\",\n },\n },\n },\n}\n```\n\nFor per-account configuration:\n\n```json5\n{\n channels: {\n telegram: {\n accounts: {\n main: {\n capabilities: {\n inlineButtons: \"allowlist\",\n },\n },\n },\n },\n },\n}\n```\n\nScopes:\n\n- `off` — inline buttons disabled\n- `dm` — only DMs (group targets blocked)\n- `group` — only groups (DM targets blocked)\n- `all` — DMs + groups\n- `allowlist` — DMs + groups, but only senders allowed by `allowFrom`/`groupAllowFrom` (same rules as control commands)\n\nDefault: `allowlist`.\nLegacy: `capabilities: [\"inlineButtons\"]` = `inlineButtons: \"all\"`.\n\n### Sending buttons\n\nUse the message tool with the `buttons` parameter:\n\n```json5\n{\n action: \"send\",\n channel: \"telegram\",\n to: \"123456789\",\n message: \"Choose an option:\",\n buttons: [\n [\n { text: \"Yes\", callback_data: \"yes\" },\n { text: \"No\", callback_data: \"no\" },\n ],\n [{ text: \"Cancel\", callback_data: \"cancel\" }],\n ],\n}\n```\n\nWhen a user clicks a button, the callback data is sent back to the agent as a message with the format:\n`callback_data: value`\n\n### Configuration options\n\nTelegram capabilities can be configured at two levels (object form shown above; legacy string arrays still supported):\n\n- `channels.telegram.capabilities`: Global default capability config applied to all Telegram accounts unless overridden.\n- `channels.telegram.accounts..capabilities`: Per-account capabilities that override the global defaults for that specific account.\n\nUse the global setting when all Telegram bots/accounts should behave the same. Use per-account configuration when different bots need different behaviors (for example, one account only handles DMs while another is allowed in groups).","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Access control (DMs + groups)","content":"### DM access\n\n- Default: `channels.telegram.dmPolicy = \"pairing\"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).\n- Approve via:\n - `openclaw pairing list telegram`\n - `openclaw pairing approve telegram `\n- Pairing is the default token exchange used for Telegram DMs. Details: [Pairing](/start/pairing)\n- `channels.telegram.allowFrom` accepts numeric user IDs (recommended) or `@username` entries. It is **not** the bot username; use the human sender’s ID. The wizard accepts `@username` and resolves it to the numeric ID when possible.\n\n#### Finding your Telegram user ID\n\nSafer (no third-party bot):\n\n1. Start the gateway and DM your bot.\n2. Run `openclaw logs --follow` and look for `from.id`.\n\nAlternate (official Bot API):\n\n1. DM your bot.\n2. Fetch updates with your bot token and read `message.from.id`:\n ```bash\n curl \"https://api.telegram.org/bot/getUpdates\"\n ```\n\nThird-party (less private):\n\n- DM `@userinfobot` or `@getidsbot` and use the returned user id.\n\n### Group access\n\nTwo independent controls:\n\n**1. Which groups are allowed** (group allowlist via `channels.telegram.groups`):\n\n- No `groups` config = all groups allowed\n- With `groups` config = only listed groups or `\"*\"` are allowed\n- Example: `\"groups\": { \"-1001234567890\": {}, \"*\": {} }` allows all groups\n\n**2. Which senders are allowed** (sender filtering via `channels.telegram.groupPolicy`):\n\n- `\"open\"` = all senders in allowed groups can message\n- `\"allowlist\"` = only senders in `channels.telegram.groupAllowFrom` can message\n- `\"disabled\"` = no group messages accepted at all\n Default is `groupPolicy: \"allowlist\"` (blocked unless you add `groupAllowFrom`).\n\nMost users want: `groupPolicy: \"allowlist\"` + `groupAllowFrom` + specific groups listed in `channels.telegram.groups`","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Long-polling vs webhook","content":"- Default: long-polling (no public URL required).\n- Webhook mode: set `channels.telegram.webhookUrl` and `channels.telegram.webhookSecret` (optionally `channels.telegram.webhookPath`).\n - The local listener binds to `0.0.0.0:8787` and serves `POST /telegram-webhook` by default.\n - If your public URL is different, use a reverse proxy and point `channels.telegram.webhookUrl` at the public endpoint.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Reply threading","content":"Telegram supports optional threaded replies via tags:\n\n- `[[reply_to_current]]` -- reply to the triggering message.\n- `[[reply_to:]]` -- reply to a specific message id.\n\nControlled by `channels.telegram.replyToMode`:\n\n- `first` (default), `all`, `off`.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Audio messages (voice vs file)","content":"Telegram distinguishes **voice notes** (round bubble) from **audio files** (metadata card).\nOpenClaw defaults to audio files for backward compatibility.\n\nTo force a voice note bubble in agent replies, include this tag anywhere in the reply:\n\n- `[[audio_as_voice]]` — send audio as a voice note instead of a file.\n\nThe tag is stripped from the delivered text. Other channels ignore this tag.\n\nFor message tool sends, set `asVoice: true` with a voice-compatible audio `media` URL\n(`message` is optional when media is present):\n\n```json5\n{\n action: \"send\",\n channel: \"telegram\",\n to: \"123456789\",\n media: \"https://example.com/voice.ogg\",\n asVoice: true,\n}\n```","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Stickers","content":"OpenClaw supports receiving and sending Telegram stickers with intelligent caching.\n\n### Receiving stickers\n\nWhen a user sends a sticker, OpenClaw handles it based on the sticker type:\n\n- **Static stickers (WEBP):** Downloaded and processed through vision. The sticker appears as a `` placeholder in the message content.\n- **Animated stickers (TGS):** Skipped (Lottie format not supported for processing).\n- **Video stickers (WEBM):** Skipped (video format not supported for processing).\n\nTemplate context field available when receiving stickers:\n\n- `Sticker` — object with:\n - `emoji` — emoji associated with the sticker\n - `setName` — name of the sticker set\n - `fileId` — Telegram file ID (send the same sticker back)\n - `fileUniqueId` — stable ID for cache lookup\n - `cachedDescription` — cached vision description when available\n\n### Sticker cache\n\nStickers are processed through the AI's vision capabilities to generate descriptions. Since the same stickers are often sent repeatedly, OpenClaw caches these descriptions to avoid redundant API calls.\n\n**How it works:**\n\n1. **First encounter:** The sticker image is sent to the AI for vision analysis. The AI generates a description (e.g., \"A cartoon cat waving enthusiastically\").\n2. **Cache storage:** The description is saved along with the sticker's file ID, emoji, and set name.\n3. **Subsequent encounters:** When the same sticker is seen again, the cached description is used directly. The image is not sent to the AI.\n\n**Cache location:** `~/.openclaw/telegram/sticker-cache.json`\n\n**Cache entry format:**\n\n```json\n{\n \"fileId\": \"CAACAgIAAxkBAAI...\",\n \"fileUniqueId\": \"AgADBAADb6cxG2Y\",\n \"emoji\": \"👋\",\n \"setName\": \"CoolCats\",\n \"description\": \"A cartoon cat waving enthusiastically\",\n \"cachedAt\": \"2026-01-15T10:30:00.000Z\"\n}\n```\n\n**Benefits:**\n\n- Reduces API costs by avoiding repeated vision calls for the same sticker\n- Faster response times for cached stickers (no vision processing delay)\n- Enables sticker search functionality based on cached descriptions\n\nThe cache is populated automatically as stickers are received. There is no manual cache management required.\n\n### Sending stickers\n\nThe agent can send and search stickers using the `sticker` and `sticker-search` actions. These are disabled by default and must be enabled in config:\n\n```json5\n{\n channels: {\n telegram: {\n actions: {\n sticker: true,\n },\n },\n },\n}\n```\n\n**Send a sticker:**\n\n```json5\n{\n action: \"sticker\",\n channel: \"telegram\",\n to: \"123456789\",\n fileId: \"CAACAgIAAxkBAAI...\",\n}\n```\n\nParameters:\n\n- `fileId` (required) — the Telegram file ID of the sticker. Obtain this from `Sticker.fileId` when receiving a sticker, or from a `sticker-search` result.\n- `replyTo` (optional) — message ID to reply to.\n- `threadId` (optional) — message thread ID for forum topics.\n\n**Search for stickers:**\n\nThe agent can search cached stickers by description, emoji, or set name:\n\n```json5\n{\n action: \"sticker-search\",\n channel: \"telegram\",\n query: \"cat waving\",\n limit: 5,\n}\n```\n\nReturns matching stickers from the cache:\n\n```json5\n{\n ok: true,\n count: 2,\n stickers: [\n {\n fileId: \"CAACAgIAAxkBAAI...\",\n emoji: \"👋\",\n description: \"A cartoon cat waving enthusiastically\",\n setName: \"CoolCats\",\n },\n ],\n}\n```\n\nThe search uses fuzzy matching across description text, emoji characters, and set names.\n\n**Example with threading:**\n\n```json5\n{\n action: \"sticker\",\n channel: \"telegram\",\n to: \"-1001234567890\",\n fileId: \"CAACAgIAAxkBAAI...\",\n replyTo: 42,\n threadId: 123,\n}\n```","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Streaming (drafts)","content":"Telegram can stream **draft bubbles** while the agent is generating a response.\nOpenClaw uses Bot API `sendMessageDraft` (not real messages) and then sends the\nfinal reply as a normal message.\n\nRequirements (Telegram Bot API 9.3+):\n\n- **Private chats with topics enabled** (forum topic mode for the bot).\n- Incoming messages must include `message_thread_id` (private topic thread).\n- Streaming is ignored for groups/supergroups/channels.\n\nConfig:\n\n- `channels.telegram.streamMode: \"off\" | \"partial\" | \"block\"` (default: `partial`)\n - `partial`: update the draft bubble with the latest streaming text.\n - `block`: update the draft bubble in larger blocks (chunked).\n - `off`: disable draft streaming.\n- Optional (only for `streamMode: \"block\"`):\n - `channels.telegram.draftChunk: { minChars?, maxChars?, breakPreference? }`\n - defaults: `minChars: 200`, `maxChars: 800`, `breakPreference: \"paragraph\"` (clamped to `channels.telegram.textChunkLimit`).\n\nNote: draft streaming is separate from **block streaming** (channel messages).\nBlock streaming is off by default and requires `channels.telegram.blockStreaming: true`\nif you want early Telegram messages instead of draft updates.\n\nReasoning stream (Telegram only):\n\n- `/reasoning stream` streams reasoning into the draft bubble while the reply is\n generating, then sends the final answer without reasoning.\n- If `channels.telegram.streamMode` is `off`, reasoning stream is disabled.\n More context: [Streaming + chunking](/concepts/streaming).","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Retry policy","content":"Outbound Telegram API calls retry on transient network/429 errors with exponential backoff and jitter. Configure via `channels.telegram.retry`. See [Retry policy](/concepts/retry).","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Agent tool (messages + reactions)","content":"- Tool: `telegram` with `sendMessage` action (`to`, `content`, optional `mediaUrl`, `replyToMessageId`, `messageThreadId`).\n- Tool: `telegram` with `react` action (`chatId`, `messageId`, `emoji`).\n- Tool: `telegram` with `deleteMessage` action (`chatId`, `messageId`).\n- Reaction removal semantics: see [/tools/reactions](/tools/reactions).\n- Tool gating: `channels.telegram.actions.reactions`, `channels.telegram.actions.sendMessage`, `channels.telegram.actions.deleteMessage` (default: enabled), and `channels.telegram.actions.sticker` (default: disabled).","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Reaction notifications","content":"**How reactions work:**\nTelegram reactions arrive as **separate `message_reaction` events**, not as properties in message payloads. When a user adds a reaction, OpenClaw:\n\n1. Receives the `message_reaction` update from Telegram API\n2. Converts it to a **system event** with format: `\"Telegram reaction added: {emoji} by {user} on msg {id}\"`\n3. Enqueues the system event using the **same session key** as regular messages\n4. When the next message arrives in that conversation, system events are drained and prepended to the agent's context\n\nThe agent sees reactions as **system notifications** in the conversation history, not as message metadata.\n\n**Configuration:**\n\n- `channels.telegram.reactionNotifications`: Controls which reactions trigger notifications\n - `\"off\"` — ignore all reactions\n - `\"own\"` — notify when users react to bot messages (best-effort; in-memory) (default)\n - `\"all\"` — notify for all reactions\n\n- `channels.telegram.reactionLevel`: Controls agent's reaction capability\n - `\"off\"` — agent cannot react to messages\n - `\"ack\"` — bot sends acknowledgment reactions (👀 while processing) (default)\n - `\"minimal\"` — agent can react sparingly (guideline: 1 per 5-10 exchanges)\n - `\"extensive\"` — agent can react liberally when appropriate\n\n**Forum groups:** Reactions in forum groups include `message_thread_id` and use session keys like `agent:main:telegram:group:{chatId}:topic:{threadId}`. This ensures reactions and messages in the same topic stay together.\n\n**Example config:**\n\n```json5\n{\n channels: {\n telegram: {\n reactionNotifications: \"all\", // See all reactions\n reactionLevel: \"minimal\", // Agent can react sparingly\n },\n },\n}\n```\n\n**Requirements:**\n\n- Telegram bots must explicitly request `message_reaction` in `allowed_updates` (configured automatically by OpenClaw)\n- For webhook mode, reactions are included in the webhook `allowed_updates`\n- For polling mode, reactions are included in the `getUpdates` `allowed_updates`","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Delivery targets (CLI/cron)","content":"- Use a chat id (`123456789`) or a username (`@name`) as the target.\n- Example: `openclaw message send --channel telegram --target 123456789 --message \"hi\"`.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Troubleshooting","content":"**Bot doesn’t respond to non-mention messages in a group:**\n\n- If you set `channels.telegram.groups.*.requireMention=false`, Telegram’s Bot API **privacy mode** must be disabled.\n - BotFather: `/setprivacy` → **Disable** (then remove + re-add the bot to the group)\n- `openclaw channels status` shows a warning when config expects unmentioned group messages.\n- `openclaw channels status --probe` can additionally check membership for explicit numeric group IDs (it can’t audit wildcard `\"*\"` rules).\n- Quick test: `/activation always` (session-only; use config for persistence)\n\n**Bot not seeing group messages at all:**\n\n- If `channels.telegram.groups` is set, the group must be listed or use `\"*\"`\n- Check Privacy Settings in @BotFather → \"Group Privacy\" should be **OFF**\n- Verify bot is actually a member (not just an admin with no read access)\n- Check gateway logs: `openclaw logs --follow` (look for \"skipping group message\")\n\n**Bot responds to mentions but not `/activation always`:**\n\n- The `/activation` command updates session state but doesn't persist to config\n- For persistent behavior, add group to `channels.telegram.groups` with `requireMention: false`\n\n**Commands like `/status` don't work:**\n\n- Make sure your Telegram user ID is authorized (via pairing or `channels.telegram.allowFrom`)\n- Commands require authorization even in groups with `groupPolicy: \"open\"`\n\n**Long-polling aborts immediately on Node 22+ (often with proxies/custom fetch):**\n\n- Node 22+ is stricter about `AbortSignal` instances; foreign signals can abort `fetch` calls right away.\n- Upgrade to a OpenClaw build that normalizes abort signals, or run the gateway on Node 20 until you can upgrade.\n\n**Bot starts, then silently stops responding (or logs `HttpError: Network request ... failed`):**\n\n- Some hosts resolve `api.telegram.org` to IPv6 first. If your server does not have working IPv6 egress, grammY can get stuck on IPv6-only requests.\n- Fix by enabling IPv6 egress **or** forcing IPv4 resolution for `api.telegram.org` (for example, add an `/etc/hosts` entry using the IPv4 A record, or prefer IPv4 in your OS DNS stack), then restart the gateway.\n- Quick check: `dig +short api.telegram.org A` and `dig +short api.telegram.org AAAA` to confirm what DNS returns.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/telegram.md","title":"Configuration reference (Telegram)","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.telegram.enabled`: enable/disable channel startup.\n- `channels.telegram.botToken`: bot token (BotFather).\n- `channels.telegram.tokenFile`: read token from file path.\n- `channels.telegram.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).\n- `channels.telegram.allowFrom`: DM allowlist (ids/usernames). `open` requires `\"*\"`.\n- `channels.telegram.groupPolicy`: `open | allowlist | disabled` (default: allowlist).\n- `channels.telegram.groupAllowFrom`: group sender allowlist (ids/usernames).\n- `channels.telegram.groups`: per-group defaults + allowlist (use `\"*\"` for global defaults).\n - `channels.telegram.groups..requireMention`: mention gating default.\n - `channels.telegram.groups..skills`: skill filter (omit = all skills, empty = none).\n - `channels.telegram.groups..allowFrom`: per-group sender allowlist override.\n - `channels.telegram.groups..systemPrompt`: extra system prompt for the group.\n - `channels.telegram.groups..enabled`: disable the group when `false`.\n - `channels.telegram.groups..topics..*`: per-topic overrides (same fields as group).\n - `channels.telegram.groups..topics..requireMention`: per-topic mention gating override.\n- `channels.telegram.capabilities.inlineButtons`: `off | dm | group | all | allowlist` (default: allowlist).\n- `channels.telegram.accounts..capabilities.inlineButtons`: per-account override.\n- `channels.telegram.replyToMode`: `off | first | all` (default: `first`).\n- `channels.telegram.textChunkLimit`: outbound chunk size (chars).\n- `channels.telegram.chunkMode`: `length` (default) or `newline` to split on blank lines (paragraph boundaries) before length chunking.\n- `channels.telegram.linkPreview`: toggle link previews for outbound messages (default: true).\n- `channels.telegram.streamMode`: `off | partial | block` (draft streaming).\n- `channels.telegram.mediaMaxMb`: inbound/outbound media cap (MB).\n- `channels.telegram.retry`: retry policy for outbound Telegram API calls (attempts, minDelayMs, maxDelayMs, jitter).\n- `channels.telegram.network.autoSelectFamily`: override Node autoSelectFamily (true=enable, false=disable). Defaults to disabled on Node 22 to avoid Happy Eyeballs timeouts.\n- `channels.telegram.proxy`: proxy URL for Bot API calls (SOCKS/HTTP).\n- `channels.telegram.webhookUrl`: enable webhook mode (requires `channels.telegram.webhookSecret`).\n- `channels.telegram.webhookSecret`: webhook secret (required when webhookUrl is set).\n- `channels.telegram.webhookPath`: local webhook path (default `/telegram-webhook`).\n- `channels.telegram.actions.reactions`: gate Telegram tool reactions.\n- `channels.telegram.actions.sendMessage`: gate Telegram tool message sends.\n- `channels.telegram.actions.deleteMessage`: gate Telegram tool message deletes.\n- `channels.telegram.actions.sticker`: gate Telegram sticker actions — send and search (default: false).\n- `channels.telegram.reactionNotifications`: `off | own | all` — control which reactions trigger system events (default: `own` when not set).\n- `channels.telegram.reactionLevel`: `off | ack | minimal | extensive` — control agent's reaction capability (default: `minimal` when not set).\n\nRelated global options:\n\n- `agents.list[].groupChat.mentionPatterns` (mention gating patterns).\n- `messages.groupChat.mentionPatterns` (global fallback).\n- `commands.native` (defaults to `\"auto\"` → on for Telegram/Discord, off for Slack), `commands.text`, `commands.useAccessGroups` (command behavior). Override with `channels.telegram.commands.native`.\n- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`, `messages.removeAckAfterReply`.","url":"https://docs.openclaw.ai/channels/telegram"},{"path":"channels/tlon.md","title":"tlon","content":"# Tlon (plugin)\n\nTlon is a decentralized messenger built on Urbit. OpenClaw connects to your Urbit ship and can\nrespond to DMs and group chat messages. Group replies require an @ mention by default and can\nbe further restricted via allowlists.\n\nStatus: supported via plugin. DMs, group mentions, thread replies, and text-only media fallback\n(URL appended to caption). Reactions, polls, and native media uploads are not supported.","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/tlon.md","title":"Plugin required","content":"Tlon ships as a plugin and is not bundled with the core install.\n\nInstall via CLI (npm registry):\n\n```bash\nopenclaw plugins install @openclaw/tlon\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/tlon\n```\n\nDetails: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/tlon.md","title":"Setup","content":"1. Install the Tlon plugin.\n2. Gather your ship URL and login code.\n3. Configure `channels.tlon`.\n4. Restart the gateway.\n5. DM the bot or mention it in a group channel.\n\nMinimal config (single account):\n\n```json5\n{\n channels: {\n tlon: {\n enabled: true,\n ship: \"~sampel-palnet\",\n url: \"https://your-ship-host\",\n code: \"lidlut-tabwed-pillex-ridrup\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/tlon.md","title":"Group channels","content":"Auto-discovery is enabled by default. You can also pin channels manually:\n\n```json5\n{\n channels: {\n tlon: {\n groupChannels: [\"chat/~host-ship/general\", \"chat/~host-ship/support\"],\n },\n },\n}\n```\n\nDisable auto-discovery:\n\n```json5\n{\n channels: {\n tlon: {\n autoDiscoverChannels: false,\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/tlon.md","title":"Access control","content":"DM allowlist (empty = allow all):\n\n```json5\n{\n channels: {\n tlon: {\n dmAllowlist: [\"~zod\", \"~nec\"],\n },\n },\n}\n```\n\nGroup authorization (restricted by default):\n\n```json5\n{\n channels: {\n tlon: {\n defaultAuthorizedShips: [\"~zod\"],\n authorization: {\n channelRules: {\n \"chat/~host-ship/general\": {\n mode: \"restricted\",\n allowedShips: [\"~zod\", \"~nec\"],\n },\n \"chat/~host-ship/announcements\": {\n mode: \"open\",\n },\n },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/tlon.md","title":"Delivery targets (CLI/cron)","content":"Use these with `openclaw message send` or cron delivery:\n\n- DM: `~sampel-palnet` or `dm/~sampel-palnet`\n- Group: `chat/~host-ship/channel` or `group:~host-ship/channel`","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/tlon.md","title":"Notes","content":"- Group replies require a mention (e.g. `~your-bot-ship`) to respond.\n- Thread replies: if the inbound message is in a thread, OpenClaw replies in-thread.\n- Media: `sendMedia` falls back to text + URL (no native upload).","url":"https://docs.openclaw.ai/channels/tlon"},{"path":"channels/troubleshooting.md","title":"troubleshooting","content":"# Channel troubleshooting\n\nStart with:\n\n```bash\nopenclaw doctor\nopenclaw channels status --probe\n```\n\n`channels status --probe` prints warnings when it can detect common channel misconfigurations, and includes small live checks (credentials, some permissions/membership).","url":"https://docs.openclaw.ai/channels/troubleshooting"},{"path":"channels/troubleshooting.md","title":"Channels","content":"- Discord: [/channels/discord#troubleshooting](/channels/discord#troubleshooting)\n- Telegram: [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting)\n- WhatsApp: [/channels/whatsapp#troubleshooting-quick](/channels/whatsapp#troubleshooting-quick)","url":"https://docs.openclaw.ai/channels/troubleshooting"},{"path":"channels/troubleshooting.md","title":"Telegram quick fixes","content":"- Logs show `HttpError: Network request for 'sendMessage' failed` or `sendChatAction` → check IPv6 DNS. If `api.telegram.org` resolves to IPv6 first and the host lacks IPv6 egress, force IPv4 or enable IPv6. See [/channels/telegram#troubleshooting](/channels/telegram#troubleshooting).\n- Logs show `setMyCommands failed` → check outbound HTTPS and DNS reachability to `api.telegram.org` (common on locked-down VPS or proxies).","url":"https://docs.openclaw.ai/channels/troubleshooting"},{"path":"channels/twitch.md","title":"twitch","content":"# Twitch (plugin)\n\nTwitch chat support via IRC connection. OpenClaw connects as a Twitch user (bot account) to receive and send messages in channels.","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Plugin required","content":"Twitch ships as a plugin and is not bundled with the core install.\n\nInstall via CLI (npm registry):\n\n```bash\nopenclaw plugins install @openclaw/twitch\n```\n\nLocal checkout (when running from a git repo):\n\n```bash\nopenclaw plugins install ./extensions/twitch\n```\n\nDetails: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Quick setup (beginner)","content":"1. Create a dedicated Twitch account for the bot (or use an existing account).\n2. Generate credentials: [Twitch Token Generator](https://twitchtokengenerator.com/)\n - Select **Bot Token**\n - Verify scopes `chat:read` and `chat:write` are selected\n - Copy the **Client ID** and **Access Token**\n3. Find your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/\n4. Configure the token:\n - Env: `OPENCLAW_TWITCH_ACCESS_TOKEN=...` (default account only)\n - Or config: `channels.twitch.accessToken`\n - If both are set, config takes precedence (env fallback is default-account only).\n5. Start the gateway.\n\n**⚠️ Important:** Add access control (`allowFrom` or `allowedRoles`) to prevent unauthorized users from triggering the bot. `requireMention` defaults to `true`.\n\nMinimal config:\n\n```json5\n{\n channels: {\n twitch: {\n enabled: true,\n username: \"openclaw\", // Bot's Twitch account\n accessToken: \"oauth:abc123...\", // OAuth Access Token (or use OPENCLAW_TWITCH_ACCESS_TOKEN env var)\n clientId: \"xyz789...\", // Client ID from Token Generator\n channel: \"vevisk\", // Which Twitch channel's chat to join (required)\n allowFrom: [\"123456789\"], // (recommended) Your Twitch user ID only - get it from https://www.streamweasels.com/tools/convert-twitch-username-to-user-id/\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"What it is","content":"- A Twitch channel owned by the Gateway.\n- Deterministic routing: replies always go back to Twitch.\n- Each account maps to an isolated session key `agent::twitch:`.\n- `username` is the bot's account (who authenticates), `channel` is which chat room to join.","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Setup (detailed)","content":"### Generate credentials\n\nUse [Twitch Token Generator](https://twitchtokengenerator.com/):\n\n- Select **Bot Token**\n- Verify scopes `chat:read` and `chat:write` are selected\n- Copy the **Client ID** and **Access Token**\n\nNo manual app registration needed. Tokens expire after several hours.\n\n### Configure the bot\n\n**Env var (default account only):**\n\n```bash\nOPENCLAW_TWITCH_ACCESS_TOKEN=oauth:abc123...\n```\n\n**Or config:**\n\n```json5\n{\n channels: {\n twitch: {\n enabled: true,\n username: \"openclaw\",\n accessToken: \"oauth:abc123...\",\n clientId: \"xyz789...\",\n channel: \"vevisk\",\n },\n },\n}\n```\n\nIf both env and config are set, config takes precedence.\n\n### Access control (recommended)\n\n```json5\n{\n channels: {\n twitch: {\n allowFrom: [\"123456789\"], // (recommended) Your Twitch user ID only\n },\n },\n}\n```\n\nPrefer `allowFrom` for a hard allowlist. Use `allowedRoles` instead if you want role-based access.\n\n**Available roles:** `\"moderator\"`, `\"owner\"`, `\"vip\"`, `\"subscriber\"`, `\"all\"`.\n\n**Why user IDs?** Usernames can change, allowing impersonation. User IDs are permanent.\n\nFind your Twitch user ID: https://www.streamweasels.com/tools/convert-twitch-username-%20to-user-id/ (Convert your Twitch username to ID)","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Token refresh (optional)","content":"Tokens from [Twitch Token Generator](https://twitchtokengenerator.com/) cannot be automatically refreshed - regenerate when expired.\n\nFor automatic token refresh, create your own Twitch application at [Twitch Developer Console](https://dev.twitch.tv/console) and add to config:\n\n```json5\n{\n channels: {\n twitch: {\n clientSecret: \"your_client_secret\",\n refreshToken: \"your_refresh_token\",\n },\n },\n}\n```\n\nThe bot automatically refreshes tokens before expiration and logs refresh events.","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Multi-account support","content":"Use `channels.twitch.accounts` with per-account tokens. See [`gateway/configuration`](/gateway/configuration) for the shared pattern.\n\nExample (one bot account in two channels):\n\n```json5\n{\n channels: {\n twitch: {\n accounts: {\n channel1: {\n username: \"openclaw\",\n accessToken: \"oauth:abc123...\",\n clientId: \"xyz789...\",\n channel: \"vevisk\",\n },\n channel2: {\n username: \"openclaw\",\n accessToken: \"oauth:def456...\",\n clientId: \"uvw012...\",\n channel: \"secondchannel\",\n },\n },\n },\n },\n}\n```\n\n**Note:** Each account needs its own token (one token per channel).","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Access control","content":"### Role-based restrictions\n\n```json5\n{\n channels: {\n twitch: {\n accounts: {\n default: {\n allowedRoles: [\"moderator\", \"vip\"],\n },\n },\n },\n },\n}\n```\n\n### Allowlist by User ID (most secure)\n\n```json5\n{\n channels: {\n twitch: {\n accounts: {\n default: {\n allowFrom: [\"123456789\", \"987654321\"],\n },\n },\n },\n },\n}\n```\n\n### Role-based access (alternative)\n\n`allowFrom` is a hard allowlist. When set, only those user IDs are allowed.\nIf you want role-based access, leave `allowFrom` unset and configure `allowedRoles` instead:\n\n```json5\n{\n channels: {\n twitch: {\n accounts: {\n default: {\n allowedRoles: [\"moderator\"],\n },\n },\n },\n },\n}\n```\n\n### Disable @mention requirement\n\nBy default, `requireMention` is `true`. To disable and respond to all messages:\n\n```json5\n{\n channels: {\n twitch: {\n accounts: {\n default: {\n requireMention: false,\n },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Troubleshooting","content":"First, run diagnostic commands:\n\n```bash\nopenclaw doctor\nopenclaw channels status --probe\n```\n\n### Bot doesn't respond to messages\n\n**Check access control:** Ensure your user ID is in `allowFrom`, or temporarily remove\n`allowFrom` and set `allowedRoles: [\"all\"]` to test.\n\n**Check the bot is in the channel:** The bot must join the channel specified in `channel`.\n\n### Token issues\n\n**\"Failed to connect\" or authentication errors:**\n\n- Verify `accessToken` is the OAuth access token value (typically starts with `oauth:` prefix)\n- Check token has `chat:read` and `chat:write` scopes\n- If using token refresh, verify `clientSecret` and `refreshToken` are set\n\n### Token refresh not working\n\n**Check logs for refresh events:**\n\n```\nUsing env token source for mybot\nAccess token refreshed for user 123456 (expires in 14400s)\n```\n\nIf you see \"token refresh disabled (no refresh token)\":\n\n- Ensure `clientSecret` is provided\n- Ensure `refreshToken` is provided","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Config","content":"**Account config:**\n\n- `username` - Bot username\n- `accessToken` - OAuth access token with `chat:read` and `chat:write`\n- `clientId` - Twitch Client ID (from Token Generator or your app)\n- `channel` - Channel to join (required)\n- `enabled` - Enable this account (default: `true`)\n- `clientSecret` - Optional: For automatic token refresh\n- `refreshToken` - Optional: For automatic token refresh\n- `expiresIn` - Token expiry in seconds\n- `obtainmentTimestamp` - Token obtained timestamp\n- `allowFrom` - User ID allowlist\n- `allowedRoles` - Role-based access control (`\"moderator\" | \"owner\" | \"vip\" | \"subscriber\" | \"all\"`)\n- `requireMention` - Require @mention (default: `true`)\n\n**Provider options:**\n\n- `channels.twitch.enabled` - Enable/disable channel startup\n- `channels.twitch.username` - Bot username (simplified single-account config)\n- `channels.twitch.accessToken` - OAuth access token (simplified single-account config)\n- `channels.twitch.clientId` - Twitch Client ID (simplified single-account config)\n- `channels.twitch.channel` - Channel to join (simplified single-account config)\n- `channels.twitch.accounts.` - Multi-account config (all account fields above)\n\nFull example:\n\n```json5\n{\n channels: {\n twitch: {\n enabled: true,\n username: \"openclaw\",\n accessToken: \"oauth:abc123...\",\n clientId: \"xyz789...\",\n channel: \"vevisk\",\n clientSecret: \"secret123...\",\n refreshToken: \"refresh456...\",\n allowFrom: [\"123456789\"],\n allowedRoles: [\"moderator\", \"vip\"],\n accounts: {\n default: {\n username: \"mybot\",\n accessToken: \"oauth:abc123...\",\n clientId: \"xyz789...\",\n channel: \"your_channel\",\n enabled: true,\n clientSecret: \"secret123...\",\n refreshToken: \"refresh456...\",\n expiresIn: 14400,\n obtainmentTimestamp: 1706092800000,\n allowFrom: [\"123456789\", \"987654321\"],\n allowedRoles: [\"moderator\"],\n },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Tool actions","content":"The agent can call `twitch` with action:\n\n- `send` - Send a message to a channel\n\nExample:\n\n```json5\n{\n action: \"twitch\",\n params: {\n message: \"Hello Twitch!\",\n to: \"#mychannel\",\n },\n}\n```","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Safety & ops","content":"- **Treat tokens like passwords** - Never commit tokens to git\n- **Use automatic token refresh** for long-running bots\n- **Use user ID allowlists** instead of usernames for access control\n- **Monitor logs** for token refresh events and connection status\n- **Scope tokens minimally** - Only request `chat:read` and `chat:write`\n- **If stuck**: Restart the gateway after confirming no other process owns the session","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/twitch.md","title":"Limits","content":"- **500 characters** per message (auto-chunked at word boundaries)\n- Markdown is stripped before chunking\n- No rate limiting (uses Twitch's built-in rate limits)","url":"https://docs.openclaw.ai/channels/twitch"},{"path":"channels/whatsapp.md","title":"whatsapp","content":"# WhatsApp (web channel)\n\nStatus: WhatsApp Web via Baileys only. Gateway owns the session(s).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Quick setup (beginner)","content":"1. Use a **separate phone number** if possible (recommended).\n2. Configure WhatsApp in `~/.openclaw/openclaw.json`.\n3. Run `openclaw channels login` to scan the QR code (Linked Devices).\n4. Start the gateway.\n\nMinimal config:\n\n```json5\n{\n channels: {\n whatsapp: {\n dmPolicy: \"allowlist\",\n allowFrom: [\"+15551234567\"],\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Goals","content":"- Multiple WhatsApp accounts (multi-account) in one Gateway process.\n- Deterministic routing: replies return to WhatsApp, no model routing.\n- Model sees enough context to understand quoted replies.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Config writes","content":"By default, WhatsApp is allowed to write config updates triggered by `/config set|unset` (requires `commands.config: true`).\n\nDisable with:\n\n```json5\n{\n channels: { whatsapp: { configWrites: false } },\n}\n```","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Architecture (who owns what)","content":"- **Gateway** owns the Baileys socket and inbox loop.\n- **CLI / macOS app** talk to the gateway; no direct Baileys use.\n- **Active listener** is required for outbound sends; otherwise send fails fast.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Getting a phone number (two modes)","content":"WhatsApp requires a real mobile number for verification. VoIP and virtual numbers are usually blocked. There are two supported ways to run OpenClaw on WhatsApp:\n\n### Dedicated number (recommended)\n\nUse a **separate phone number** for OpenClaw. Best UX, clean routing, no self-chat quirks. Ideal setup: **spare/old Android phone + eSIM**. Leave it on Wi‑Fi and power, and link it via QR.\n\n**WhatsApp Business:** You can use WhatsApp Business on the same device with a different number. Great for keeping your personal WhatsApp separate — install WhatsApp Business and register the OpenClaw number there.\n\n**Sample config (dedicated number, single-user allowlist):**\n\n```json5\n{\n channels: {\n whatsapp: {\n dmPolicy: \"allowlist\",\n allowFrom: [\"+15551234567\"],\n },\n },\n}\n```\n\n**Pairing mode (optional):**\nIf you want pairing instead of allowlist, set `channels.whatsapp.dmPolicy` to `pairing`. Unknown senders get a pairing code; approve with:\n`openclaw pairing approve whatsapp `\n\n### Personal number (fallback)\n\nQuick fallback: run OpenClaw on **your own number**. Message yourself (WhatsApp “Message yourself”) for testing so you don’t spam contacts. Expect to read verification codes on your main phone during setup and experiments. **Must enable self-chat mode.**\nWhen the wizard asks for your personal WhatsApp number, enter the phone you will message from (the owner/sender), not the assistant number.\n\n**Sample config (personal number, self-chat):**\n\n```json\n{\n \"whatsapp\": {\n \"selfChatMode\": true,\n \"dmPolicy\": \"allowlist\",\n \"allowFrom\": [\"+15551234567\"]\n }\n}\n```\n\nSelf-chat replies default to `[{identity.name}]` when set (otherwise `[openclaw]`)\nif `messages.responsePrefix` is unset. Set it explicitly to customize or disable\nthe prefix (use `\"\"` to remove it).\n\n### Number sourcing tips\n\n- **Local eSIM** from your country's mobile carrier (most reliable)\n - Austria: [hot.at](https://www.hot.at)\n - UK: [giffgaff](https://www.giffgaff.com) — free SIM, no contract\n- **Prepaid SIM** — cheap, just needs to receive one SMS for verification\n\n**Avoid:** TextNow, Google Voice, most \"free SMS\" services — WhatsApp blocks these aggressively.\n\n**Tip:** The number only needs to receive one verification SMS. After that, WhatsApp Web sessions persist via `creds.json`.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Why Not Twilio?","content":"- Early OpenClaw builds supported Twilio’s WhatsApp Business integration.\n- WhatsApp Business numbers are a poor fit for a personal assistant.\n- Meta enforces a 24‑hour reply window; if you haven’t responded in the last 24 hours, the business number can’t initiate new messages.\n- High-volume or “chatty” usage triggers aggressive blocking, because business accounts aren’t meant to send dozens of personal assistant messages.\n- Result: unreliable delivery and frequent blocks, so support was removed.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Login + credentials","content":"- Login command: `openclaw channels login` (QR via Linked Devices).\n- Multi-account login: `openclaw channels login --account ` (`` = `accountId`).\n- Default account (when `--account` is omitted): `default` if present, otherwise the first configured account id (sorted).\n- Credentials stored in `~/.openclaw/credentials/whatsapp//creds.json`.\n- Backup copy at `creds.json.bak` (restored on corruption).\n- Legacy compatibility: older installs stored Baileys files directly in `~/.openclaw/credentials/`.\n- Logout: `openclaw channels logout` (or `--account `) deletes WhatsApp auth state (but keeps shared `oauth.json`).\n- Logged-out socket => error instructs re-link.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Inbound flow (DM + group)","content":"- WhatsApp events come from `messages.upsert` (Baileys).\n- Inbox listeners are detached on shutdown to avoid accumulating event handlers in tests/restarts.\n- Status/broadcast chats are ignored.\n- Direct chats use E.164; groups use group JID.\n- **DM policy**: `channels.whatsapp.dmPolicy` controls direct chat access (default: `pairing`).\n - Pairing: unknown senders get a pairing code (approve via `openclaw pairing approve whatsapp `; codes expire after 1 hour).\n - Open: requires `channels.whatsapp.allowFrom` to include `\"*\"`.\n - Your linked WhatsApp number is implicitly trusted, so self messages skip ⁠`channels.whatsapp.dmPolicy` and `channels.whatsapp.allowFrom` checks.\n\n### Personal-number mode (fallback)\n\nIf you run OpenClaw on your **personal WhatsApp number**, enable `channels.whatsapp.selfChatMode` (see sample above).\n\nBehavior:\n\n- Outbound DMs never trigger pairing replies (prevents spamming contacts).\n- Inbound unknown senders still follow `channels.whatsapp.dmPolicy`.\n- Self-chat mode (allowFrom includes your number) avoids auto read receipts and ignores mention JIDs.\n- Read receipts sent for non-self-chat DMs.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Read receipts","content":"By default, the gateway marks inbound WhatsApp messages as read (blue ticks) once they are accepted.\n\nDisable globally:\n\n```json5\n{\n channels: { whatsapp: { sendReadReceipts: false } },\n}\n```\n\nDisable per account:\n\n```json5\n{\n channels: {\n whatsapp: {\n accounts: {\n personal: { sendReadReceipts: false },\n },\n },\n },\n}\n```\n\nNotes:\n\n- Self-chat mode always skips read receipts.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"WhatsApp FAQ: sending messages + pairing","content":"**Will OpenClaw message random contacts when I link WhatsApp?** \nNo. Default DM policy is **pairing**, so unknown senders only get a pairing code and their message is **not processed**. OpenClaw only replies to chats it receives, or to sends you explicitly trigger (agent/CLI).\n\n**How does pairing work on WhatsApp?** \nPairing is a DM gate for unknown senders:\n\n- First DM from a new sender returns a short code (message is not processed).\n- Approve with: `openclaw pairing approve whatsapp ` (list with `openclaw pairing list whatsapp`).\n- Codes expire after 1 hour; pending requests are capped at 3 per channel.\n\n**Can multiple people use different OpenClaw instances on one WhatsApp number?** \nYes, by routing each sender to a different agent via `bindings` (peer `kind: \"dm\"`, sender E.164 like `+15551234567`). Replies still come from the **same WhatsApp account**, and direct chats collapse to each agent’s main session, so use **one agent per person**. DM access control (`dmPolicy`/`allowFrom`) is global per WhatsApp account. See [Multi-Agent Routing](/concepts/multi-agent).\n\n**Why do you ask for my phone number in the wizard?** \nThe wizard uses it to set your **allowlist/owner** so your own DMs are permitted. It’s not used for auto-sending. If you run on your personal WhatsApp number, use that same number and enable `channels.whatsapp.selfChatMode`.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Message normalization (what the model sees)","content":"- `Body` is the current message body with envelope.\n- Quoted reply context is **always appended**:\n ```\n [Replying to +1555 id:ABC123]\n >\n [/Replying]\n ```\n- Reply metadata also set:\n - `ReplyToId` = stanzaId\n - `ReplyToBody` = quoted body or media placeholder\n - `ReplyToSender` = E.164 when known\n- Media-only inbound messages use placeholders:\n - ``","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Groups","content":"- Groups map to `agent::whatsapp:group:` sessions.\n- Group policy: `channels.whatsapp.groupPolicy = open|disabled|allowlist` (default `allowlist`).\n- Activation modes:\n - `mention` (default): requires @mention or regex match.\n - `always`: always triggers.\n- `/activation mention|always` is owner-only and must be sent as a standalone message.\n- Owner = `channels.whatsapp.allowFrom` (or self E.164 if unset).\n- **History injection** (pending-only):\n - Recent _unprocessed_ messages (default 50) inserted under:\n `[Chat messages since your last reply - for context]` (messages already in the session are not re-injected)\n - Current message under:\n `[Current message - respond to this]`\n - Sender suffix appended: `[from: Name (+E164)]`\n- Group metadata cached 5 min (subject + participants).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Reply delivery (threading)","content":"- WhatsApp Web sends standard messages (no quoted reply threading in the current gateway).\n- Reply tags are ignored on this channel.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Acknowledgment reactions (auto-react on receipt)","content":"WhatsApp can automatically send emoji reactions to incoming messages immediately upon receipt, before the bot generates a reply. This provides instant feedback to users that their message was received.\n\n**Configuration:**\n\n```json\n{\n \"whatsapp\": {\n \"ackReaction\": {\n \"emoji\": \"👀\",\n \"direct\": true,\n \"group\": \"mentions\"\n }\n }\n}\n```\n\n**Options:**\n\n- `emoji` (string): Emoji to use for acknowledgment (e.g., \"👀\", \"✅\", \"📨\"). Empty or omitted = feature disabled.\n- `direct` (boolean, default: `true`): Send reactions in direct/DM chats.\n- `group` (string, default: `\"mentions\"`): Group chat behavior:\n - `\"always\"`: React to all group messages (even without @mention)\n - `\"mentions\"`: React only when bot is @mentioned\n - `\"never\"`: Never react in groups\n\n**Per-account override:**\n\n```json\n{\n \"whatsapp\": {\n \"accounts\": {\n \"work\": {\n \"ackReaction\": {\n \"emoji\": \"✅\",\n \"direct\": false,\n \"group\": \"always\"\n }\n }\n }\n }\n}\n```\n\n**Behavior notes:**\n\n- Reactions are sent **immediately** upon message receipt, before typing indicators or bot replies.\n- In groups with `requireMention: false` (activation: always), `group: \"mentions\"` will react to all messages (not just @mentions).\n- Fire-and-forget: reaction failures are logged but don't prevent the bot from replying.\n- Participant JID is automatically included for group reactions.\n- WhatsApp ignores `messages.ackReaction`; use `channels.whatsapp.ackReaction` instead.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Agent tool (reactions)","content":"- Tool: `whatsapp` with `react` action (`chatJid`, `messageId`, `emoji`, optional `remove`).\n- Optional: `participant` (group sender), `fromMe` (reacting to your own message), `accountId` (multi-account).\n- Reaction removal semantics: see [/tools/reactions](/tools/reactions).\n- Tool gating: `channels.whatsapp.actions.reactions` (default: enabled).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Limits","content":"- Outbound text is chunked to `channels.whatsapp.textChunkLimit` (default 4000).\n- Optional newline chunking: set `channels.whatsapp.chunkMode=\"newline\"` to split on blank lines (paragraph boundaries) before length chunking.\n- Inbound media saves are capped by `channels.whatsapp.mediaMaxMb` (default 50 MB).\n- Outbound media items are capped by `agents.defaults.mediaMaxMb` (default 5 MB).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Outbound send (text + media)","content":"- Uses active web listener; error if gateway not running.\n- Text chunking: 4k max per message (configurable via `channels.whatsapp.textChunkLimit`, optional `channels.whatsapp.chunkMode`).\n- Media:\n - Image/video/audio/document supported.\n - Audio sent as PTT; `audio/ogg` => `audio/ogg; codecs=opus`.\n - Caption only on first media item.\n - Media fetch supports HTTP(S) and local paths.\n - Animated GIFs: WhatsApp expects MP4 with `gifPlayback: true` for inline looping.\n - CLI: `openclaw message send --media --gif-playback`\n - Gateway: `send` params include `gifPlayback: true`","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Voice notes (PTT audio)","content":"WhatsApp sends audio as **voice notes** (PTT bubble).\n\n- Best results: OGG/Opus. OpenClaw rewrites `audio/ogg` to `audio/ogg; codecs=opus`.\n- `[[audio_as_voice]]` is ignored for WhatsApp (audio already ships as voice note).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Media limits + optimization","content":"- Default outbound cap: 5 MB (per media item).\n- Override: `agents.defaults.mediaMaxMb`.\n- Images are auto-optimized to JPEG under cap (resize + quality sweep).\n- Oversize media => error; media reply falls back to text warning.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Heartbeats","content":"- **Gateway heartbeat** logs connection health (`web.heartbeatSeconds`, default 60s).\n- **Agent heartbeat** can be configured per agent (`agents.list[].heartbeat`) or globally\n via `agents.defaults.heartbeat` (fallback when no per-agent entries are set).\n - Uses the configured heartbeat prompt (default: `Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.`) + `HEARTBEAT_OK` skip behavior.\n - Delivery defaults to the last used channel (or configured target).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Reconnect behavior","content":"- Backoff policy: `web.reconnect`:\n - `initialMs`, `maxMs`, `factor`, `jitter`, `maxAttempts`.\n- If maxAttempts reached, web monitoring stops (degraded).\n- Logged-out => stop and require re-link.","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Config quick map","content":"- `channels.whatsapp.dmPolicy` (DM policy: pairing/allowlist/open/disabled).\n- `channels.whatsapp.selfChatMode` (same-phone setup; bot uses your personal WhatsApp number).\n- `channels.whatsapp.allowFrom` (DM allowlist). WhatsApp uses E.164 phone numbers (no usernames).\n- `channels.whatsapp.mediaMaxMb` (inbound media save cap).\n- `channels.whatsapp.ackReaction` (auto-reaction on message receipt: `{emoji, direct, group}`).\n- `channels.whatsapp.accounts..*` (per-account settings + optional `authDir`).\n- `channels.whatsapp.accounts..mediaMaxMb` (per-account inbound media cap).\n- `channels.whatsapp.accounts..ackReaction` (per-account ack reaction override).\n- `channels.whatsapp.groupAllowFrom` (group sender allowlist).\n- `channels.whatsapp.groupPolicy` (group policy).\n- `channels.whatsapp.historyLimit` / `channels.whatsapp.accounts..historyLimit` (group history context; `0` disables).\n- `channels.whatsapp.dmHistoryLimit` (DM history limit in user turns). Per-user overrides: `channels.whatsapp.dms[\"\"].historyLimit`.\n- `channels.whatsapp.groups` (group allowlist + mention gating defaults; use `\"*\"` to allow all)\n- `channels.whatsapp.actions.reactions` (gate WhatsApp tool reactions).\n- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`)\n- `messages.groupChat.historyLimit`\n- `channels.whatsapp.messagePrefix` (inbound prefix; per-account: `channels.whatsapp.accounts..messagePrefix`; deprecated: `messages.messagePrefix`)\n- `messages.responsePrefix` (outbound prefix)\n- `agents.defaults.mediaMaxMb`\n- `agents.defaults.heartbeat.every`\n- `agents.defaults.heartbeat.model` (optional override)\n- `agents.defaults.heartbeat.target`\n- `agents.defaults.heartbeat.to`\n- `agents.defaults.heartbeat.session`\n- `agents.list[].heartbeat.*` (per-agent overrides)\n- `session.*` (scope, idle, store, mainKey)\n- `web.enabled` (disable channel startup when false)\n- `web.heartbeatSeconds`\n- `web.reconnect.*`","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Logs + troubleshooting","content":"- Subsystems: `whatsapp/inbound`, `whatsapp/outbound`, `web-heartbeat`, `web-reconnect`.\n- Log file: `/tmp/openclaw/openclaw-YYYY-MM-DD.log` (configurable).\n- Troubleshooting guide: [Gateway troubleshooting](/gateway/troubleshooting).","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/whatsapp.md","title":"Troubleshooting (quick)","content":"**Not linked / QR login required**\n\n- Symptom: `channels status` shows `linked: false` or warns “Not linked”.\n- Fix: run `openclaw channels login` on the gateway host and scan the QR (WhatsApp → Settings → Linked Devices).\n\n**Linked but disconnected / reconnect loop**\n\n- Symptom: `channels status` shows `running, disconnected` or warns “Linked but disconnected”.\n- Fix: `openclaw doctor` (or restart the gateway). If it persists, relink via `channels login` and inspect `openclaw logs --follow`.\n\n**Bun runtime**\n\n- Bun is **not recommended**. WhatsApp (Baileys) and Telegram are unreliable on Bun.\n Run the gateway with **Node**. (See Getting Started runtime note.)","url":"https://docs.openclaw.ai/channels/whatsapp"},{"path":"channels/zalo.md","title":"zalo","content":"# Zalo (Bot API)\n\nStatus: experimental. Direct messages only; groups coming soon per Zalo docs.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Plugin required","content":"Zalo ships as a plugin and is not bundled with the core install.\n\n- Install via CLI: `openclaw plugins install @openclaw/zalo`\n- Or select **Zalo** during onboarding and confirm the install prompt\n- Details: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Quick setup (beginner)","content":"1. Install the Zalo plugin:\n - From a source checkout: `openclaw plugins install ./extensions/zalo`\n - From npm (if published): `openclaw plugins install @openclaw/zalo`\n - Or pick **Zalo** in onboarding and confirm the install prompt\n2. Set the token:\n - Env: `ZALO_BOT_TOKEN=...`\n - Or config: `channels.zalo.botToken: \"...\"`.\n3. Restart the gateway (or finish onboarding).\n4. DM access is pairing by default; approve the pairing code on first contact.\n\nMinimal config:\n\n```json5\n{\n channels: {\n zalo: {\n enabled: true,\n botToken: \"12345689:abc-xyz\",\n dmPolicy: \"pairing\",\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"What it is","content":"Zalo is a Vietnam-focused messaging app; its Bot API lets the Gateway run a bot for 1:1 conversations.\nIt is a good fit for support or notifications where you want deterministic routing back to Zalo.\n\n- A Zalo Bot API channel owned by the Gateway.\n- Deterministic routing: replies go back to Zalo; the model never chooses channels.\n- DMs share the agent's main session.\n- Groups are not yet supported (Zalo docs state \"coming soon\").","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Setup (fast path)","content":"### 1) Create a bot token (Zalo Bot Platform)\n\n1. Go to **https://bot.zaloplatforms.com** and sign in.\n2. Create a new bot and configure its settings.\n3. Copy the bot token (format: `12345689:abc-xyz`).\n\n### 2) Configure the token (env or config)\n\nExample:\n\n```json5\n{\n channels: {\n zalo: {\n enabled: true,\n botToken: \"12345689:abc-xyz\",\n dmPolicy: \"pairing\",\n },\n },\n}\n```\n\nEnv option: `ZALO_BOT_TOKEN=...` (works for the default account only).\n\nMulti-account support: use `channels.zalo.accounts` with per-account tokens and optional `name`.\n\n3. Restart the gateway. Zalo starts when a token is resolved (env or config).\n4. DM access defaults to pairing. Approve the code when the bot is first contacted.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"How it works (behavior)","content":"- Inbound messages are normalized into the shared channel envelope with media placeholders.\n- Replies always route back to the same Zalo chat.\n- Long-polling by default; webhook mode available with `channels.zalo.webhookUrl`.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Limits","content":"- Outbound text is chunked to 2000 characters (Zalo API limit).\n- Media downloads/uploads are capped by `channels.zalo.mediaMaxMb` (default 5).\n- Streaming is blocked by default due to the 2000 char limit making streaming less useful.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Access control (DMs)","content":"### DM access\n\n- Default: `channels.zalo.dmPolicy = \"pairing\"`. Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).\n- Approve via:\n - `openclaw pairing list zalo`\n - `openclaw pairing approve zalo `\n- Pairing is the default token exchange. Details: [Pairing](/start/pairing)\n- `channels.zalo.allowFrom` accepts numeric user IDs (no username lookup available).","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Long-polling vs webhook","content":"- Default: long-polling (no public URL required).\n- Webhook mode: set `channels.zalo.webhookUrl` and `channels.zalo.webhookSecret`.\n - The webhook secret must be 8-256 characters.\n - Webhook URL must use HTTPS.\n - Zalo sends events with `X-Bot-Api-Secret-Token` header for verification.\n - Gateway HTTP handles webhook requests at `channels.zalo.webhookPath` (defaults to the webhook URL path).\n\n**Note:** getUpdates (polling) and webhook are mutually exclusive per Zalo API docs.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Supported message types","content":"- **Text messages**: Full support with 2000 character chunking.\n- **Image messages**: Download and process inbound images; send images via `sendPhoto`.\n- **Stickers**: Logged but not fully processed (no agent response).\n- **Unsupported types**: Logged (e.g., messages from protected users).","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Capabilities","content":"| Feature | Status |\n| --------------- | ------------------------------ |\n| Direct messages | ✅ Supported |\n| Groups | ❌ Coming soon (per Zalo docs) |\n| Media (images) | ✅ Supported |\n| Reactions | ❌ Not supported |\n| Threads | ❌ Not supported |\n| Polls | ❌ Not supported |\n| Native commands | ❌ Not supported |\n| Streaming | ⚠️ Blocked (2000 char limit) |","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Delivery targets (CLI/cron)","content":"- Use a chat id as the target.\n- Example: `openclaw message send --channel zalo --target 123456789 --message \"hi\"`.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Troubleshooting","content":"**Bot doesn't respond:**\n\n- Check that the token is valid: `openclaw channels status --probe`\n- Verify the sender is approved (pairing or allowFrom)\n- Check gateway logs: `openclaw logs --follow`\n\n**Webhook not receiving events:**\n\n- Ensure webhook URL uses HTTPS\n- Verify secret token is 8-256 characters\n- Confirm the gateway HTTP endpoint is reachable on the configured path\n- Check that getUpdates polling is not running (they're mutually exclusive)","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalo.md","title":"Configuration reference (Zalo)","content":"Full configuration: [Configuration](/gateway/configuration)\n\nProvider options:\n\n- `channels.zalo.enabled`: enable/disable channel startup.\n- `channels.zalo.botToken`: bot token from Zalo Bot Platform.\n- `channels.zalo.tokenFile`: read token from file path.\n- `channels.zalo.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing).\n- `channels.zalo.allowFrom`: DM allowlist (user IDs). `open` requires `\"*\"`. The wizard will ask for numeric IDs.\n- `channels.zalo.mediaMaxMb`: inbound/outbound media cap (MB, default 5).\n- `channels.zalo.webhookUrl`: enable webhook mode (HTTPS required).\n- `channels.zalo.webhookSecret`: webhook secret (8-256 chars).\n- `channels.zalo.webhookPath`: webhook path on the gateway HTTP server.\n- `channels.zalo.proxy`: proxy URL for API requests.\n\nMulti-account options:\n\n- `channels.zalo.accounts..botToken`: per-account token.\n- `channels.zalo.accounts..tokenFile`: per-account token file.\n- `channels.zalo.accounts..name`: display name.\n- `channels.zalo.accounts..enabled`: enable/disable account.\n- `channels.zalo.accounts..dmPolicy`: per-account DM policy.\n- `channels.zalo.accounts..allowFrom`: per-account allowlist.\n- `channels.zalo.accounts..webhookUrl`: per-account webhook URL.\n- `channels.zalo.accounts..webhookSecret`: per-account webhook secret.\n- `channels.zalo.accounts..webhookPath`: per-account webhook path.\n- `channels.zalo.accounts..proxy`: per-account proxy URL.","url":"https://docs.openclaw.ai/channels/zalo"},{"path":"channels/zalouser.md","title":"zalouser","content":"# Zalo Personal (unofficial)\n\nStatus: experimental. This integration automates a **personal Zalo account** via `zca-cli`.\n\n> **Warning:** This is an unofficial integration and may result in account suspension/ban. Use at your own risk.","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Plugin required","content":"Zalo Personal ships as a plugin and is not bundled with the core install.\n\n- Install via CLI: `openclaw plugins install @openclaw/zalouser`\n- Or from a source checkout: `openclaw plugins install ./extensions/zalouser`\n- Details: [Plugins](/plugin)","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Prerequisite: zca-cli","content":"The Gateway machine must have the `zca` binary available in `PATH`.\n\n- Verify: `zca --version`\n- If missing, install zca-cli (see `extensions/zalouser/README.md` or the upstream zca-cli docs).","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Quick setup (beginner)","content":"1. Install the plugin (see above).\n2. Login (QR, on the Gateway machine):\n - `openclaw channels login --channel zalouser`\n - Scan the QR code in the terminal with the Zalo mobile app.\n3. Enable the channel:\n\n```json5\n{\n channels: {\n zalouser: {\n enabled: true,\n dmPolicy: \"pairing\",\n },\n },\n}\n```\n\n4. Restart the Gateway (or finish onboarding).\n5. DM access defaults to pairing; approve the pairing code on first contact.","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"What it is","content":"- Uses `zca listen` to receive inbound messages.\n- Uses `zca msg ...` to send replies (text/media/link).\n- Designed for “personal account” use cases where Zalo Bot API is not available.","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Naming","content":"Channel id is `zalouser` to make it explicit this automates a **personal Zalo user account** (unofficial). We keep `zalo` reserved for a potential future official Zalo API integration.","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Finding IDs (directory)","content":"Use the directory CLI to discover peers/groups and their IDs:\n\n```bash\nopenclaw directory self --channel zalouser\nopenclaw directory peers list --channel zalouser --query \"name\"\nopenclaw directory groups list --channel zalouser --query \"work\"\n```","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Limits","content":"- Outbound text is chunked to ~2000 characters (Zalo client limits).\n- Streaming is blocked by default.","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Access control (DMs)","content":"`channels.zalouser.dmPolicy` supports: `pairing | allowlist | open | disabled` (default: `pairing`).\n`channels.zalouser.allowFrom` accepts user IDs or names. The wizard resolves names to IDs via `zca friend find` when available.\n\nApprove via:\n\n- `openclaw pairing list zalouser`\n- `openclaw pairing approve zalouser `","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Group access (optional)","content":"- Default: `channels.zalouser.groupPolicy = \"open\"` (groups allowed). Use `channels.defaults.groupPolicy` to override the default when unset.\n- Restrict to an allowlist with:\n - `channels.zalouser.groupPolicy = \"allowlist\"`\n - `channels.zalouser.groups` (keys are group IDs or names)\n- Block all groups: `channels.zalouser.groupPolicy = \"disabled\"`.\n- The configure wizard can prompt for group allowlists.\n- On startup, OpenClaw resolves group/user names in allowlists to IDs and logs the mapping; unresolved entries are kept as typed.\n\nExample:\n\n```json5\n{\n channels: {\n zalouser: {\n groupPolicy: \"allowlist\",\n groups: {\n \"123456789\": { allow: true },\n \"Work Chat\": { allow: true },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Multi-account","content":"Accounts map to zca profiles. Example:\n\n```json5\n{\n channels: {\n zalouser: {\n enabled: true,\n defaultAccount: \"default\",\n accounts: {\n work: { enabled: true, profile: \"work\" },\n },\n },\n },\n}\n```","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"channels/zalouser.md","title":"Troubleshooting","content":"**`zca` not found:**\n\n- Install zca-cli and ensure it’s on `PATH` for the Gateway process.\n\n**Login doesn’t stick:**\n\n- `openclaw channels status --probe`\n- Re-login: `openclaw channels logout --channel zalouser && openclaw channels login --channel zalouser`","url":"https://docs.openclaw.ai/channels/zalouser"},{"path":"cli/acp.md","title":"acp","content":"# acp\n\nRun the ACP (Agent Client Protocol) bridge that talks to a OpenClaw Gateway.\n\nThis command speaks ACP over stdio for IDEs and forwards prompts to the Gateway\nover WebSocket. It keeps ACP sessions mapped to Gateway session keys.","url":"https://docs.openclaw.ai/cli/acp"},{"path":"cli/acp.md","title":"Usage","content":"```bash\nopenclaw acp\n\n# Remote Gateway\nopenclaw acp --url wss://gateway-host:18789 --token \n\n# Attach to an existing session key\nopenclaw acp --session agent:main:main\n\n# Attach by label (must already exist)\nopenclaw acp --session-label \"support inbox\"\n\n# Reset the session key before the first prompt\nopenclaw acp --session agent:main:main --reset-session\n```","url":"https://docs.openclaw.ai/cli/acp"},{"path":"cli/acp.md","title":"ACP client (debug)","content":"Use the built-in ACP client to sanity-check the bridge without an IDE.\nIt spawns the ACP bridge and lets you type prompts interactively.\n\n```bash\nopenclaw acp client\n\n# Point the spawned bridge at a remote Gateway\nopenclaw acp client --server-args --url wss://gateway-host:18789 --token \n\n# Override the server command (default: openclaw)\nopenclaw acp client --server \"node\" --server-args openclaw.mjs acp --url ws://127.0.0.1:19001\n```","url":"https://docs.openclaw.ai/cli/acp"},{"path":"cli/acp.md","title":"How to use this","content":"Use ACP when an IDE (or other client) speaks Agent Client Protocol and you want\nit to drive a OpenClaw Gateway session.\n\n1. Ensure the Gateway is running (local or remote).\n2. Configure the Gateway target (config or flags).\n3. Point your IDE to run `openclaw acp` over stdio.\n\nExample config (persisted):\n\n```bash\nopenclaw config set gateway.remote.url wss://gateway-host:18789\nopenclaw config set gateway.remote.token \n```\n\nExample direct run (no config write):\n\n```bash\nopenclaw acp --url wss://gateway-host:18789 --token \n```","url":"https://docs.openclaw.ai/cli/acp"},{"path":"cli/acp.md","title":"Selecting agents","content":"ACP does not pick agents directly. It routes by the Gateway session key.\n\nUse agent-scoped session keys to target a specific agent:\n\n```bash\nopenclaw acp --session agent:main:main\nopenclaw acp --session agent:design:main\nopenclaw acp --session agent:qa:bug-123\n```\n\nEach ACP session maps to a single Gateway session key. One agent can have many\nsessions; ACP defaults to an isolated `acp:` session unless you override\nthe key or label.","url":"https://docs.openclaw.ai/cli/acp"},{"path":"cli/acp.md","title":"Zed editor setup","content":"Add a custom ACP agent in `~/.config/zed/settings.json` (or use Zed’s Settings UI):\n\n```json\n{\n \"agent_servers\": {\n \"OpenClaw ACP\": {\n \"type\": \"custom\",\n \"command\": \"openclaw\",\n \"args\": [\"acp\"],\n \"env\": {}\n }\n }\n}\n```\n\nTo target a specific Gateway or agent:\n\n```json\n{\n \"agent_servers\": {\n \"OpenClaw ACP\": {\n \"type\": \"custom\",\n \"command\": \"openclaw\",\n \"args\": [\n \"acp\",\n \"--url\",\n \"wss://gateway-host:18789\",\n \"--token\",\n \"\",\n \"--session\",\n \"agent:design:main\"\n ],\n \"env\": {}\n }\n }\n}\n```\n\nIn Zed, open the Agent panel and select “OpenClaw ACP” to start a thread.","url":"https://docs.openclaw.ai/cli/acp"},{"path":"cli/acp.md","title":"Session mapping","content":"By default, ACP sessions get an isolated Gateway session key with an `acp:` prefix.\nTo reuse a known session, pass a session key or label:\n\n- `--session `: use a specific Gateway session key.\n- `--session-label