feishu-thread-forward
Forward Feishu thread/topic to a user, group, or topic via REST API. Activate when: forwarding a thread/topic to another chat, sharing a topic post to a different group, or any scenario involving Feishu thread-level forwarding (转发话题). The built-in feishu message tool does NOT support thread forwarding — this skill fills that gap.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-feishu-thread-forward
Repository
Skill path: skills/deadblue22/feishu-thread-forward
Forward Feishu thread/topic to a user, group, or topic via REST API. Activate when: forwarding a thread/topic to another chat, sharing a topic post to a different group, or any scenario involving Feishu thread-level forwarding (转发话题). The built-in feishu message tool does NOT support thread forwarding — this skill fills that gap.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Backend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install feishu-thread-forward into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding feishu-thread-forward to shared team environments
- Use feishu-thread-forward for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: feishu-thread-forward
description: |
Forward Feishu thread/topic to a user, group, or topic via REST API.
Activate when: forwarding a thread/topic to another chat, sharing a topic post to a different group,
or any scenario involving Feishu thread-level forwarding (转发话题).
The built-in feishu message tool does NOT support thread forwarding — this skill fills that gap.
---
# Feishu Thread Forward
Forward a Feishu thread (话题) to a user, group, or another topic using the Feishu Open API.
## When to Use
- User asks to forward/share a thread or topic to another group or user
- You need to cross-post a topic post from one group to another
- The built-in `message` tool cannot do thread-level forwarding
## API
```
POST https://open.feishu.cn/open-apis/im/v1/threads/{thread_id}/forward?receive_id_type={type}
```
### Parameters
| Param | Location | Required | Description |
|-------|----------|----------|-------------|
| `thread_id` | path | yes | Thread ID to forward (format: `omt_xxxxx`) |
| `receive_id_type` | query | yes | Target ID type: `open_id`, `chat_id`, `user_id`, `union_id`, `email`, `thread_id` |
| `receive_id` | body | yes | Target ID matching the `receive_id_type` |
| `uuid` | query | no | Idempotency key (max 50 chars, dedup within 1 hour) |
### Headers
```
Authorization: Bearer {tenant_access_token}
Content-Type: application/json
```
## How to Get `thread_id`
A message in a topic group has a `thread_id` field. Retrieve it via:
```
GET https://open.feishu.cn/open-apis/im/v1/messages/{message_id}
```
The response `data.items[0].thread_id` contains the thread ID (e.g., `omt_1accc5a75c0f9b93`).
## Script
Use `scripts/forward_thread.py` for the complete implementation.
```bash
python3 skills/feishu-thread-forward/scripts/forward_thread.py \
--thread-id omt_xxxxx \
--receive-id oc_xxxxx \
--receive-id-type chat_id
```
## Typical Flow
1. **Get `thread_id`** — from the message's metadata, or by calling GET message API
2. **Call forward API** — `POST /im/v1/threads/{thread_id}/forward`
3. **Result** — a `merge_forward` type message appears in the target chat as a clickable topic card
## Forward vs Merge Forward vs Message Forward
| Method | API | Result |
|--------|-----|--------|
| **Thread forward** (this skill) | `POST /threads/{thread_id}/forward` | Topic card (clickable, shows thread context) ✅ |
| Merge forward | `POST /messages/merge_forward` | "群聊会话记录" bundle (expandable message list) |
| Message forward | `POST /messages/{message_id}/forward` | Single message copied to target (loses thread context) |
**Thread forward** is what users see when they click "转发话题" in Feishu client.
## Prerequisites
- Bot must be in the source group (and can see the thread)
- Bot must be in the target group (or target user must be in bot's availability scope)
- Bot needs `im:message` or `im:message:send_as_bot` permission
## Credentials
Read from `/root/.openclaw/openclaw.json` → `channels.feishu.appId` / `channels.feishu.appSecret` to obtain `tenant_access_token`.
## Error Codes
| Code | Meaning |
|------|---------|
| 230002 | Bot not in target group |
| 230013 | Target user not in bot's availability scope |
| 230064 | Invalid thread_id |
| 230066 | Thread is in a secret group (no forwarding) |
| 230070 | Thread's group has anti-leak mode enabled |
| 230073 | Thread invisible to bot (joined after thread creation + history hidden) |
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### scripts/forward_thread.py
```python
#!/usr/bin/env python3
"""Forward a Feishu thread (话题) to a user or group.
Usage:
python3 forward_thread.py --thread-id omt_xxx --receive-id oc_xxx --receive-id-type chat_id
python3 forward_thread.py --message-id om_xxx --receive-id ou_xxx --receive-id-type open_id
If --thread-id is not provided but --message-id is, the script will first
fetch the message to extract its thread_id.
"""
import argparse
import json
import sys
import urllib.request
import urllib.error
def get_token(config_path="/root/.openclaw/openclaw.json"):
with open(config_path) as f:
cfg = json.load(f)
app_id = cfg["channels"]["feishu"]["appId"]
app_secret = cfg["channels"]["feishu"]["appSecret"]
req = urllib.request.Request(
"https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
data=json.dumps({"app_id": app_id, "app_secret": app_secret}).encode(),
headers={"Content-Type": "application/json"},
)
resp = json.loads(urllib.request.urlopen(req).read())
if resp.get("code", -1) != 0:
print(f"ERROR: Failed to get token: {resp}", file=sys.stderr)
sys.exit(1)
return resp["tenant_access_token"]
def get_thread_id_from_message(token, message_id):
req = urllib.request.Request(
f"https://open.feishu.cn/open-apis/im/v1/messages/{message_id}",
headers={"Authorization": f"Bearer {token}"},
)
resp = json.loads(urllib.request.urlopen(req).read())
if resp.get("code", -1) != 0:
print(f"ERROR: Failed to get message: {resp}", file=sys.stderr)
sys.exit(1)
items = resp.get("data", {}).get("items", [])
if not items:
print("ERROR: Message not found", file=sys.stderr)
sys.exit(1)
thread_id = items[0].get("thread_id")
if not thread_id:
print("ERROR: Message has no thread_id (not a topic message)", file=sys.stderr)
sys.exit(1)
return thread_id
def forward_thread(token, thread_id, receive_id, receive_id_type, uuid=None):
url = f"https://open.feishu.cn/open-apis/im/v1/threads/{thread_id}/forward?receive_id_type={receive_id_type}"
if uuid:
url += f"&uuid={uuid}"
req = urllib.request.Request(
url,
data=json.dumps({"receive_id": receive_id}).encode(),
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
},
)
try:
resp = json.loads(urllib.request.urlopen(req).read())
except urllib.error.HTTPError as e:
resp = json.loads(e.read().decode())
return resp
def main():
parser = argparse.ArgumentParser(description="Forward a Feishu thread")
parser.add_argument("--thread-id", help="Thread ID (omt_xxx)")
parser.add_argument("--message-id", help="Message ID to extract thread_id from")
parser.add_argument("--receive-id", required=True, help="Target ID")
parser.add_argument(
"--receive-id-type",
required=True,
choices=["open_id", "chat_id", "user_id", "union_id", "email", "thread_id"],
help="Target ID type",
)
parser.add_argument("--uuid", help="Idempotency key (max 50 chars)")
parser.add_argument("--config", default="/root/.openclaw/openclaw.json")
args = parser.parse_args()
if not args.thread_id and not args.message_id:
parser.error("Either --thread-id or --message-id is required")
token = get_token(args.config)
thread_id = args.thread_id
if not thread_id:
thread_id = get_thread_id_from_message(token, args.message_id)
print(f"Resolved thread_id: {thread_id}", file=sys.stderr)
result = forward_thread(
token, thread_id, args.receive_id, args.receive_id_type, args.uuid
)
print(json.dumps(result, indent=2, ensure_ascii=False))
if result.get("code", -1) != 0:
sys.exit(1)
if __name__ == "__main__":
main()
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "deadblue22",
"slug": "feishu-thread-forward",
"displayName": "Feishu Thread Forward",
"latest": {
"version": "1.0.0",
"publishedAt": 1772703647916,
"commit": "https://github.com/openclaw/skills/commit/70ab99c6fcdab363c6fbedd6d3267ec600cf899c"
},
"history": []
}
```