Back to skills
SkillHub ClubShip Full StackFull StackBackend

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.

Stars
3,131
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
B77.6

Install command

npx @skill-hub/cli install openclaw-skills-feishu-thread-forward

Repository

openclaw/skills

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 repository

Best 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

Claude CodeCodex CLIGemini CLIOpenCode

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": []
}

```

feishu-thread-forward | SkillHub