Skip to main content

Feishu / Lark Setup

Hermes Agent integrates with Feishu and Lark as a full-featured bot. Once connected, you can chat with the agent in direct messages or group chats, receive cron job results in a home chat, and send text, images, audio, and file attachments through the normal gateway flow.

The integration supports both connection modes:

  • websocket — recommended; Hermes opens the outbound connection and you do not need a public webhook endpoint
  • webhook — useful when you want Feishu/Lark to push events into your gateway over HTTP

How Hermes Behaves

ContextBehavior
Direct messagesHermes responds to every message.
Group chatsHermes responds only when the bot is @mentioned in the chat.
Shared group chatsBy default, session history is isolated per user inside a shared chat.

This shared-chat behavior is controlled by config.yaml:

group_sessions_per_user: true

Set it to false only if you explicitly want one shared conversation per chat.

Step 1: Create a Feishu / Lark App

  1. Open the Feishu or Lark developer console:
  2. Create a new app.
  3. In Credentials & Basic Info, copy the App ID and App Secret.
  4. Enable the Bot capability for the app.
warning

Keep the App Secret private. Anyone with it can impersonate your app.

Step 2: Choose a Connection Mode

Use WebSocket mode when Hermes runs on your laptop, workstation, or a private server. No public URL is required. The official Lark SDK opens and maintains a persistent outbound WebSocket connection with automatic reconnection.

FEISHU_CONNECTION_MODE=websocket

Requirements: The websockets Python package must be installed. The SDK handles connection lifecycle, heartbeats, and auto-reconnection internally.

How it works: The adapter runs the Lark SDK's WebSocket client in a background executor thread. Inbound events (messages, reactions, card actions) are dispatched to the main asyncio loop. On disconnect, the SDK will attempt to reconnect automatically.

Optional: Webhook mode

Use webhook mode only when you already run Hermes behind a reachable HTTP endpoint.

FEISHU_CONNECTION_MODE=webhook

In webhook mode, Hermes starts an HTTP server (via aiohttp) and serves a Feishu endpoint at:

/feishu/webhook

Requirements: The aiohttp Python package must be installed.

You can customize the webhook server bind address and path:

FEISHU_WEBHOOK_HOST=127.0.0.1   # default: 127.0.0.1
FEISHU_WEBHOOK_PORT=8765 # default: 8765
FEISHU_WEBHOOK_PATH=/feishu/webhook # default: /feishu/webhook

When Feishu sends a URL verification challenge (type: url_verification), the webhook responds automatically so you can complete the subscription setup in the Feishu developer console.

Step 3: Configure Hermes

Option A: Interactive Setup

hermes gateway setup

Select Feishu / Lark and fill in the prompts.

Option B: Manual Configuration

Add the following to ~/.hermes/.env:

FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=secret_xxx
FEISHU_DOMAIN=feishu
FEISHU_CONNECTION_MODE=websocket

# Optional but strongly recommended
FEISHU_ALLOWED_USERS=ou_xxx,ou_yyy
FEISHU_HOME_CHANNEL=oc_xxx

FEISHU_DOMAIN accepts:

  • feishu for Feishu China
  • lark for Lark international

Step 4: Start the Gateway

hermes gateway

Then message the bot from Feishu/Lark to confirm that the connection is live.

Home Chat

Use /set-home in a Feishu/Lark chat to mark it as the home channel for cron job results and cross-platform notifications.

You can also preconfigure it:

FEISHU_HOME_CHANNEL=oc_xxx

Security

User Allowlist

For production use, set an allowlist of Feishu Open IDs:

FEISHU_ALLOWED_USERS=ou_xxx,ou_yyy

If you leave the allowlist empty, anyone who can reach the bot may be able to use it. In group chats, the allowlist is checked against the sender's open_id before the message is processed.

Webhook Encryption Key

When running in webhook mode, set an encryption key to enable signature verification of inbound webhook payloads:

FEISHU_ENCRYPT_KEY=your-encrypt-key

This key is found in the Event Subscriptions section of your Feishu app configuration. When set, the adapter verifies every webhook request using the signature algorithm:

SHA256(timestamp + nonce + encrypt_key + body)

The computed hash is compared against the x-lark-signature header using timing-safe comparison. Requests with invalid or missing signatures are rejected with HTTP 401.

tip

In WebSocket mode, signature verification is handled by the SDK itself, so FEISHU_ENCRYPT_KEY is optional. In webhook mode, it is strongly recommended for production.

Verification Token

An additional layer of authentication that checks the token field inside webhook payloads:

FEISHU_VERIFICATION_TOKEN=your-verification-token

This token is also found in the Event Subscriptions section of your Feishu app. When set, every inbound webhook payload must contain a matching token in its header object. Mismatched tokens are rejected with HTTP 401.

Both FEISHU_ENCRYPT_KEY and FEISHU_VERIFICATION_TOKEN can be used together for defense in depth.

Group Message Policy

The FEISHU_GROUP_POLICY environment variable controls whether and how Hermes responds in group chats:

FEISHU_GROUP_POLICY=allowlist   # default
ValueBehavior
openHermes responds to @mentions from any user in any group.
allowlistHermes only responds to @mentions from users listed in FEISHU_ALLOWED_USERS.
disabledHermes ignores all group messages entirely.

In all modes, the bot must be explicitly @mentioned (or @all) in the group before the message is processed. Direct messages bypass this gate.

Bot Identity for @Mention Gating

For precise @mention detection in groups, the adapter needs to know the bot's identity. It can be provided explicitly:

FEISHU_BOT_OPEN_ID=ou_xxx
FEISHU_BOT_USER_ID=xxx
FEISHU_BOT_NAME=MyBot

If none of these are set, the adapter will attempt to auto-discover the bot name via the Application Info API on startup. For this to work, grant the admin:app.info:readonly or application:application:self_manage permission scope.

Interactive Card Actions

When users click buttons or interact with interactive cards sent by the bot, the adapter routes these as synthetic /card command events:

  • Button clicks become: /card button {"key": "value", ...}
  • The action's value payload from the card definition is included as JSON.
  • Card actions are deduplicated with a 15-minute window to prevent double processing.

Card action events are dispatched with MessageType.COMMAND, so they flow through the normal command processing pipeline.

To use this feature, enable the Interactive Card event in your Feishu app's event subscriptions (card.action.trigger).

Media Support

Inbound (receiving)

The adapter receives and caches the following media types from users:

TypeExtensionsHow it's processed
Images.jpg, .jpeg, .png, .gif, .webp, .bmpDownloaded via Feishu API and cached locally
Audio.ogg, .mp3, .wav, .m4a, .aac, .flac, .opus, .webmDownloaded and cached; small text files are auto-extracted
Video.mp4, .mov, .avi, .mkv, .webm, .m4v, .3gpDownloaded and cached as documents
Files.pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, and moreDownloaded and cached as documents

Media from rich-text (post) messages, including inline images and file attachments, is also extracted and cached.

For small text-based documents (.txt, .md), the file content is automatically injected into the message text so the agent can read it directly without needing tools.

Outbound (sending)

MethodWhat it sends
sendText or rich post messages (auto-detected based on markdown content)
send_image / send_image_fileUploads image to Feishu, then sends as native image bubble (with optional caption)
send_documentUploads file to Feishu API, then sends as file attachment
send_voiceUploads audio file as a Feishu file attachment
send_videoUploads video and sends as native media message
send_animationGIFs are downgraded to file attachments (Feishu has no native GIF bubble)

File upload routing is automatic based on extension:

  • .ogg, .opus → uploaded as opus audio
  • .mp4, .mov, .avi, .m4v → uploaded as mp4 media
  • .pdf, .doc(x), .xls(x), .ppt(x) → uploaded with their document type
  • Everything else → uploaded as a generic stream file

Markdown Rendering and Post Fallback

When outbound text contains markdown formatting (headings, bold, lists, code blocks, links, etc.), the adapter automatically sends it as a Feishu post message with an embedded md tag rather than as plain text. This enables rich rendering in the Feishu client.

If the Feishu API rejects the post payload (e.g., due to unsupported markdown constructs), the adapter automatically falls back to sending as plain text with markdown stripped. This two-stage fallback ensures messages are always delivered.

Plain text messages (no markdown detected) are sent as the simple text message type.

ACK Emoji Reactions

When the adapter receives an inbound message, it immediately adds an ✅ (OK) emoji reaction to signal that the message was received and is being processed. This provides visual feedback before the agent completes its response.

The reaction is persistent — it remains on the message after the response is sent, serving as a receipt marker.

User reactions on bot messages are also tracked. If a user adds or removes an emoji reaction on a message sent by the bot, it is routed as a synthetic text event (reaction:added:EMOJI_TYPE or reaction:removed:EMOJI_TYPE) so the agent can respond to feedback.

Burst Protection and Batching

The adapter includes debouncing for rapid message bursts to avoid overwhelming the agent:

Text Batching

When a user sends multiple text messages in quick succession, they are merged into a single event before being dispatched:

SettingEnv VarDefault
Quiet periodHERMES_FEISHU_TEXT_BATCH_DELAY_SECONDS0.6s
Max messages per batchHERMES_FEISHU_TEXT_BATCH_MAX_MESSAGES8
Max characters per batchHERMES_FEISHU_TEXT_BATCH_MAX_CHARS4000

Media Batching

Multiple media attachments sent in quick succession (e.g., dragging several images) are merged into a single event:

SettingEnv VarDefault
Quiet periodHERMES_FEISHU_MEDIA_BATCH_DELAY_SECONDS0.8s

Per-Chat Serialization

Messages within the same chat are processed serially (one at a time) to maintain conversation coherence. Each chat has its own lock, so messages in different chats are processed concurrently.

Rate Limiting (Webhook Mode)

In webhook mode, the adapter enforces per-IP rate limiting to protect against abuse:

  • Window: 60-second sliding window
  • Limit: 120 requests per window per (app_id, path, IP) triple
  • Tracking cap: Up to 4096 unique keys tracked (prevents unbounded memory growth)

Requests that exceed the limit receive HTTP 429 (Too Many Requests).

Webhook Anomaly Tracking

The adapter tracks consecutive error responses per IP address. After 25 consecutive errors from the same IP within a 6-hour window, a warning is logged. This helps detect misconfigured clients or probing attempts.

Additional webhook protections:

  • Body size limit: 1 MB maximum
  • Body read timeout: 30 seconds
  • Content-Type enforcement: Only application/json is accepted

Deduplication

Inbound messages are deduplicated using message IDs with a 24-hour TTL. The dedup state is persisted across restarts to ~/.hermes/feishu_seen_message_ids.json.

SettingEnv VarDefault
Cache sizeHERMES_FEISHU_DEDUP_CACHE_SIZE2048 entries

All Environment Variables

VariableRequiredDefaultDescription
FEISHU_APP_IDFeishu/Lark App ID
FEISHU_APP_SECRETFeishu/Lark App Secret
FEISHU_DOMAINfeishufeishu (China) or lark (international)
FEISHU_CONNECTION_MODEwebsocketwebsocket or webhook
FEISHU_ALLOWED_USERS(empty)Comma-separated open_id list for user allowlist
FEISHU_HOME_CHANNELChat ID for cron/notification output
FEISHU_ENCRYPT_KEY(empty)Encrypt key for webhook signature verification
FEISHU_VERIFICATION_TOKEN(empty)Verification token for webhook payload auth
FEISHU_GROUP_POLICYallowlistGroup message policy: open, allowlist, disabled
FEISHU_BOT_OPEN_ID(empty)Bot's open_id (for @mention detection)
FEISHU_BOT_USER_ID(empty)Bot's user_id (for @mention detection)
FEISHU_BOT_NAME(empty)Bot's display name (for @mention detection)
FEISHU_WEBHOOK_HOST127.0.0.1Webhook server bind address
FEISHU_WEBHOOK_PORT8765Webhook server port
FEISHU_WEBHOOK_PATH/feishu/webhookWebhook endpoint path
HERMES_FEISHU_DEDUP_CACHE_SIZE2048Max deduplicated message IDs to track
HERMES_FEISHU_TEXT_BATCH_DELAY_SECONDS0.6Text burst debounce quiet period
HERMES_FEISHU_TEXT_BATCH_MAX_MESSAGES8Max messages merged per text batch
HERMES_FEISHU_TEXT_BATCH_MAX_CHARS4000Max characters merged per text batch
HERMES_FEISHU_MEDIA_BATCH_DELAY_SECONDS0.8Media burst debounce quiet period

Troubleshooting

ProblemFix
lark-oapi not installedInstall the SDK: pip install lark-oapi
websockets not installed; websocket mode unavailableInstall websockets: pip install websockets
aiohttp not installed; webhook mode unavailableInstall aiohttp: pip install aiohttp
FEISHU_APP_ID or FEISHU_APP_SECRET not setSet both env vars or configure via hermes gateway setup
Another local Hermes gateway is already using this Feishu app_idOnly one Hermes instance can use the same app_id at a time. Stop the other gateway first.
Bot doesn't respond in groupsEnsure the bot is @mentioned, check FEISHU_GROUP_POLICY, and verify the sender is in FEISHU_ALLOWED_USERS if policy is allowlist
Webhook rejected: invalid verification tokenEnsure FEISHU_VERIFICATION_TOKEN matches the token in your Feishu app's Event Subscriptions config
Webhook rejected: invalid signatureEnsure FEISHU_ENCRYPT_KEY matches the encrypt key in your Feishu app config
Post messages show as plain textThe Feishu API rejected the post payload; this is normal fallback behavior. Check logs for details.
Images/files not received by botGrant im:message and im:resource permission scopes to your Feishu app
Bot identity not auto-detectedGrant admin:app.info:readonly scope, or set FEISHU_BOT_OPEN_ID / FEISHU_BOT_NAME manually
Webhook rate limit exceededMore than 120 requests/minute from the same IP. This is usually a misconfiguration or loop.

Toolset

Feishu / Lark uses the hermes-feishu platform preset, which includes the same core tools as Telegram and other gateway-based messaging platforms.