Regional platforms

Feishu

Status: ** production-ready for bot DMs + group chats. WebSocket is the default mode; webhook mode is optional.

Feishu/Lark is an all-in-one collaboration platform where teams chat, share documents, manage calendars, and get work done together.

Status: production-ready for bot DMs + group chats. WebSocket is the default mode; webhook mode is optional.


Quick start

  • Run the channel setup wizard

    bash
    openclaw channels login --channel feishu

    Choose manual setup to paste an App ID and App Secret from Feishu Open Platform, or choose QR setup to create a bot automatically. If the domestic Feishu mobile app does not react to the QR code, rerun setup and choose manual setup.

  • After setup completes, restart the gateway to apply the changes

    bash
    openclaw gateway restart

  • Access control

    Direct messages

    Configure dmPolicy to control who can DM the bot:

    • "pairing" - unknown users receive a pairing code; approve via CLI
    • "allowlist" - only users listed in allowFrom can chat (default: bot owner only)
    • "open" - allow public DMs only when allowFrom includes "*"; with restrictive entries, only matching users can chat
    • "disabled" - disable all DMs

    Approve a pairing request:

    bash
    openclaw pairing list feishuopenclaw pairing approve feishu <CODE>

    Group chats

    Group policy (channels.feishu.groupPolicy):

    Value Behavior
    "open" Respond to all messages in groups
    "allowlist" Only respond to groups in groupAllowFrom or explicitly configured under groups.<chat_id>
    "disabled" Disable all group messages; explicit groups.<chat_id> entries do not override this

    Default: allowlist

    Mention requirement (channels.feishu.requireMention):

    • true - require @mention (default)
    • false - respond without @mention
    • Per-group override: channels.feishu.groups.<chat_id>.requireMention
    • Broadcast-only @all and @_all are not treated as bot mentions. A message that mentions both @all and the bot directly still counts as a bot mention.

    Group configuration examples

    Allow all groups, no @mention required

    json5
    {  channels: {    feishu: {      groupPolicy: "open",    },  },}

    Allow all groups, still require @mention

    json5
    {  channels: {    feishu: {      groupPolicy: "open",      requireMention: true,    },  },}

    Allow specific groups only

    json5
    {  channels: {    feishu: {      groupPolicy: "allowlist",      // Group IDs look like: oc_xxx      groupAllowFrom: ["oc_xxx", "oc_yyy"],    },  },}

    In allowlist mode, you can also admit a group by adding an explicit groups.<chat_id> entry. Explicit entries do not override groupPolicy: "disabled". Wildcard defaults under groups.* configure matching groups, but they do not admit groups by themselves.

    json5
    {  channels: {    feishu: {      groupPolicy: "allowlist",      groups: {        oc_xxx: {          requireMention: false,        },      },    },  },}

    Restrict senders within a group

    json5
    {  channels: {    feishu: {      groupPolicy: "allowlist",      groupAllowFrom: ["oc_xxx"],      groups: {        oc_xxx: {          // User open_ids look like: ou_xxx          allowFrom: ["ou_user1", "ou_user2"],        },      },    },  },}

    Get group/user IDs

    Group IDs (chat_id, format: oc_xxx)

    Open the group in Feishu/Lark, click the menu icon in the top-right corner, and go to Settings. The group ID (chat_id) is listed on the settings page.

    Get Group ID

    User IDs (open_id, format: ou_xxx)

    Start the gateway, send a DM to the bot, then check the logs:

    bash
    openclaw logs --follow

    Look for open_id in the log output. You can also check pending pairing requests:

    bash
    openclaw pairing list feishu

    Common commands

    Command Description
    /status Show bot status
    /reset Reset the current session
    /model Show or switch the AI model

    Troubleshooting

    Bot does not respond in group chats

    1. Ensure the bot is added to the group
    2. Ensure you @mention the bot (required by default)
    3. Verify groupPolicy is not "disabled"
    4. Check logs: openclaw logs --follow

    Bot does not receive messages

    1. Ensure the bot is published and approved in Feishu Open Platform / Lark Developer
    2. Ensure event subscription includes im.message.receive_v1
    3. Ensure persistent connection (WebSocket) is selected
    4. Ensure all required permission scopes are granted
    5. Ensure the gateway is running: openclaw gateway status
    6. Check logs: openclaw logs --follow

    QR setup does not react in the Feishu mobile app

    1. Rerun setup: openclaw channels login --channel feishu
    2. Choose manual setup
    3. In Feishu Open Platform, create a self-built app and copy its App ID and App Secret
    4. Paste those credentials into the setup wizard

    App Secret leaked

    1. Reset the App Secret in Feishu Open Platform / Lark Developer
    2. Update the value in your config
    3. Restart the gateway: openclaw gateway restart

    Advanced configuration

    Multiple accounts

    json5
    {  channels: {    feishu: {      defaultAccount: "main",      accounts: {        main: {          appId: "cli_xxx",          appSecret: "xxx",          name: "Primary bot",          tts: {            providers: {              openai: { voice: "shimmer" },            },          },        },        backup: {          appId: "cli_yyy",          appSecret: "yyy",          name: "Backup bot",          enabled: false,        },      },    },  },}

    defaultAccount controls which account is used when outbound APIs do not specify an accountId. accounts.<id>.tts uses the same shape as messages.tts and deep-merges over global TTS config, so multi-bot Feishu setups can keep shared provider credentials globally while overriding only voice, model, persona, or auto mode per account.

    Message limits

    • textChunkLimit - outbound text chunk size (default: 2000 chars)
    • mediaMaxMb - media upload/download limit (default: 30 MB)

    Streaming

    Feishu/Lark supports streaming replies via interactive cards. When enabled, the bot updates the card in real time as it generates text.

    json5
    {  channels: {    feishu: {      streaming: true, // enable streaming card output (default: true)      blockStreaming: true, // opt into completed-block streaming    },  },}

    Set streaming: false to send the complete reply in one message. blockStreaming is off by default; enable it only when you want completed assistant blocks flushed before the final reply.

    Quota optimization

    Reduce the number of Feishu/Lark API calls with two optional flags:

    • typingIndicator (default true): set false to skip typing reaction calls
    • resolveSenderNames (default true): set false to skip sender profile lookups
    json5
    {  channels: {    feishu: {      typingIndicator: false,      resolveSenderNames: false,    },  },}

    ACP sessions

    Feishu/Lark supports ACP for DMs and group thread messages. Feishu/Lark ACP is text-command driven - there are no native slash-command menus, so use /acp ... messages directly in the conversation.

    Persistent ACP binding

    json5
    {  agents: {    list: [      {        id: "codex",        runtime: {          type: "acp",          acp: {            agent: "codex",            backend: "acpx",            mode: "persistent",            cwd: "/workspace/openclaw",          },        },      },    ],  },  bindings: [    {      type: "acp",      agentId: "codex",      match: {        channel: "feishu",        accountId: "default",        peer: { kind: "direct", id: "ou_1234567890" },      },    },    {      type: "acp",      agentId: "codex",      match: {        channel: "feishu",        accountId: "default",        peer: { kind: "group", id: "oc_group_chat:topic:om_topic_root" },      },      acp: { label: "codex-feishu-topic" },    },  ],}

    Spawn ACP from chat

    In a Feishu/Lark DM or thread:

    text
    /acp spawn codex --thread here

    --thread here works for DMs and Feishu/Lark thread messages. Follow-up messages in the bound conversation route directly to that ACP session.

    Multi-agent routing

    Use bindings to route Feishu/Lark DMs or groups to different agents.

    json5
    {  agents: {    list: [      { id: "main" },      { id: "agent-a", workspace: "/home/user/agent-a" },      { id: "agent-b", workspace: "/home/user/agent-b" },    ],  },  bindings: [    {      agentId: "agent-a",      match: {        channel: "feishu",        peer: { kind: "direct", id: "ou_xxx" },      },    },    {      agentId: "agent-b",      match: {        channel: "feishu",        peer: { kind: "group", id: "oc_zzz" },      },    },  ],}

    Routing fields:

    • match.channel: "feishu"
    • match.peer.kind: "direct" (DM) or "group" (group chat)
    • match.peer.id: user Open ID (ou_xxx) or group ID (oc_xxx)

    See Get group/user IDs for lookup tips.


    Per-user agent isolation (Dynamic Agent Creation)

    Enable dynamicAgentCreation to automatically create isolated agent instances for each DM user. Each user gets their own:

    • Independent workspace directory
    • Separate USER.md / SOUL.md / MEMORY.md
    • Private conversation history
    • Isolated skills and state

    This is essential for public bots where you want each user to have their own private AI assistant experience.

    Quick setup

    json5
    {  channels: {    feishu: {      dmPolicy: "open",      allowFrom: ["*"],      dynamicAgentCreation: {        enabled: true,        workspaceTemplate: "~/.openclaw/workspace-{agentId}",        agentDirTemplate: "~/.openclaw/agents/{agentId}/agent",      },    },  },  session: {    // Critical: makes each user's DM their "main session"    // Automatically loads USER.md / SOUL.md / MEMORY.md    // For stronger isolation, use "per-channel-peer" instead    dmScope: "main",  },}

    How it works

    When a new user sends their first DM:

    1. The channel generates a unique agentId = feishu-{user_open_id}
    2. Creates a new workspace at workspaceTemplate path
    3. Registers the agent and creates a binding for this user
    4. The workspace helper ensures bootstrap files (AGENTS.md, SOUL.md, USER.md, etc.) on first access
    5. Routes all future messages from this user to their dedicated agent

    Configuration options

    Setting Description Default
    channels.feishu.dynamicAgentCreation.enabled Enable automatic per-user agent creation false
    channels.feishu.dynamicAgentCreation.workspaceTemplate Path template for dynamic agent workspaces ~/.openclaw/workspace-{agentId}
    channels.feishu.dynamicAgentCreation.agentDirTemplate Agent directory name template ~/.openclaw/agents/{agentId}/agent
    channels.feishu.dynamicAgentCreation.maxAgents Maximum number of dynamic agents to create unlimited

    Template variables:

    • {agentId} - the generated agent ID (e.g., feishu-ou_xxxxxx)
    • {userId} - the sender's Feishu open_id (e.g., ou_xxxxxx)

    Session scope

    session.dmScope controls how direct messages are mapped to agent sessions. This is a global setting that affects all channels.

    Value Behavior Best for
    "main" Each user's DM maps to their agent's main session Single-user bots where you want USER.md / SOUL.md to auto-load
    "per-channel-peer" Each (channel + user) combination gets a separate session Public multi-user bots needing stronger isolation

    Tradeoff: Using "main" enables automatic bootstrap file loading (USER.md, SOUL.md, MEMORY.md), but means all DMs across all channels share the same session key pattern. For public multi-user bots where isolation matters more than bootstrap auto-loading, consider "per-channel-peer" and manage bootstrap files manually.

    json5
    {  session: {    // For single-user personal bots: enables auto bootstrap loading    dmScope: "main",     // For public multi-user bots: stronger isolation    // dmScope: "per-channel-peer",  },}

    Typical multi-user deployment

    json5
    {  channels: {    feishu: {      appId: "cli_xxx",      appSecret: "xxx",      dmPolicy: "open",      allowFrom: ["*"],      groupPolicy: "open",      requireMention: true,      dynamicAgentCreation: {        enabled: true,        workspaceTemplate: "~/.openclaw/workspace-{agentId}",        agentDirTemplate: "~/.openclaw/agents/{agentId}/agent",      },    },  },  session: {    // Choose dmScope based on your isolation needs:    // "main" for bootstrap auto-loading, "per-channel-peer" for stronger isolation    dmScope: "main",  },  bindings: [], // Empty - dynamic agents auto-bind}

    Verification

    Check gateway logs to confirm dynamic creation is working:

    Code
    feishu: creating dynamic agent "feishu-ou_xxxxxx" for user ou_xxxxxxworkspace: /Users/you/.openclaw/workspace-feishu-ou_xxxxxxfeishu: dynamic agent created, new route: agent:feishu-ou_xxxxxx:main

    List all created workspaces:

    bash
    ls -la ~/.openclaw/workspace-*

    Notes

    • Workspace isolation: Each user gets their own workspace directory and agent instance. Users cannot see each other's conversation history or files within the normal messaging flow.
    • Security boundary: This is a messaging-context isolation mechanism, not a hostile co-tenant security boundary. The agent process and host environment are shared.
    • bindings should be empty: Dynamic agents auto-register their own bindings
    • Upgrade path: Existing manual bindings continue to work alongside dynamic agents
    • session.dmScope is global: This affects all channels, not just Feishu

    Configuration reference

    Full configuration: Gateway configuration

    Setting Description Default
    channels.feishu.enabled Enable/disable the channel true
    channels.feishu.domain API domain (feishu or lark) feishu
    channels.feishu.connectionMode Event transport (websocket or webhook) websocket
    channels.feishu.defaultAccount Default account for outbound routing default
    channels.feishu.verificationToken Required for webhook mode -
    channels.feishu.encryptKey Required for webhook mode -
    channels.feishu.webhookPath Webhook route path /feishu/events
    channels.feishu.webhookHost Webhook bind host 127.0.0.1
    channels.feishu.webhookPort Webhook bind port 3000
    channels.feishu.accounts.<id>.appId App ID -
    channels.feishu.accounts.<id>.appSecret App Secret -
    channels.feishu.accounts.<id>.domain Per-account domain override feishu
    channels.feishu.accounts.<id>.tts Per-account TTS override messages.tts
    channels.feishu.dmPolicy DM policy allowlist
    channels.feishu.allowFrom DM allowlist (open_id list) [BotOwnerId]
    channels.feishu.groupPolicy Group policy allowlist
    channels.feishu.groupAllowFrom Group allowlist -
    channels.feishu.requireMention Require @mention in groups true
    channels.feishu.groups.<chat_id>.requireMention Per-group @mention override; explicit IDs also admit the group in allowlist mode inherited
    channels.feishu.groups.<chat_id>.enabled Enable/disable a specific group true
    channels.feishu.dynamicAgentCreation.enabled Enable automatic per-user agent creation false
    channels.feishu.dynamicAgentCreation.workspaceTemplate Path template for dynamic agent workspaces ~/.openclaw/workspace-{agentId}
    channels.feishu.dynamicAgentCreation.agentDirTemplate Agent directory name template ~/.openclaw/agents/{agentId}/agent
    channels.feishu.dynamicAgentCreation.maxAgents Maximum number of dynamic agents to create unlimited
    channels.feishu.textChunkLimit Message chunk size 2000
    channels.feishu.mediaMaxMb Media size limit 30
    channels.feishu.streaming Streaming card output true
    channels.feishu.blockStreaming Completed-block reply streaming false
    channels.feishu.typingIndicator Send typing reactions true
    channels.feishu.resolveSenderNames Resolve sender display names true
    channels.feishu.tools.bitable Enable Bitable/Base tools true
    channels.feishu.tools.base Alias for channels.feishu.tools.bitable; explicit bitable wins when both set true
    channels.feishu.accounts.<id>.tools.bitable Per-account Bitable/Base tool gate inherited
    channels.feishu.accounts.<id>.tools.base Per-account alias for tools.bitable inherited

    Supported message types

    Receive

    • ✅ Text
    • ✅ Rich text (post)
    • ✅ Images
    • ✅ Files
    • ✅ Audio
    • ✅ Video/media
    • ✅ Stickers

    Inbound Feishu/Lark audio messages are normalized as media placeholders instead of raw file_key JSON. When tools.media.audio is configured, OpenClaw downloads the voice-note resource and runs shared audio transcription before the agent turn, so the agent receives the spoken transcript. If Feishu includes transcript text directly in the audio payload, that text is used without another ASR call. Without an audio transcription provider, the agent still receives a <media:audio> placeholder plus the saved attachment, not the raw Feishu resource payload.

    Send

    • ✅ Text
    • ✅ Images
    • ✅ Files
    • ✅ Audio
    • ✅ Video/media
    • ✅ Interactive cards (including streaming updates)
    • ⚠️ Rich text (post-style formatting; doesn't support full Feishu/Lark authoring capabilities)

    Native Feishu/Lark audio bubbles use the Feishu audio message type and require Ogg/Opus upload media (file_type: "opus"). Existing .opus and .ogg media is sent directly as native audio. MP3/WAV/M4A and other likely audio formats are transcoded to 48kHz Ogg/Opus with ffmpeg only when the reply requests voice delivery (audioAsVoice / message tool asVoice, including TTS voice-note replies). Ordinary MP3 attachments stay regular files. If ffmpeg is missing or conversion fails, OpenClaw falls back to a file attachment and logs the reason.

    Threads and replies

    • ✅ Inline replies
    • ✅ Thread replies
    • ✅ Media replies stay thread-aware when replying to a thread message

    For groupSessionScope: "group_topic" and "group_topic_sender", native Feishu/Lark topic groups use the event thread_id (omt_*) as the canonical topic session key. If a native topic starter event omits thread_id, OpenClaw hydrates it from Feishu before routing the turn. Normal group replies that OpenClaw turns into threads keep using the reply root message ID (om_*) so the first turn and follow-up turn stay in the same session.


    Was this useful?