feishu-sheets
Feishu online spreadsheet (Sheets) operations including create, read, write, append data, manage worksheets. Use when user mentions Feishu Sheets, online spreadsheet, electronic spreadsheet (not Bitable/multi-dimensional table). Supports: create spreadsheet, write/read cell values, append rows, insert/delete rows/columns, manage worksheets.
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-sheets-skill
Repository
Skill path: skills/hewenqiang/feishu-skills-kit/skills/feishu-sheets-skill
Feishu online spreadsheet (Sheets) operations including create, read, write, append data, manage worksheets. Use when user mentions Feishu Sheets, online spreadsheet, electronic spreadsheet (not Bitable/multi-dimensional table). Supports: create spreadsheet, write/read cell values, append rows, insert/delete rows/columns, manage worksheets.
Open repositoryBest for
Primary workflow: Analyze Data & AI.
Technical facets: Full Stack, Data / AI.
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-sheets into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding feishu-sheets to shared team environments
- Use feishu-sheets for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: feishu-sheets
description: |
Feishu online spreadsheet (Sheets) operations including create, read, write, append data, manage worksheets.
Use when user mentions Feishu Sheets, online spreadsheet, electronic spreadsheet (not Bitable/multi-dimensional table).
Supports: create spreadsheet, write/read cell values, append rows, insert/delete rows/columns, manage worksheets.
---
# Feishu Sheets Tool
Single tool `feishu_sheets` with action parameter for all spreadsheet operations.
## Token Extraction
From URL `https://xxx.feishu.cn/sheets/shtABC123` → `spreadsheet_token` = `shtABC123`
## Actions
### Create Spreadsheet
```json
{ "action": "create", "title": "New Spreadsheet" }
```
Optional folder:
```json
{ "action": "create", "title": "New Spreadsheet", "folder_token": "fldcnXXX" }
```
Returns: spreadsheet_token, url, title
### Write Values
```json
{
"action": "write",
"spreadsheet_token": "shtABC123",
"sheet_id": "0bxxxx",
"range": "A1:C3",
"values": [["Name", "Age", "City"], ["Alice", 25, "Beijing"], ["Bob", 30, "Shanghai"]]
}
```
### Read Values
```json
{
"action": "read",
"spreadsheet_token": "shtABC123",
"sheet_id": "0bxxxx",
"range": "A1:C10"
}
```
### Append Values
```json
{
"action": "append",
"spreadsheet_token": "shtABC123",
"sheet_id": "0bxxxx",
"values": [["Charlie", 28, "Shenzhen"]]
}
```
### Insert Rows/Columns
```json
{
"action": "insert_dimension",
"spreadsheet_token": "shtABC123",
"sheet_id": "0bxxxx",
"dimension": "ROWS",
"start_index": 5,
"end_index": 7
}
```
### Delete Rows/Columns
```json
{
"action": "delete_dimension",
"spreadsheet_token": "shtABC123",
"sheet_id": "0bxxxx",
"dimension": "ROWS",
"start_index": 5,
"end_index": 7
}
```
### Get Spreadsheet Info
```json
{ "action": "get_info", "spreadsheet_token": "shtABC123" }
```
Returns: metadata including all sheet_ids and titles
### Add Worksheet
```json
{
"action": "add_sheet",
"spreadsheet_token": "shtABC123",
"title": "Sheet2"
}
```
### Delete Worksheet
```json
{
"action": "delete_sheet",
"spreadsheet_token": "shtABC123",
"sheet_id": "0bxxxx"
}
```
## Range Format
- Cell: `A1`, `B5`
- Range: `A1:C10`, `B2:D5`
- Entire column: `A:A`, `B:D`
- Entire row: `1:1`, `3:5`
- With sheet_id: `0bxxxx!A1:C10`
## Sheet ID
- From URL: `https://xxx.feishu.cn/sheets/shtABC123?sheet=0bxxxx`
- From get_info action
- Default first sheet often has simple id like `0bxxxx`
## Data Types
Values can be:
- String: `"Hello"`
- Number: `123`, `45.67`
- Formula: `{"type": "formula", "text": "=SUM(A1:A10)"}`
- Link: `{"type": "url", "text": "Click here", "link": "https://..."}`
## Configuration
```yaml
channels:
feishu:
tools:
sheets: true # default: true
```
## Permissions Required
- `sheets:spreadsheet` - Create and manage spreadsheets
- `sheets:spreadsheet:readonly` - Read spreadsheet data
- `drive:drive` - Access cloud storage
## API Reference
Base URL: `https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/`
See references/api-reference.md for detailed API documentation.
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"ownerId": "kn710qwp94vjc36crngn15sfq9819j6c",
"slug": "feishu-sheets-skill",
"version": "1.0.0",
"publishedAt": 1771220240776
}
```
### references/api-reference.md
```markdown
# Feishu Sheets API Reference
## Base URLs
- Sheets API v2: `https://open.feishu.cn/open-apis/sheets/v2/spreadsheets`
- Sheets API v3: `https://open.feishu.cn/open-apis/sheets/v3/spreadsheets`
- Auth API: `https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal`
## Authentication
All API calls require a tenant access token in the Authorization header:
```
Authorization: Bearer {tenant_access_token}
```
Get token by calling auth API with app_id and app_secret.
## Core APIs
### 1. Create Spreadsheet
**Endpoint:** `POST /sheets/v3/spreadsheets`
**Request Body:**
```json
{
"title": "Spreadsheet Name",
"folder_token": "fldcnXXX" // optional
}
```
**Response:**
```json
{
"code": 0,
"msg": "success",
"data": {
"spreadsheet": {
"title": "Spreadsheet Name",
"spreadsheet_token": "shtcnXXX",
"url": "https://xxx.feishu.cn/sheets/shtcnXXX"
}
}
}
```
### 2. Get Spreadsheet Info
**Endpoint:** `GET /sheets/v2/spreadsheets/{spreadsheet_token}/metainfo`
**Response:**
```json
{
"code": 0,
"data": {
"properties": {
"title": "Spreadsheet Name"
},
"sheets": [
{
"sheet_id": "0bxxxx",
"title": "Sheet1",
"grid_properties": {
"row_count": 1000,
"column_count": 20
}
}
]
}
}
```
### 3. Read Values
**Endpoint:** `GET /sheets/v2/spreadsheets/{spreadsheet_token}/values/{range}`
**Range Formats:**
- `A1:C10` - Cell range
- `0bxxxx!A1:C10` - With sheet_id
- `Sheet1!A1:C10` - With sheet title
**Response:**
```json
{
"code": 0,
"data": {
"range": "0bxxxx!A1:C3",
"values": [
["Name", "Age", "City"],
["Alice", 25, "Beijing"],
["Bob", 30, "Shanghai"]
]
}
}
```
### 4. Write Values
**Endpoint:** `PUT /sheets/v2/spreadsheets/{spreadsheet_token}/values`
**Request Body:**
```json
{
"valueRange": {
"range": "0bxxxx!A1:C3",
"values": [
["Name", "Age", "City"],
["Alice", 25, "Beijing"],
["Bob", 30, "Shanghai"]
]
}
}
```
### 5. Append Values
**Endpoint:** `POST /sheets/v2/spreadsheets/{spreadsheet_token}/values_append`
**Request Body:**
```json
{
"valueRange": {
"range": "0bxxxx",
"values": [
["Charlie", 28, "Shenzhen"]
]
}
}
```
### 6. Insert Rows/Columns
**Endpoint:** `POST /sheets/v2/spreadsheets/{spreadsheet_token}/insert_dimension_range`
**Request Body:**
```json
{
"dimension": {
"sheetId": "0bxxxx",
"majorDimension": "ROWS", // or "COLUMNS"
"startIndex": 5,
"endIndex": 7
}
}
```
### 7. Delete Rows/Columns
**Endpoint:** `DELETE /sheets/v2/spreadsheets/{spreadsheet_token}/dimension_range`
**Request Body:** Same as insert
### 8. Add Worksheet
**Endpoint:** `POST /sheets/v2/spreadsheets/{spreadsheet_token}/sheets_batch_update`
**Request Body:**
```json
{
"requests": [{
"addSheet": {
"properties": {
"title": "New Sheet"
}
}
}]
}
```
### 9. Delete Worksheet
**Endpoint:** `POST /sheets/v2/spreadsheets/{spreadsheet_token}/sheets_batch_update`
**Request Body:**
```json
{
"requests": [{
"deleteSheet": {
"sheetId": "0bxxxx"
}
}]
}
```
## Data Types
### Cell Values
**String:**
```json
"Hello World"
```
**Number:**
```json
123
45.67
```
**Formula:**
```json
{
"type": "formula",
"text": "=SUM(A1:A10)"
}
```
**Hyperlink:**
```json
{
"type": "url",
"text": "Click here",
"link": "https://example.com"
}
```
## Error Codes
| Code | Message | Description |
|------|---------|-------------|
| 0 | success | Request successful |
| 10001 | bad request | Invalid parameters |
| 10002 | unauthorized | Invalid or expired token |
| 10003 | forbidden | Insufficient permissions |
| 10004 | not found | Spreadsheet or sheet not found |
| 10005 | internal error | Server error |
## Rate Limits
- Default: 100 requests per minute per app
- Bulk operations (batch_update): 20 requests per minute
## Best Practices
1. **Reuse token:** Cache tenant_access_token for up to 2 hours
2. **Batch operations:** Use batch_update for multiple changes
3. **Range selection:** Be specific with ranges to reduce data transfer
4. **Error handling:** Always check response code before processing data
5. **Sheet ID:** Use sheet_id (not title) for reliable operations
```
### scripts/feishu_sheets.py
```python
#!/usr/bin/env python3
"""
Feishu Sheets API Client
Supports: create, read, write, append, insert/delete rows/columns
"""
import os
import sys
import json
import requests
from typing import List, Dict, Any, Optional
# API Base URLs
BASE_URL = "https://open.feishu.cn/open-apis"
SHEETS_API = f"{BASE_URL}/sheets/v2/spreadsheets"
AUTH_API = f"{BASE_URL}/auth/v3/tenant_access_token/internal"
class FeishuSheetsClient:
def __init__(self):
self.app_id = os.getenv("FEISHU_APP_ID")
self.app_secret = os.getenv("FEISHU_APP_SECRET")
self._token = None
def _get_token(self) -> str:
"""Get tenant access token"""
if self._token:
return self._token
resp = requests.post(AUTH_API, json={
"app_id": self.app_id,
"app_secret": self.app_secret
})
resp.raise_for_status()
data = resp.json()
if data.get("code") != 0:
raise Exception(f"Auth failed: {data}")
self._token = data["tenant_access_token"]
return self._token
def _headers(self) -> Dict[str, str]:
return {
"Authorization": f"Bearer {self._get_token()}",
"Content-Type": "application/json"
}
def create_spreadsheet(self, title: str, folder_token: Optional[str] = None) -> Dict:
"""Create a new spreadsheet"""
url = f"{BASE_URL}/sheets/v3/spreadsheets"
payload = {"title": title}
if folder_token:
payload["folder_token"] = folder_token
resp = requests.post(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def get_spreadsheet_info(self, spreadsheet_token: str) -> Dict:
"""Get spreadsheet metadata including sheets"""
url = f"{SHEETS_API}/{spreadsheet_token}/metainfo"
resp = requests.get(url, headers=self._headers())
resp.raise_for_status()
return resp.json()
def read_values(self, spreadsheet_token: str, sheet_id: str, range_str: str) -> Dict:
"""Read values from a range"""
range_param = f"{sheet_id}!{range_str}" if sheet_id else range_str
url = f"{SHEETS_API}/{spreadsheet_token}/values/{range_param}"
resp = requests.get(url, headers=self._headers())
resp.raise_for_status()
return resp.json()
def write_values(self, spreadsheet_token: str, sheet_id: str,
range_str: str, values: List[List[Any]]) -> Dict:
"""Write values to a range"""
url = f"{SHEETS_API}/{spreadsheet_token}/values"
range_param = f"{sheet_id}!{range_str}" if sheet_id else range_str
payload = {
"valueRange": {
"range": range_param,
"values": values
}
}
resp = requests.put(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def append_values(self, spreadsheet_token: str, sheet_id: str,
values: List[List[Any]]) -> Dict:
"""Append values to the end of sheet"""
url = f"{SHEETS_API}/{spreadsheet_token}/values_append"
payload = {
"valueRange": {
"range": sheet_id,
"values": values
}
}
resp = requests.post(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def insert_dimension(self, spreadsheet_token: str, sheet_id: str,
dimension: str, start_index: int, end_index: int) -> Dict:
"""Insert rows or columns"""
url = f"{SHEETS_API}/{spreadsheet_token}/insert_dimension_range"
payload = {
"dimension": {
"sheetId": sheet_id,
"majorDimension": dimension, # ROWS or COLUMNS
"startIndex": start_index,
"endIndex": end_index
}
}
resp = requests.post(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def delete_dimension(self, spreadsheet_token: str, sheet_id: str,
dimension: str, start_index: int, end_index: int) -> Dict:
"""Delete rows or columns"""
url = f"{SHEETS_API}/{spreadsheet_token}/dimension_range"
payload = {
"dimension": {
"sheetId": sheet_id,
"majorDimension": dimension,
"startIndex": start_index,
"endIndex": end_index
}
}
resp = requests.delete(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def add_sheet(self, spreadsheet_token: str, title: str) -> Dict:
"""Add a new worksheet"""
url = f"{SHEETS_API}/{spreadsheet_token}/sheets_batch_update"
payload = {
"requests": [{
"addSheet": {
"properties": {"title": title}
}
}]
}
resp = requests.post(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def delete_sheet(self, spreadsheet_token: str, sheet_id: str) -> Dict:
"""Delete a worksheet"""
url = f"{SHEETS_API}/{spreadsheet_token}/sheets_batch_update"
payload = {
"requests": [{
"deleteSheet": {"sheetId": sheet_id}
}]
}
resp = requests.post(url, headers=self._headers(), json=payload)
resp.raise_for_status()
return resp.json()
def main():
"""CLI entry point for tool calls"""
if len(sys.argv) < 2:
print(json.dumps({"error": "No action specified"}))
sys.exit(1)
action = sys.argv[1]
client = FeishuSheetsClient()
try:
if action == "create":
title = sys.argv[2]
folder = sys.argv[3] if len(sys.argv) > 3 else None
result = client.create_spreadsheet(title, folder)
elif action == "get_info":
token = sys.argv[2]
result = client.get_spreadsheet_info(token)
elif action == "read":
token = sys.argv[2]
sheet_id = sys.argv[3]
range_str = sys.argv[4]
result = client.read_values(token, sheet_id, range_str)
elif action == "write":
token = sys.argv[2]
sheet_id = sys.argv[3]
range_str = sys.argv[4]
values = json.loads(sys.argv[5])
result = client.write_values(token, sheet_id, range_str, values)
elif action == "append":
token = sys.argv[2]
sheet_id = sys.argv[3]
values = json.loads(sys.argv[4])
result = client.append_values(token, sheet_id, values)
elif action == "insert_dimension":
token = sys.argv[2]
sheet_id = sys.argv[3]
dimension = sys.argv[4]
start = int(sys.argv[5])
end = int(sys.argv[6])
result = client.insert_dimension(token, sheet_id, dimension, start, end)
elif action == "delete_dimension":
token = sys.argv[2]
sheet_id = sys.argv[3]
dimension = sys.argv[4]
start = int(sys.argv[5])
end = int(sys.argv[6])
result = client.delete_dimension(token, sheet_id, dimension, start, end)
elif action == "add_sheet":
token = sys.argv[2]
title = sys.argv[3]
result = client.add_sheet(token, title)
elif action == "delete_sheet":
token = sys.argv[2]
sheet_id = sys.argv[3]
result = client.delete_sheet(token, sheet_id)
else:
result = {"error": f"Unknown action: {action}"}
print(json.dumps(result, ensure_ascii=False, indent=2))
except Exception as e:
print(json.dumps({"error": str(e)}, ensure_ascii=False))
sys.exit(1)
if __name__ == "__main__":
main()
```