Feishu lark skill

Send messages and interactive cards to Feishu (飞书) and Lark channels via webhooks or Bot API.

by OpenClaudia·MIT license·★ 705 Stars on the repo·GitHub ↗

Use now

Files of Feishu lark

OpenClaudia/main1 file shown
SKILL.md
Show the full text972 lines

Feishu / Lark Messaging Skill

You are a messaging specialist for Feishu (飞书, ByteDance's Chinese workplace platform) and Lark (the international version). Your job is to send messages, interactive cards, and marketing content to Feishu/Lark group chats via Custom Bot Webhooks or the App Bot API.

Prerequisites

Check which credentials are available:

echo "FEISHU_WEBHOOK_URL is ${FEISHU_WEBHOOK_URL:+set}"
echo "FEISHU_WEBHOOK_SECRET is ${FEISHU_WEBHOOK_SECRET:+set}"
echo "FEISHU_APP_ID is ${FEISHU_APP_ID:+set}"
echo "FEISHU_APP_SECRET is ${FEISHU_APP_SECRET:+set}"
Two Integration Modes
Mode Credentials Required Capabilities
Custom Bot Webhook (simple) FEISHU_WEBHOOK_URL (+ optional FEISHU_WEBHOOK_SECRET) Send text, rich text, interactive cards to a single group
App Bot API (full featured) FEISHU_APP_ID + FEISHU_APP_SECRET Send to any chat, upload images, at-mention users, manage cards, receive events

If no credentials are set, instruct the user:

Custom Bot Webhook (quickest setup):

  1. Open a Feishu/Lark group chat
  2. Click the group name at the top to open Group Settings
  3. Go to Bots > Add Bot > Custom Bot
  4. Name the bot and optionally set a Signature Verification secret
  5. Copy the webhook URL and add to .env:
    FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/{webhook_id}
    FEISHU_WEBHOOK_SECRET=your_secret_here  # optional, for signed webhooks
    

App Bot API (for advanced use):

  1. Go to Feishu Open Platform or Lark Developer Console
  2. Create a new app, enable the Bot capability
  3. Add required permissions: im:message:send_as_bot, im:chat:readonly
  4. Publish and approve the app, then add to .env:
    FEISHU_APP_ID=cli_xxxxx
    FEISHU_APP_SECRET=xxxxx
    
Webhook URL Formats
  • Feishu (China): https://open.feishu.cn/open-apis/bot/v2/hook/{webhook_id}
  • Lark (International): https://open.larksuite.com/open-apis/bot/v2/hook/{webhook_id}
API Base URLs
  • Feishu (China): https://open.feishu.cn/open-apis
  • Lark (International): https://open.larksuite.com/open-apis

1. Custom Bot Webhook Messages

1.1 Plain Text Message
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "text",
    "content": {
      "text": "Hello from OpenClaudia! This is a test message."
    }
  }'

At-mention everyone in the group:

curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "text",
    "content": {
      "text": "<at user_id=\"all\">Everyone</at> Important announcement: new release is live!"
    }
  }'
1.2 Rich Text Message (Post)

Rich text supports bold, links, at-mentions, and images in a structured format.

curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "post",
    "content": {
      "post": {
        "zh_cn": {
          "title": "产品更新公告",
          "content": [
            [
              {"tag": "text", "text": "我们很高兴地宣布 "},
              {"tag": "a", "text": "v2.0 版本", "href": "https://example.com/changelog"},
              {"tag": "text", "text": " 已正式发布!"}
            ],
            [
              {"tag": "text", "text": "主要更新:"}
            ],
            [
              {"tag": "text", "text": "1. 全新用户界面\n2. 性能提升 50%\n3. 支持暗色模式"}
            ],
            [
              {"tag": "at", "user_id": "all", "user_name": "所有人"}
            ]
          ]
        }
      }
    }
  }'

English version (for Lark):

curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "post",
    "content": {
      "post": {
        "en_us": {
          "title": "Product Update Announcement",
          "content": [
            [
              {"tag": "text", "text": "We are excited to announce that "},
              {"tag": "a", "text": "v2.0", "href": "https://example.com/changelog"},
              {"tag": "text", "text": " is now live!"}
            ],
            [
              {"tag": "text", "text": "Key updates:"}
            ],
            [
              {"tag": "text", "text": "1. Brand new UI\n2. 50% performance improvement\n3. Dark mode support"}
            ],
            [
              {"tag": "at", "user_id": "all", "user_name": "Everyone"}
            ]
          ]
        }
      }
    }
  }'
Rich Text Tag Reference
Tag Purpose Attributes
text Plain text text, un_escape (boolean, interpret \n etc.)
a Hyperlink text, href
at At-mention user_id (use "all" for everyone), user_name
img Image (App Bot only) image_key (requires uploading image first)
media Video/file (App Bot only) file_key, image_key
1.3 Signed Webhook Requests

If FEISHU_WEBHOOK_SECRET is set, the webhook requires a signature for verification.

Generate a signed request:

# Calculate timestamp and signature
TIMESTAMP=$(date +%s)
STRING_TO_SIGN="${TIMESTAMP}\n${FEISHU_WEBHOOK_SECRET}"
SIGN=$(printf '%b' "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "" -binary | openssl base64)

# For proper HMAC-SHA256 signing:
SIGN=$(echo -ne "${TIMESTAMP}\n${FEISHU_WEBHOOK_SECRET}" | openssl dgst -sha256 -hmac "" -binary | base64)

curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d "{
    \"timestamp\": \"${TIMESTAMP}\",
    \"sign\": \"${SIGN}\",
    \"msg_type\": \"text\",
    \"content\": {
      \"text\": \"Signed message from OpenClaudia.\"
    }
  }"

Feishu signature algorithm details:

  1. Concatenate timestamp + "\n" + secret as the string to sign
  2. Compute HMAC-SHA256 with an empty key over that string
  3. Base64-encode the result
  4. Include both timestamp and sign in the request JSON body

2. Interactive Card Messages

Interactive cards are the most powerful message format. They support headers, content sections, images, action buttons, and structured layouts.

2.1 Basic Card Structure
{
  "msg_type": "interactive",
  "card": {
    "header": {
      "title": {
        "tag": "plain_text",
        "content": "Card Title Here"
      },
      "template": "blue"
    },
    "elements": []
  }
}
Header Color Templates
Template Color Best For
blue Blue General info, updates
green Green Success, positive news
red Red Urgent, alerts, errors
orange Orange Warnings, action needed
purple Purple Events, creative
indigo Indigo Technical, engineering
turquoise Teal Growth, marketing
yellow Yellow Highlights, tips
grey Grey Neutral, low priority
wathet Light blue Default, clean
2.2 Card Elements Reference

Markdown Content Block:

{
  "tag": "markdown",
  "content": "**Bold text** and *italic text*\n[Link text](https://example.com)\nList:\n- Item 1\n- Item 2"
}

Divider:

{
  "tag": "hr"
}

Note (small gray footer text):

{
  "tag": "note",
  "elements": [
    {"tag": "plain_text", "content": "Sent via OpenClaudia Marketing Toolkit"}
  ]
}

Image Block:

{
  "tag": "img",
  "img_key": "img_v2_xxx",
  "alt": {"tag": "plain_text", "content": "Image description"},
  "title": {"tag": "plain_text", "content": "Image Title"}
}

Action Buttons:

{
  "tag": "action",
  "actions": [
    {
      "tag": "button",
      "text": {"tag": "plain_text", "content": "View Details"},
      "type": "primary",
      "url": "https://example.com/details"
    },
    {
      "tag": "button",
      "text": {"tag": "plain_text", "content": "Dismiss"},
      "type": "default"
    }
  ]
}

Button types: primary (blue), danger (red), default (gray)

Multi-column Layout:

{
  "tag": "column_set",
  "flex_mode": "bisect",
  "columns": [
    {
      "tag": "column",
      "width": "weighted",
      "weight": 1,
      "elements": [
        {"tag": "markdown", "content": "**Left Column**\nContent here"}
      ]
    },
    {
      "tag": "column",
      "width": "weighted",
      "weight": 1,
      "elements": [
        {"tag": "markdown", "content": "**Right Column**\nContent here"}
      ]
    }
  ]
}
2.3 Full Card Example: Product Announcement
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "interactive",
    "card": {
      "header": {
        "title": {
          "tag": "plain_text",
          "content": "New Feature Launch: AI-Powered Analytics"
        },
        "template": "turquoise"
      },
      "elements": [
        {
          "tag": "markdown",
          "content": "We are thrilled to announce our latest feature!\n\n**AI-Powered Analytics** is now available to all Pro and Enterprise users.\n\nKey highlights:\n- **Smart Insights**: Automatic trend detection and anomaly alerts\n- **Natural Language Queries**: Ask questions in plain English\n- **Predictive Forecasting**: 90-day revenue and growth projections\n- **Custom Dashboards**: Drag-and-drop report builder"
        },
        {
          "tag": "hr"
        },
        {
          "tag": "markdown",
          "content": "**Availability:** Rolling out now, fully live by end of week\n**Documentation:** [View the guide](https://example.com/docs/analytics)\n**Feedback:** Reply in this thread or submit via [feedback form](https://example.com/feedback)"
        },
        {
          "tag": "action",
          "actions": [
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "Try It Now"},
              "type": "primary",
              "url": "https://example.com/analytics"
            },
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "Read Docs"},
              "type": "default",
              "url": "https://example.com/docs/analytics"
            }
          ]
        },
        {
          "tag": "note",
          "elements": [
            {"tag": "plain_text", "content": "Product Team | Released 2025-01-15"}
          ]
        }
      ]
    }
  }'

The App Bot API requires FEISHU_APP_ID and FEISHU_APP_SECRET. It provides full messaging capabilities including sending to any chat, uploading images, and managing messages.

3.1 Get Tenant Access Token

All App Bot API calls require a tenant_access_token. Tokens expire after 2 hours.

# For Feishu (China)
FEISHU_API_BASE="https://open.feishu.cn/open-apis"

# For Lark (International)
# FEISHU_API_BASE="https://open.larksuite.com/open-apis"

TENANT_TOKEN=$(curl -s -X POST "${FEISHU_API_BASE}/auth/v3/tenant_access_token/internal" \
  -H "Content-Type: application/json" \
  -d "{
    \"app_id\": \"${FEISHU_APP_ID}\",
    \"app_secret\": \"${FEISHU_APP_SECRET}\"
  }" | python3 -c "import json,sys; print(json.load(sys.stdin).get('tenant_access_token',''))")

echo "Token: ${TENANT_TOKEN:0:10}..."
3.2 List Chats the Bot Belongs To
curl -s "${FEISHU_API_BASE}/im/v1/chats?page_size=20" \
  -H "Authorization: Bearer ${TENANT_TOKEN}" | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
for chat in data.get('data', {}).get('items', []):
    print(f\"Chat ID: {chat['chat_id']}  |  Name: {chat.get('name', 'N/A')}  |  Type: {chat.get('chat_type', 'N/A')}\")
"
3.3 Send Message to a Chat
CHAT_ID="oc_xxxxx"  # Replace with actual chat_id

# Send a text message
curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=chat_id" \
  -H "Authorization: Bearer ${TENANT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"receive_id\": \"${CHAT_ID}\",
    \"msg_type\": \"text\",
    \"content\": \"{\\\"text\\\": \\\"Hello from the App Bot!\\\"}\"
  }"

Send a rich text message via the API:

curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=chat_id" \
  -H "Authorization: Bearer ${TENANT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"receive_id\": \"${CHAT_ID}\",
    \"msg_type\": \"post\",
    \"content\": $(python3 -c "
import json
content = {
    'zh_cn': {
        'title': 'App Bot 消息',
        'content': [
            [
                {'tag': 'text', 'text': '这是一条通过 App Bot API 发送的 '},
                {'tag': 'a', 'text': '富文本消息', 'href': 'https://example.com'},
                {'tag': 'text', 'text': '。'}
            ]
        ]
    }
}
print(json.dumps(json.dumps(content)))
")
  }"

Send an interactive card via the API:

curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=chat_id" \
  -H "Authorization: Bearer ${TENANT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"receive_id\": \"${CHAT_ID}\",
    \"msg_type\": \"interactive\",
    \"content\": $(python3 -c "
import json
card = {
    'header': {
        'title': {'tag': 'plain_text', 'content': 'Marketing Update'},
        'template': 'turquoise'
    },
    'elements': [
        {'tag': 'markdown', 'content': '**Campaign Performance This Week**\n\n- Impressions: **120,450** (+12%)\n- Clicks: **8,320** (+8%)\n- Conversions: **342** (+15%)\n- Cost per Conversion: **\$14.20** (-5%)'},
        {'tag': 'hr'},
        {'tag': 'action', 'actions': [
            {'tag': 'button', 'text': {'tag': 'plain_text', 'content': 'View Full Report'}, 'type': 'primary', 'url': 'https://example.com/report'}
        ]},
        {'tag': 'note', 'elements': [{'tag': 'plain_text', 'content': 'Auto-generated by OpenClaudia Marketing Toolkit'}]}
    ]
}
print(json.dumps(json.dumps(card)))
")
  }"
3.4 Upload an Image

Upload an image to get an image_key for use in cards and rich text messages.

IMAGE_KEY=$(curl -s -X POST "${FEISHU_API_BASE}/im/v1/images" \
  -H "Authorization: Bearer ${TENANT_TOKEN}" \
  -F "image_type=message" \
  -F "image=@/path/to/image.png" | python3 -c "import json,sys; print(json.load(sys.stdin).get('data',{}).get('image_key',''))")

echo "Image key: ${IMAGE_KEY}"
3.5 Send to a Specific User (by email or user_id)
# By email (receive_id_type=email)
curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=email" \
  -H "Authorization: Bearer ${TENANT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d "{
    \"receive_id\": \"[email protected]\",
    \"msg_type\": \"text\",
    \"content\": \"{\\\"text\\\": \\\"Direct message from the marketing bot.\\\"}\"
  }"

4. Message Templates

4.1 Product Announcement
send_product_announcement() {
  local TITLE="$1"
  local VERSION="$2"
  local FEATURES="$3"
  local DOCS_URL="$4"
  local CTA_URL="$5"

  curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
    -H "Content-Type: application/json" \
    -d "$(python3 -c "
import json
card = {
    'msg_type': 'interactive',
    'card': {
        'header': {
            'title': {'tag': 'plain_text', 'content': '${TITLE}'},
            'template': 'green'
        },
        'elements': [
            {'tag': 'markdown', 'content': '**Version ${VERSION}** is now available!\n\n${FEATURES}'},
            {'tag': 'hr'},
            {'tag': 'action', 'actions': [
                {'tag': 'button', 'text': {'tag': 'plain_text', 'content': 'Get Started'}, 'type': 'primary', 'url': '${CTA_URL}'},
                {'tag': 'button', 'text': {'tag': 'plain_text', 'content': 'Release Notes'}, 'type': 'default', 'url': '${DOCS_URL}'}
            ]},
            {'tag': 'note', 'elements': [{'tag': 'plain_text', 'content': 'Product Team | $(date +%Y-%m-%d)'}]}
        ]
    }
}
print(json.dumps(card))
")"
}
4.2 Team Update / Weekly Report
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "interactive",
    "card": {
      "header": {
        "title": {"tag": "plain_text", "content": "Weekly Marketing Report - W03 2025"},
        "template": "blue"
      },
      "elements": [
        {
          "tag": "column_set",
          "flex_mode": "bisect",
          "columns": [
            {
              "tag": "column",
              "width": "weighted",
              "weight": 1,
              "elements": [
                {"tag": "markdown", "content": "**Traffic**\n\nSessions: **45,230**\nUnique Visitors: **32,100**\nBounce Rate: **42%**"}
              ]
            },
            {
              "tag": "column",
              "width": "weighted",
              "weight": 1,
              "elements": [
                {"tag": "markdown", "content": "**Conversions**\n\nSignups: **580**\nTrials: **120**\nPaid: **34**"}
              ]
            }
          ]
        },
        {"tag": "hr"},
        {
          "tag": "markdown",
          "content": "**Top Performing Content:**\n1. \"10 Tips for Better SEO\" - 8,200 views\n2. \"Product Comparison Guide\" - 5,100 views\n3. \"Customer Success Story: Acme Corp\" - 3,800 views\n\n**Action Items:**\n- [ ] Publish Q1 campaign landing page\n- [ ] Review ad spend allocation\n- [ ] Schedule social media posts for next week"
        },
        {
          "tag": "action",
          "actions": [
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "Full Dashboard"},
              "type": "primary",
              "url": "https://example.com/dashboard"
            }
          ]
        },
        {
          "tag": "note",
          "elements": [
            {"tag": "plain_text", "content": "Marketing Team | Auto-generated weekly report"}
          ]
        }
      ]
    }
  }'
4.3 Event Notification
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "interactive",
    "card": {
      "header": {
        "title": {"tag": "plain_text", "content": "Upcoming Webinar: AI in Marketing"},
        "template": "purple"
      },
      "elements": [
        {
          "tag": "markdown",
          "content": "Join us for an exclusive webinar on leveraging AI for marketing success.\n\n**Date:** Thursday, January 30, 2025\n**Time:** 2:00 PM - 3:30 PM (PST)\n**Speaker:** Jane Smith, VP of Marketing\n**Format:** Live presentation + Q&A\n\n**What you will learn:**\n- How to use AI for content personalization\n- Automating campaign optimization\n- Measuring AI-driven marketing ROI"
        },
        {"tag": "hr"},
        {
          "tag": "action",
          "actions": [
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "Register Now"},
              "type": "primary",
              "url": "https://example.com/webinar/register"
            },
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "Add to Calendar"},
              "type": "default",
              "url": "https://example.com/webinar/calendar"
            }
          ]
        },
        {
          "tag": "note",
          "elements": [
            {"tag": "plain_text", "content": "Limited to 200 seats | Free for all team members"}
          ]
        }
      ]
    }
  }'
4.4 Marketing Campaign Alert
curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "interactive",
    "card": {
      "header": {
        "title": {"tag": "plain_text", "content": "Campaign Alert: Budget Threshold Reached"},
        "template": "orange"
      },
      "elements": [
        {
          "tag": "markdown",
          "content": "**Google Ads - Q1 Brand Campaign** has reached **80%** of its monthly budget.\n\n| Metric | Value |\n|--------|-------|\n| Budget | $10,000 |\n| Spent | $8,042 |\n| Remaining | $1,958 |\n| Days Left | 8 |\n| Projected Overspend | $2,100 |\n\n**Recommendation:** Reduce daily bid cap by 15% or pause low-performing ad groups."
        },
        {
          "tag": "action",
          "actions": [
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "Adjust Budget"},
              "type": "danger",
              "url": "https://ads.google.com/campaigns"
            },
            {
              "tag": "button",
              "text": {"tag": "plain_text", "content": "View Campaign"},
              "type": "default",
              "url": "https://example.com/campaigns/q1-brand"
            }
          ]
        }
      ]
    }
  }'

5. Helper: Build and Send Cards Programmatically

For complex or dynamic cards, use Python to construct the JSON payload:

python3 -c "
import json, subprocess, os

webhook_url = os.environ.get('FEISHU_WEBHOOK_URL', '')
if not webhook_url:
    print('Error: FEISHU_WEBHOOK_URL not set')
    exit(1)

# Build card dynamically
card = {
    'msg_type': 'interactive',
    'card': {
        'header': {
            'title': {'tag': 'plain_text', 'content': 'Dynamic Card Title'},
            'template': 'blue'
        },
        'elements': []
    }
}

# Add content blocks
card['card']['elements'].append({
    'tag': 'markdown',
    'content': 'This card was built programmatically.\n\n**Key metrics:**\n- Users: 10,000\n- Revenue: \$50,000'
})

# Add a divider
card['card']['elements'].append({'tag': 'hr'})

# Add buttons
card['card']['elements'].append({
    'tag': 'action',
    'actions': [
        {
            'tag': 'button',
            'text': {'tag': 'plain_text', 'content': 'Learn More'},
            'type': 'primary',
            'url': 'https://example.com'
        }
    ]
})

# Add footer
card['card']['elements'].append({
    'tag': 'note',
    'elements': [{'tag': 'plain_text', 'content': 'Sent via OpenClaudia'}]
})

payload = json.dumps(card)
result = subprocess.run(
    ['curl', '-s', '-X', 'POST', webhook_url,
     '-H', 'Content-Type: application/json',
     '-d', payload],
    capture_output=True, text=True
)
print(result.stdout)
"

6. Bilingual Support (Chinese + English)

When sending messages that need both Chinese and English content, use the rich text post format which supports multiple locales. Feishu will display the locale matching the user's language setting.

curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
  -H "Content-Type: application/json" \
  -d '{
    "msg_type": "post",
    "content": {
      "post": {
        "zh_cn": {
          "title": "重要通知:系统维护",
          "content": [
            [
              {"tag": "text", "text": "我们将于 "},
              {"tag": "text", "text": "1月25日 22:00-02:00 (北京时间)", "un_escape": true},
              {"tag": "text", "text": " 进行系统维护。"}
            ],
            [
              {"tag": "text", "text": "维护期间服务将暂时不可用。如有问题请联系 "},
              {"tag": "a", "text": "技术支持", "href": "https://example.com/support"},
              {"tag": "text", "text": "。"}
            ]
          ]
        },
        "en_us": {
          "title": "Important: Scheduled Maintenance",
          "content": [
            [
              {"tag": "text", "text": "We will perform scheduled maintenance on "},
              {"tag": "text", "text": "January 25, 10:00 PM - 2:00 AM (CST)"},
              {"tag": "text", "text": "."}
            ],
            [
              {"tag": "text", "text": "Services will be temporarily unavailable. For questions, contact "},
              {"tag": "a", "text": "Support", "href": "https://example.com/support"},
              {"tag": "text", "text": "."}
            ]
          ]
        }
      }
    }
  }'

7. Error Handling

Webhook Response Codes
Code StatusMessage Meaning
0 "success" Message sent successfully
9499 "Bad Request" Malformed JSON or missing required fields
19001 "param invalid" Invalid msg_type or content format
19002 "sign match fail" Signature verification failed (check timestamp and secret)
19021 "request too fast" Rate limit: max 100 messages per minute per webhook
19024 "bot not in chat" Bot has been removed from the group
Common Troubleshooting

Message not delivered:

  • Verify the webhook URL is correct and the bot is still in the group
  • Check that msg_type matches the content structure
  • For signed webhooks, ensure the timestamp is within 1 hour of current time

Card not rendering:

  • Validate JSON structure: header and elements are both required
  • Button URLs must start with http:// or https://
  • Markdown in cards supports a limited subset: bold, italic, links, lists, tables

API token errors:

  • Tenant access tokens expire after 2 hours; re-fetch before sending
  • Ensure the app has been published and approved in the developer console
  • Verify im:message:send_as_bot permission is granted
Rate Limits
Integration Limit
Custom Bot Webhook 100 messages/minute per webhook
App Bot API (messages) 50 messages/second per app
App Bot API (token refresh) 500 requests/hour

8. Workflow: Post Marketing Content to Feishu/Lark

When the user asks to send marketing content to Feishu or Lark, follow this workflow:

Step 1: Check Credentials

Verify that FEISHU_WEBHOOK_URL or FEISHU_APP_ID + FEISHU_APP_SECRET are set. If not, guide the user through setup.

Step 2: Determine Message Type
User Intent Recommended Format
Quick text update Plain text (msg_type: text)
Formatted announcement Rich text (msg_type: post)
Marketing report with metrics Interactive card with columns
Product launch Interactive card with buttons
Event notification Interactive card with CTA buttons
Alert or warning Interactive card with red/orange header
Step 3: Compose the Message
  • Use the appropriate template from section 4
  • Adapt content to the user's requirements
  • For bilingual groups, provide both zh_cn and en_us content
Step 4: Preview and Confirm

Show the user the full JSON payload before sending. Explain what the message will look like.

Never auto-send without explicit user confirmation.

Step 5: Send

Execute the curl command and report the response.

Step 6: Verify

Check the response code. If code: 0, the message was delivered. If there is an error, troubleshoot using the error table above.


9. Advanced: Message Card JSON Schema Quick Reference

{
  "msg_type": "interactive",
  "card": {
    "header": {                          // Required
      "title": {
        "tag": "plain_text",
        "content": "string"
      },
      "template": "blue|green|red|..."   // Header color
    },
    "elements": [                        // Required, array of blocks
      {"tag": "markdown", "content": "..."}, // Rich content
      {"tag": "hr"},                         // Divider line
      {"tag": "img", "img_key": "...", "alt": {...}}, // Image
      {                                      // Multi-column layout
        "tag": "column_set",
        "flex_mode": "bisect|trisect|...",
        "columns": [
          {"tag": "column", "width": "weighted", "weight": 1, "elements": [...]}
        ]
      },
      {                                      // Action buttons
        "tag": "action",
        "actions": [
          {"tag": "button", "text": {...}, "type": "primary|danger|default", "url": "..."}
        ]
      },
      {                                      // Footer note
        "tag": "note",
        "elements": [{"tag": "plain_text", "content": "..."}]
      }
    ]
  }
}

Tips

  • Start with webhooks. Custom Bot Webhooks require zero code infrastructure and can be set up in under a minute.
  • Use interactive cards for anything beyond simple text. They are more readable and actionable.
  • Include action buttons in every marketing card. Drive recipients to a landing page, dashboard, or sign-up form.
  • Leverage bilingual support if your team uses both Feishu and Lark, or has members in China and internationally.
  • Respect rate limits. For bulk messaging (e.g., sending to multiple groups), add a 1-second delay between requests.
  • Test in a private group first before sending to large team channels.
  • Keep card content concise. Cards have a maximum content size of approximately 30KB. For very long reports, link to an external page.
  • Use the Feishu Message Card Builder for visual card design: https://open.feishu.cn/tool/cardbuilder (Feishu) or https://open.larksuite.com/tool/cardbuilder (Lark).
1---
2name: feishu-lark
3description: >
4 Send messages and interactive cards to Feishu (飞书) and Lark channels via webhooks or Bot API.
5 Create rich-text announcements, marketing updates, and team notifications. Trigger phrases:
6 "post to feishu", "feishu message", "lark message", "feishu webhook", "lark webhook",
7 "send to feishu", "send to lark", "feishu bot", "lark bot", "飞书", "飞书机器人".
8allowed-tools:
9 - Bash
10 - WebFetch
11 - WebSearch
12---
13 
14# Feishu / Lark Messaging Skill
15 
16You are a messaging specialist for Feishu (飞书, ByteDance's Chinese workplace platform) and Lark (the international version). Your job is to send messages, interactive cards, and marketing content to Feishu/Lark group chats via Custom Bot Webhooks or the App Bot API.
17 
18## Prerequisites
19 
20Check which credentials are available:
21 
22```bash
23echo "FEISHU_WEBHOOK_URL is ${FEISHU_WEBHOOK_URL:+set}"
24echo "FEISHU_WEBHOOK_SECRET is ${FEISHU_WEBHOOK_SECRET:+set}"
25echo "FEISHU_APP_ID is ${FEISHU_APP_ID:+set}"
26echo "FEISHU_APP_SECRET is ${FEISHU_APP_SECRET:+set}"
27```
28 
29### Two Integration Modes
30 
31| Mode | Credentials Required | Capabilities |
32|------|---------------------|--------------|
33| **Custom Bot Webhook** (simple) | `FEISHU_WEBHOOK_URL` (+ optional `FEISHU_WEBHOOK_SECRET`) | Send text, rich text, interactive cards to a single group |
34| **App Bot API** (full featured) | `FEISHU_APP_ID` + `FEISHU_APP_SECRET` | Send to any chat, upload images, at-mention users, manage cards, receive events |
35 
36If no credentials are set, instruct the user:
37 
38> **Custom Bot Webhook (quickest setup):**
39> 1. Open a Feishu/Lark group chat
40> 2. Click the group name at the top to open Group Settings
41> 3. Go to **Bots** > **Add Bot** > **Custom Bot**
42> 4. Name the bot and optionally set a Signature Verification secret
43> 5. Copy the webhook URL and add to `.env`:
44> ```
45> FEISHU_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/{webhook_id}
46> FEISHU_WEBHOOK_SECRET=your_secret_here # optional, for signed webhooks
47> ```
48>
49> **App Bot API (for advanced use):**
50> 1. Go to [Feishu Open Platform](https://open.feishu.cn/app) or [Lark Developer Console](https://open.larksuite.com/app)
51> 2. Create a new app, enable the Bot capability
52> 3. Add required permissions: `im:message:send_as_bot`, `im:chat:readonly`
53> 4. Publish and approve the app, then add to `.env`:
54> ```
55> FEISHU_APP_ID=cli_xxxxx
56> FEISHU_APP_SECRET=xxxxx
57> ```
58 
59### Webhook URL Formats
60 
61- **Feishu (China):** `https://open.feishu.cn/open-apis/bot/v2/hook/{webhook_id}`
62- **Lark (International):** `https://open.larksuite.com/open-apis/bot/v2/hook/{webhook_id}`
63 
64### API Base URLs
65 
66- **Feishu (China):** `https://open.feishu.cn/open-apis`
67- **Lark (International):** `https://open.larksuite.com/open-apis`
68 
69---
70 
71## 1. Custom Bot Webhook Messages
72 
73### 1.1 Plain Text Message
74 
75```bash
76curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
77 -H "Content-Type: application/json" \
78 -d '{
79 "msg_type": "text",
80 "content": {
81 "text": "Hello from OpenClaudia! This is a test message."
82 }
83 }'
84```
85 
86**At-mention everyone in the group:**
87 
88```bash
89curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
90 -H "Content-Type: application/json" \
91 -d '{
92 "msg_type": "text",
93 "content": {
94 "text": "<at user_id=\"all\">Everyone</at> Important announcement: new release is live!"
95 }
96 }'
97```
98 
99### 1.2 Rich Text Message (Post)
100 
101Rich text supports bold, links, at-mentions, and images in a structured format.
102 
103```bash
104curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
105 -H "Content-Type: application/json" \
106 -d '{
107 "msg_type": "post",
108 "content": {
109 "post": {
110 "zh_cn": {
111 "title": "产品更新公告",
112 "content": [
113 [
114 {"tag": "text", "text": "我们很高兴地宣布 "},
115 {"tag": "a", "text": "v2.0 版本", "href": "https://example.com/changelog"},
116 {"tag": "text", "text": " 已正式发布!"}
117 ],
118 [
119 {"tag": "text", "text": "主要更新:"}
120 ],
121 [
122 {"tag": "text", "text": "1. 全新用户界面\n2. 性能提升 50%\n3. 支持暗色模式"}
123 ],
124 [
125 {"tag": "at", "user_id": "all", "user_name": "所有人"}
126 ]
127 ]
128 }
129 }
130 }
131 }'
132```
133 
134**English version (for Lark):**
135 
136```bash
137curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
138 -H "Content-Type: application/json" \
139 -d '{
140 "msg_type": "post",
141 "content": {
142 "post": {
143 "en_us": {
144 "title": "Product Update Announcement",
145 "content": [
146 [
147 {"tag": "text", "text": "We are excited to announce that "},
148 {"tag": "a", "text": "v2.0", "href": "https://example.com/changelog"},
149 {"tag": "text", "text": " is now live!"}
150 ],
151 [
152 {"tag": "text", "text": "Key updates:"}
153 ],
154 [
155 {"tag": "text", "text": "1. Brand new UI\n2. 50% performance improvement\n3. Dark mode support"}
156 ],
157 [
158 {"tag": "at", "user_id": "all", "user_name": "Everyone"}
159 ]
160 ]
161 }
162 }
163 }
164 }'
165```
166 
167### Rich Text Tag Reference
168 
169| Tag | Purpose | Attributes |
170|-----|---------|------------|
171| `text` | Plain text | `text`, `un_escape` (boolean, interpret `\n` etc.) |
172| `a` | Hyperlink | `text`, `href` |
173| `at` | At-mention | `user_id` (use `"all"` for everyone), `user_name` |
174| `img` | Image (App Bot only) | `image_key` (requires uploading image first) |
175| `media` | Video/file (App Bot only) | `file_key`, `image_key` |
176 
177### 1.3 Signed Webhook Requests
178 
179If `FEISHU_WEBHOOK_SECRET` is set, the webhook requires a signature for verification.
180 
181**Generate a signed request:**
182 
183```bash
184# Calculate timestamp and signature
185TIMESTAMP=$(date +%s)
186STRING_TO_SIGN="${TIMESTAMP}\n${FEISHU_WEBHOOK_SECRET}"
187SIGN=$(printf '%b' "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "" -binary | openssl base64)
188 
189# For proper HMAC-SHA256 signing:
190SIGN=$(echo -ne "${TIMESTAMP}\n${FEISHU_WEBHOOK_SECRET}" | openssl dgst -sha256 -hmac "" -binary | base64)
191 
192curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
193 -H "Content-Type: application/json" \
194 -d "{
195 \"timestamp\": \"${TIMESTAMP}\",
196 \"sign\": \"${SIGN}\",
197 \"msg_type\": \"text\",
198 \"content\": {
199 \"text\": \"Signed message from OpenClaudia.\"
200 }
201 }"
202```
203 
204**Feishu signature algorithm details:**
2051. Concatenate `timestamp + "\n" + secret` as the string to sign
2062. Compute HMAC-SHA256 with an empty key over that string
2073. Base64-encode the result
2084. Include both `timestamp` and `sign` in the request JSON body
209 
210---
211 
212## 2. Interactive Card Messages
213 
214Interactive cards are the most powerful message format. They support headers, content sections, images, action buttons, and structured layouts.
215 
216### 2.1 Basic Card Structure
217 
218```json
219{
220 "msg_type": "interactive",
221 "card": {
222 "header": {
223 "title": {
224 "tag": "plain_text",
225 "content": "Card Title Here"
226 },
227 "template": "blue"
228 },
229 "elements": []
230 }
231}
232```
233 
234### Header Color Templates
235 
236| Template | Color | Best For |
237|----------|-------|----------|
238| `blue` | Blue | General info, updates |
239| `green` | Green | Success, positive news |
240| `red` | Red | Urgent, alerts, errors |
241| `orange` | Orange | Warnings, action needed |
242| `purple` | Purple | Events, creative |
243| `indigo` | Indigo | Technical, engineering |
244| `turquoise` | Teal | Growth, marketing |
245| `yellow` | Yellow | Highlights, tips |
246| `grey` | Grey | Neutral, low priority |
247| `wathet` | Light blue | Default, clean |
248 
249### 2.2 Card Elements Reference
250 
251**Markdown Content Block:**
252 
253```json
254{
255 "tag": "markdown",
256 "content": "**Bold text** and *italic text*\n[Link text](https://example.com)\nList:\n- Item 1\n- Item 2"
257}
258```
259 
260**Divider:**
261 
262```json
263{
264 "tag": "hr"
265}
266```
267 
268**Note (small gray footer text):**
269 
270```json
271{
272 "tag": "note",
273 "elements": [
274 {"tag": "plain_text", "content": "Sent via OpenClaudia Marketing Toolkit"}
275 ]
276}
277```
278 
279**Image Block:**
280 
281```json
282{
283 "tag": "img",
284 "img_key": "img_v2_xxx",
285 "alt": {"tag": "plain_text", "content": "Image description"},
286 "title": {"tag": "plain_text", "content": "Image Title"}
287}
288```
289 
290**Action Buttons:**
291 
292```json
293{
294 "tag": "action",
295 "actions": [
296 {
297 "tag": "button",
298 "text": {"tag": "plain_text", "content": "View Details"},
299 "type": "primary",
300 "url": "https://example.com/details"
301 },
302 {
303 "tag": "button",
304 "text": {"tag": "plain_text", "content": "Dismiss"},
305 "type": "default"
306 }
307 ]
308}
309```
310 
311**Button types:** `primary` (blue), `danger` (red), `default` (gray)
312 
313**Multi-column Layout:**
314 
315```json
316{
317 "tag": "column_set",
318 "flex_mode": "bisect",
319 "columns": [
320 {
321 "tag": "column",
322 "width": "weighted",
323 "weight": 1,
324 "elements": [
325 {"tag": "markdown", "content": "**Left Column**\nContent here"}
326 ]
327 },
328 {
329 "tag": "column",
330 "width": "weighted",
331 "weight": 1,
332 "elements": [
333 {"tag": "markdown", "content": "**Right Column**\nContent here"}
334 ]
335 }
336 ]
337}
338```
339 
340### 2.3 Full Card Example: Product Announcement
341 
342```bash
343curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
344 -H "Content-Type: application/json" \
345 -d '{
346 "msg_type": "interactive",
347 "card": {
348 "header": {
349 "title": {
350 "tag": "plain_text",
351 "content": "New Feature Launch: AI-Powered Analytics"
352 },
353 "template": "turquoise"
354 },
355 "elements": [
356 {
357 "tag": "markdown",
358 "content": "We are thrilled to announce our latest feature!\n\n**AI-Powered Analytics** is now available to all Pro and Enterprise users.\n\nKey highlights:\n- **Smart Insights**: Automatic trend detection and anomaly alerts\n- **Natural Language Queries**: Ask questions in plain English\n- **Predictive Forecasting**: 90-day revenue and growth projections\n- **Custom Dashboards**: Drag-and-drop report builder"
359 },
360 {
361 "tag": "hr"
362 },
363 {
364 "tag": "markdown",
365 "content": "**Availability:** Rolling out now, fully live by end of week\n**Documentation:** [View the guide](https://example.com/docs/analytics)\n**Feedback:** Reply in this thread or submit via [feedback form](https://example.com/feedback)"
366 },
367 {
368 "tag": "action",
369 "actions": [
370 {
371 "tag": "button",
372 "text": {"tag": "plain_text", "content": "Try It Now"},
373 "type": "primary",
374 "url": "https://example.com/analytics"
375 },
376 {
377 "tag": "button",
378 "text": {"tag": "plain_text", "content": "Read Docs"},
379 "type": "default",
380 "url": "https://example.com/docs/analytics"
381 }
382 ]
383 },
384 {
385 "tag": "note",
386 "elements": [
387 {"tag": "plain_text", "content": "Product Team | Released 2025-01-15"}
388 ]
389 }
390 ]
391 }
392 }'
393```
394 
395---
396 
397## 3. App Bot API (Full Featured)
398 
399The App Bot API requires `FEISHU_APP_ID` and `FEISHU_APP_SECRET`. It provides full messaging capabilities including sending to any chat, uploading images, and managing messages.
400 
401### 3.1 Get Tenant Access Token
402 
403All App Bot API calls require a `tenant_access_token`. Tokens expire after 2 hours.
404 
405```bash
406# For Feishu (China)
407FEISHU_API_BASE="https://open.feishu.cn/open-apis"
408 
409# For Lark (International)
410# FEISHU_API_BASE="https://open.larksuite.com/open-apis"
411 
412TENANT_TOKEN=$(curl -s -X POST "${FEISHU_API_BASE}/auth/v3/tenant_access_token/internal" \
413 -H "Content-Type: application/json" \
414 -d "{
415 \"app_id\": \"${FEISHU_APP_ID}\",
416 \"app_secret\": \"${FEISHU_APP_SECRET}\"
417 }" | python3 -c "import json,sys; print(json.load(sys.stdin).get('tenant_access_token',''))")
418 
419echo "Token: ${TENANT_TOKEN:0:10}..."
420```
421 
422### 3.2 List Chats the Bot Belongs To
423 
424```bash
425curl -s "${FEISHU_API_BASE}/im/v1/chats?page_size=20" \
426 -H "Authorization: Bearer ${TENANT_TOKEN}" | \
427 python3 -c "
428import json, sys
429data = json.load(sys.stdin)
430for chat in data.get('data', {}).get('items', []):
431 print(f\"Chat ID: {chat['chat_id']} | Name: {chat.get('name', 'N/A')} | Type: {chat.get('chat_type', 'N/A')}\")
432"
433```
434 
435### 3.3 Send Message to a Chat
436 
437```bash
438CHAT_ID="oc_xxxxx" # Replace with actual chat_id
439 
440# Send a text message
441curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=chat_id" \
442 -H "Authorization: Bearer ${TENANT_TOKEN}" \
443 -H "Content-Type: application/json" \
444 -d "{
445 \"receive_id\": \"${CHAT_ID}\",
446 \"msg_type\": \"text\",
447 \"content\": \"{\\\"text\\\": \\\"Hello from the App Bot!\\\"}\"
448 }"
449```
450 
451**Send a rich text message via the API:**
452 
453```bash
454curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=chat_id" \
455 -H "Authorization: Bearer ${TENANT_TOKEN}" \
456 -H "Content-Type: application/json" \
457 -d "{
458 \"receive_id\": \"${CHAT_ID}\",
459 \"msg_type\": \"post\",
460 \"content\": $(python3 -c "
461import json
462content = {
463 'zh_cn': {
464 'title': 'App Bot 消息',
465 'content': [
466 [
467 {'tag': 'text', 'text': '这是一条通过 App Bot API 发送的 '},
468 {'tag': 'a', 'text': '富文本消息', 'href': 'https://example.com'},
469 {'tag': 'text', 'text': '。'}
470 ]
471 ]
472 }
473}
474print(json.dumps(json.dumps(content)))
475")
476 }"
477```
478 
479**Send an interactive card via the API:**
480 
481```bash
482curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=chat_id" \
483 -H "Authorization: Bearer ${TENANT_TOKEN}" \
484 -H "Content-Type: application/json" \
485 -d "{
486 \"receive_id\": \"${CHAT_ID}\",
487 \"msg_type\": \"interactive\",
488 \"content\": $(python3 -c "
489import json
490card = {
491 'header': {
492 'title': {'tag': 'plain_text', 'content': 'Marketing Update'},
493 'template': 'turquoise'
494 },
495 'elements': [
496 {'tag': 'markdown', 'content': '**Campaign Performance This Week**\n\n- Impressions: **120,450** (+12%)\n- Clicks: **8,320** (+8%)\n- Conversions: **342** (+15%)\n- Cost per Conversion: **\$14.20** (-5%)'},
497 {'tag': 'hr'},
498 {'tag': 'action', 'actions': [
499 {'tag': 'button', 'text': {'tag': 'plain_text', 'content': 'View Full Report'}, 'type': 'primary', 'url': 'https://example.com/report'}
500 ]},
501 {'tag': 'note', 'elements': [{'tag': 'plain_text', 'content': 'Auto-generated by OpenClaudia Marketing Toolkit'}]}
502 ]
503}
504print(json.dumps(json.dumps(card)))
505")
506 }"
507```
508 
509### 3.4 Upload an Image
510 
511Upload an image to get an `image_key` for use in cards and rich text messages.
512 
513```bash
514IMAGE_KEY=$(curl -s -X POST "${FEISHU_API_BASE}/im/v1/images" \
515 -H "Authorization: Bearer ${TENANT_TOKEN}" \
516 -F "image_type=message" \
517 -F "image=@/path/to/image.png" | python3 -c "import json,sys; print(json.load(sys.stdin).get('data',{}).get('image_key',''))")
518 
519echo "Image key: ${IMAGE_KEY}"
520```
521 
522### 3.5 Send to a Specific User (by email or user_id)
523 
524```bash
525# By email (receive_id_type=email)
526curl -s -X POST "${FEISHU_API_BASE}/im/v1/messages?receive_id_type=email" \
527 -H "Authorization: Bearer ${TENANT_TOKEN}" \
528 -H "Content-Type: application/json" \
529 -d "{
530 \"receive_id\": \"[email protected]\",
531 \"msg_type\": \"text\",
532 \"content\": \"{\\\"text\\\": \\\"Direct message from the marketing bot.\\\"}\"
533 }"
534```
535 
536---
537 
538## 4. Message Templates
539 
540### 4.1 Product Announcement
541 
542```bash
543send_product_announcement() {
544 local TITLE="$1"
545 local VERSION="$2"
546 local FEATURES="$3"
547 local DOCS_URL="$4"
548 local CTA_URL="$5"
549 
550 curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
551 -H "Content-Type: application/json" \
552 -d "$(python3 -c "
553import json
554card = {
555 'msg_type': 'interactive',
556 'card': {
557 'header': {
558 'title': {'tag': 'plain_text', 'content': '${TITLE}'},
559 'template': 'green'
560 },
561 'elements': [
562 {'tag': 'markdown', 'content': '**Version ${VERSION}** is now available!\n\n${FEATURES}'},
563 {'tag': 'hr'},
564 {'tag': 'action', 'actions': [
565 {'tag': 'button', 'text': {'tag': 'plain_text', 'content': 'Get Started'}, 'type': 'primary', 'url': '${CTA_URL}'},
566 {'tag': 'button', 'text': {'tag': 'plain_text', 'content': 'Release Notes'}, 'type': 'default', 'url': '${DOCS_URL}'}
567 ]},
568 {'tag': 'note', 'elements': [{'tag': 'plain_text', 'content': 'Product Team | $(date +%Y-%m-%d)'}]}
569 ]
570 }
571}
572print(json.dumps(card))
573")"
574}
575```
576 
577### 4.2 Team Update / Weekly Report
578 
579```bash
580curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
581 -H "Content-Type: application/json" \
582 -d '{
583 "msg_type": "interactive",
584 "card": {
585 "header": {
586 "title": {"tag": "plain_text", "content": "Weekly Marketing Report - W03 2025"},
587 "template": "blue"
588 },
589 "elements": [
590 {
591 "tag": "column_set",
592 "flex_mode": "bisect",
593 "columns": [
594 {
595 "tag": "column",
596 "width": "weighted",
597 "weight": 1,
598 "elements": [
599 {"tag": "markdown", "content": "**Traffic**\n\nSessions: **45,230**\nUnique Visitors: **32,100**\nBounce Rate: **42%**"}
600 ]
601 },
602 {
603 "tag": "column",
604 "width": "weighted",
605 "weight": 1,
606 "elements": [
607 {"tag": "markdown", "content": "**Conversions**\n\nSignups: **580**\nTrials: **120**\nPaid: **34**"}
608 ]
609 }
610 ]
611 },
612 {"tag": "hr"},
613 {
614 "tag": "markdown",
615 "content": "**Top Performing Content:**\n1. \"10 Tips for Better SEO\" - 8,200 views\n2. \"Product Comparison Guide\" - 5,100 views\n3. \"Customer Success Story: Acme Corp\" - 3,800 views\n\n**Action Items:**\n- [ ] Publish Q1 campaign landing page\n- [ ] Review ad spend allocation\n- [ ] Schedule social media posts for next week"
616 },
617 {
618 "tag": "action",
619 "actions": [
620 {
621 "tag": "button",
622 "text": {"tag": "plain_text", "content": "Full Dashboard"},
623 "type": "primary",
624 "url": "https://example.com/dashboard"
625 }
626 ]
627 },
628 {
629 "tag": "note",
630 "elements": [
631 {"tag": "plain_text", "content": "Marketing Team | Auto-generated weekly report"}
632 ]
633 }
634 ]
635 }
636 }'
637```
638 
639### 4.3 Event Notification
640 
641```bash
642curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
643 -H "Content-Type: application/json" \
644 -d '{
645 "msg_type": "interactive",
646 "card": {
647 "header": {
648 "title": {"tag": "plain_text", "content": "Upcoming Webinar: AI in Marketing"},
649 "template": "purple"
650 },
651 "elements": [
652 {
653 "tag": "markdown",
654 "content": "Join us for an exclusive webinar on leveraging AI for marketing success.\n\n**Date:** Thursday, January 30, 2025\n**Time:** 2:00 PM - 3:30 PM (PST)\n**Speaker:** Jane Smith, VP of Marketing\n**Format:** Live presentation + Q&A\n\n**What you will learn:**\n- How to use AI for content personalization\n- Automating campaign optimization\n- Measuring AI-driven marketing ROI"
655 },
656 {"tag": "hr"},
657 {
658 "tag": "action",
659 "actions": [
660 {
661 "tag": "button",
662 "text": {"tag": "plain_text", "content": "Register Now"},
663 "type": "primary",
664 "url": "https://example.com/webinar/register"
665 },
666 {
667 "tag": "button",
668 "text": {"tag": "plain_text", "content": "Add to Calendar"},
669 "type": "default",
670 "url": "https://example.com/webinar/calendar"
671 }
672 ]
673 },
674 {
675 "tag": "note",
676 "elements": [
677 {"tag": "plain_text", "content": "Limited to 200 seats | Free for all team members"}
678 ]
679 }
680 ]
681 }
682 }'
683```
684 
685### 4.4 Marketing Campaign Alert
686 
687```bash
688curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
689 -H "Content-Type: application/json" \
690 -d '{
691 "msg_type": "interactive",
692 "card": {
693 "header": {
694 "title": {"tag": "plain_text", "content": "Campaign Alert: Budget Threshold Reached"},
695 "template": "orange"
696 },
697 "elements": [
698 {
699 "tag": "markdown",
700 "content": "**Google Ads - Q1 Brand Campaign** has reached **80%** of its monthly budget.\n\n| Metric | Value |\n|--------|-------|\n| Budget | $10,000 |\n| Spent | $8,042 |\n| Remaining | $1,958 |\n| Days Left | 8 |\n| Projected Overspend | $2,100 |\n\n**Recommendation:** Reduce daily bid cap by 15% or pause low-performing ad groups."
701 },
702 {
703 "tag": "action",
704 "actions": [
705 {
706 "tag": "button",
707 "text": {"tag": "plain_text", "content": "Adjust Budget"},
708 "type": "danger",
709 "url": "https://ads.google.com/campaigns"
710 },
711 {
712 "tag": "button",
713 "text": {"tag": "plain_text", "content": "View Campaign"},
714 "type": "default",
715 "url": "https://example.com/campaigns/q1-brand"
716 }
717 ]
718 }
719 ]
720 }
721 }'
722```
723 
724---
725 
726## 5. Helper: Build and Send Cards Programmatically
727 
728For complex or dynamic cards, use Python to construct the JSON payload:
729 
730```bash
731python3 -c "
732import json, subprocess, os
733 
734webhook_url = os.environ.get('FEISHU_WEBHOOK_URL', '')
735if not webhook_url:
736 print('Error: FEISHU_WEBHOOK_URL not set')
737 exit(1)
738 
739# Build card dynamically
740card = {
741 'msg_type': 'interactive',
742 'card': {
743 'header': {
744 'title': {'tag': 'plain_text', 'content': 'Dynamic Card Title'},
745 'template': 'blue'
746 },
747 'elements': []
748 }
749}
750 
751# Add content blocks
752card['card']['elements'].append({
753 'tag': 'markdown',
754 'content': 'This card was built programmatically.\n\n**Key metrics:**\n- Users: 10,000\n- Revenue: \$50,000'
755})
756 
757# Add a divider
758card['card']['elements'].append({'tag': 'hr'})
759 
760# Add buttons
761card['card']['elements'].append({
762 'tag': 'action',
763 'actions': [
764 {
765 'tag': 'button',
766 'text': {'tag': 'plain_text', 'content': 'Learn More'},
767 'type': 'primary',
768 'url': 'https://example.com'
769 }
770 ]
771})
772 
773# Add footer
774card['card']['elements'].append({
775 'tag': 'note',
776 'elements': [{'tag': 'plain_text', 'content': 'Sent via OpenClaudia'}]
777})
778 
779payload = json.dumps(card)
780result = subprocess.run(
781 ['curl', '-s', '-X', 'POST', webhook_url,
782 '-H', 'Content-Type: application/json',
783 '-d', payload],
784 capture_output=True, text=True
785)
786print(result.stdout)
787"
788```
789 
790---
791 
792## 6. Bilingual Support (Chinese + English)
793 
794When sending messages that need both Chinese and English content, use the rich text `post` format which supports multiple locales. Feishu will display the locale matching the user's language setting.
795 
796```bash
797curl -s -X POST "${FEISHU_WEBHOOK_URL}" \
798 -H "Content-Type: application/json" \
799 -d '{
800 "msg_type": "post",
801 "content": {
802 "post": {
803 "zh_cn": {
804 "title": "重要通知:系统维护",
805 "content": [
806 [
807 {"tag": "text", "text": "我们将于 "},
808 {"tag": "text", "text": "1月25日 22:00-02:00 (北京时间)", "un_escape": true},
809 {"tag": "text", "text": " 进行系统维护。"}
810 ],
811 [
812 {"tag": "text", "text": "维护期间服务将暂时不可用。如有问题请联系 "},
813 {"tag": "a", "text": "技术支持", "href": "https://example.com/support"},
814 {"tag": "text", "text": "。"}
815 ]
816 ]
817 },
818 "en_us": {
819 "title": "Important: Scheduled Maintenance",
820 "content": [
821 [
822 {"tag": "text", "text": "We will perform scheduled maintenance on "},
823 {"tag": "text", "text": "January 25, 10:00 PM - 2:00 AM (CST)"},
824 {"tag": "text", "text": "."}
825 ],
826 [
827 {"tag": "text", "text": "Services will be temporarily unavailable. For questions, contact "},
828 {"tag": "a", "text": "Support", "href": "https://example.com/support"},
829 {"tag": "text", "text": "."}
830 ]
831 ]
832 }
833 }
834 }
835 }'
836```
837 
838---
839 
840## 7. Error Handling
841 
842### Webhook Response Codes
843 
844| Code | StatusMessage | Meaning |
845|------|---------------|---------|
846| 0 | `"success"` | Message sent successfully |
847| 9499 | `"Bad Request"` | Malformed JSON or missing required fields |
848| 19001 | `"param invalid"` | Invalid msg_type or content format |
849| 19002 | `"sign match fail"` | Signature verification failed (check timestamp and secret) |
850| 19021 | `"request too fast"` | Rate limit: max 100 messages per minute per webhook |
851| 19024 | `"bot not in chat"` | Bot has been removed from the group |
852 
853### Common Troubleshooting
854 
855**Message not delivered:**
856- Verify the webhook URL is correct and the bot is still in the group
857- Check that `msg_type` matches the content structure
858- For signed webhooks, ensure the timestamp is within 1 hour of current time
859 
860**Card not rendering:**
861- Validate JSON structure: header and elements are both required
862- Button URLs must start with `http://` or `https://`
863- Markdown in cards supports a limited subset: bold, italic, links, lists, tables
864 
865**API token errors:**
866- Tenant access tokens expire after 2 hours; re-fetch before sending
867- Ensure the app has been published and approved in the developer console
868- Verify `im:message:send_as_bot` permission is granted
869 
870### Rate Limits
871 
872| Integration | Limit |
873|-------------|-------|
874| Custom Bot Webhook | 100 messages/minute per webhook |
875| App Bot API (messages) | 50 messages/second per app |
876| App Bot API (token refresh) | 500 requests/hour |
877 
878---
879 
880## 8. Workflow: Post Marketing Content to Feishu/Lark
881 
882When the user asks to send marketing content to Feishu or Lark, follow this workflow:
883 
884### Step 1: Check Credentials
885 
886Verify that `FEISHU_WEBHOOK_URL` or `FEISHU_APP_ID` + `FEISHU_APP_SECRET` are set. If not, guide the user through setup.
887 
888### Step 2: Determine Message Type
889 
890| User Intent | Recommended Format |
891|-------------|-------------------|
892| Quick text update | Plain text (`msg_type: text`) |
893| Formatted announcement | Rich text (`msg_type: post`) |
894| Marketing report with metrics | Interactive card with columns |
895| Product launch | Interactive card with buttons |
896| Event notification | Interactive card with CTA buttons |
897| Alert or warning | Interactive card with `red`/`orange` header |
898 
899### Step 3: Compose the Message
900 
901- Use the appropriate template from section 4
902- Adapt content to the user's requirements
903- For bilingual groups, provide both `zh_cn` and `en_us` content
904 
905### Step 4: Preview and Confirm
906 
907Show the user the full JSON payload before sending. Explain what the message will look like.
908 
909**Never auto-send without explicit user confirmation.**
910 
911### Step 5: Send
912 
913Execute the curl command and report the response.
914 
915### Step 6: Verify
916 
917Check the response code. If `code: 0`, the message was delivered. If there is an error, troubleshoot using the error table above.
918 
919---
920 
921## 9. Advanced: Message Card JSON Schema Quick Reference
922 
923```
924{
925 "msg_type": "interactive",
926 "card": {
927 "header": { // Required
928 "title": {
929 "tag": "plain_text",
930 "content": "string"
931 },
932 "template": "blue|green|red|..." // Header color
933 },
934 "elements": [ // Required, array of blocks
935 {"tag": "markdown", "content": "..."}, // Rich content
936 {"tag": "hr"}, // Divider line
937 {"tag": "img", "img_key": "...", "alt": {...}}, // Image
938 { // Multi-column layout
939 "tag": "column_set",
940 "flex_mode": "bisect|trisect|...",
941 "columns": [
942 {"tag": "column", "width": "weighted", "weight": 1, "elements": [...]}
943 ]
944 },
945 { // Action buttons
946 "tag": "action",
947 "actions": [
948 {"tag": "button", "text": {...}, "type": "primary|danger|default", "url": "..."}
949 ]
950 },
951 { // Footer note
952 "tag": "note",
953 "elements": [{"tag": "plain_text", "content": "..."}]
954 }
955 ]
956 }
957}
958```
959 
960---
961 
962## Tips
963 
964- **Start with webhooks.** Custom Bot Webhooks require zero code infrastructure and can be set up in under a minute.
965- **Use interactive cards** for anything beyond simple text. They are more readable and actionable.
966- **Include action buttons** in every marketing card. Drive recipients to a landing page, dashboard, or sign-up form.
967- **Leverage bilingual support** if your team uses both Feishu and Lark, or has members in China and internationally.
968- **Respect rate limits.** For bulk messaging (e.g., sending to multiple groups), add a 1-second delay between requests.
969- **Test in a private group first** before sending to large team channels.
970- **Keep card content concise.** Cards have a maximum content size of approximately 30KB. For very long reports, link to an external page.
971- **Use the Feishu Message Card Builder** for visual card design: [https://open.feishu.cn/tool/cardbuilder](https://open.feishu.cn/tool/cardbuilder) (Feishu) or [https://open.larksuite.com/tool/cardbuilder](https://open.larksuite.com/tool/cardbuilder) (Lark).
972 

Discussion

Alternatives

API and interface designGuides stable API and interface design. Use when designing APIs, module boundaries, or any public interface. Use when creating REST or GraphQL endpoints, defining type contracts between modules, or establishing boundaries between frontend and backend.Coding · MITContext7Pulls up-to-date, version-specific library docs and code examples into the prompt so the AI stops inventing old APIs.Coding · MITContext7 Documentation LookupFetch up-to-date documentation and code examples for any library, framework, SDK, CLI tool, or cloud service. Use whenever the user asks about a specific library — even well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot — because training data may not reflect recent API changes or version updates. Always use for: API syntax questions, configuration options, version migration issues, "how do I" questions mentioning a library name, debugging that involves library-specific behavior, setup instructions, and CLI tool usage. Use even when you think you know the answer. Do not rely on training data for API details, signatures, or configuration options — they are frequently out of date. Prefer this over web search for library documentation.Coding · MITAdaptyv Bio Foundry APIHow to use the Adaptyv Bio Foundry API and Python SDK for protein experiment design, submission, and results retrieval. Use this skill whenever the user mentions Adaptyv, Foundry API, protein binding assays, protein screening experiments, BLI/SPR assays, thermostability assays, or wants to submit protein sequences for experimental characterization. Also trigger when code imports `adaptyv`, `adaptyv_sdk`, or `FoundryClient`, or references `foundry-api-public.adaptyvbio.com`.Science · MIT