back
loading skill details...
Get笔记 - 个人笔记和知识库管理工具。 ## 核心能力 **1. 一键保存任意内容为笔记** - 发一个链接 → 帮你保存原文并生成摘要,支持所有公开网页链接 - 发一张图片 → OCR 识别文字、AI 分析图片内容 - 写一段话 → 直接保存为文本笔记 - 触发词:「记一下」「存到笔记」「保存这个链接」「保...
---
name: Get笔记
description: |
Get笔记 - 个人笔记和知识库管理工具。
## 核心能力
**1. 一键保存任意内容为笔记**
- 发一个链接 → 帮你保存原文并生成摘要,支持所有公开网页链接
- 发一张图片 → OCR 识别文字、AI 分析图片内容
- 写一段话 → 直接保存为文本笔记
- 触发词:「记一下」「存到笔记」「保存这个链接」「保存这张图」「帮我记录」
**2. 查询和获取笔记**
- 支持图片笔记、链接笔记、语音笔记(录音转写)
- 详情接口返回完整原文(网页正文、音频转写、图片 OCR)
- 触发词:「查我的笔记」「看看我存了什么」「找一下笔记」「原文是什么」
**3. 知识库和标签管理**
- 创建知识库、将笔记加入/移出知识库
- 添加/删除标签,批量管理
- 查看知识库订阅的博主和直播内容
- 触发词:「查知识库」「建知识库」「加标签」「删标签」
**4. 语义搜索(无需拉取全部数据)**
- 全局搜索:在所有笔记中语义召回
- 知识库搜索:在指定知识库范围内搜索
- 触发词:「搜一下」「找找我哪些笔记提到了 XX」「在 XX 知识库搜 XX」
## 触发场景
- 用户要求「配置 Get笔记」「连接 Get笔记」「帮我设置笔记」时(OAuth 授权流程)
- 用户发送任意 URL 链接时(自动识别为保存链接笔记)
- 用户发送图片时(自动识别为保存图片笔记)
- 用户提到「笔记」「记一下」「保存」「收藏」「存起来」等关键词
- 用户询问「我之前存过什么」「帮我找一下」「搜一下笔记」等
metadata: {"openclaw": {"requires": {}, "optionalEnv": ["GETNOTE_API_KEY", "GETNOTE_CLIENT_ID", "GETNOTE_OWNER_ID"], "baseUrl": "https://openapi.biji.com", "homepage": "https://biji.com"}}
---
# Get笔记 API
## ⚠️ 必读约束
### 🌐 Base URL(重要!所有 API 共用)
```
https://openapi.biji.com
```
**所有 API 请求必须使用此 Base URL**,不要使用 `biji.com` 或其他地址。
---
### 🔑 首次安装配置
> ⚠️ **Agent 必读**:每次调用 API 前,先检查 `$GETNOTE_API_KEY` 是否存在。若不存在,**自动发起 OAuth 授权流程**(无需用户主动说"配置"),告知用户需要先授权才能使用。
#### 自动触发 OAuth(默认行为)
当检测到未配置时,Agent 会自动:
1. 生成授权链接并发送给用户
2. 后台轮询等待授权完成
3. 授权成功后自动写入配置
4. 继续执行用户原本的请求
用户只需点击链接、授权,即可无缝使用。
#### 手动配置(可选)
1. 前往 [Get笔记开放平台](https://www.biji.com/openapi) 创建应用
2. 获取 Client ID 和 API Key
3. 在 `~/.openclaw/openclaw.json` 中添加:
```json
{
"skills": {
"entries": {
"getnote": {
"apiKey": "gk_live_你的key",
"env": {
"GETNOTE_CLIENT_ID": "cli_你的id",
"GETNOTE_OWNER_ID": "ou_你的飞书ID(可选,用于权限控制)"
}
}
}
}
}
```
---
### 🔢 笔记 ID 处理规则(重要!)
笔记 ID(`id`、`note_id`、`next_cursor` 等)是 **64 位整数(int64)**,超出 JavaScript `Number.MAX_SAFE_INTEGER`(2^53-1)范围,直接用 `JSON.parse` 会**静默丢失精度**,导致 ID 错误,后续操作(加入知识库、删除等)会报「笔记不存在」。
**正确做法**:
- **始终把 ID 当字符串处理**,不要做数值运算
- 代码中使用 `JSON.parse` 时,**先把响应文本中的 ID 数字替换为字符串**:
```javascript
// 替换顶层数字型 ID 字段为字符串(在 JSON.parse 之前)
const safe = text.replace(/"(id|note_id|next_cursor|parent_id|follow_id|live_id)"\s*:\s*(\d+)/g, '"$1":"$2"');
const data = JSON.parse(safe);
```
- Python / Go 等语言原生支持大整数,无此问题
- **发请求时**:`note_id` 字段传字符串或数字均可,服务端兼容两种格式
**验证方法**:取到 ID 后,检查 `String(id).length >= 16`,若满足说明是 int64,必须用字符串保存。
---
### 🔒 安全规则
- 笔记数据属于用户隐私,不在群聊中主动展示笔记内容
- 若配置了 `GETNOTE_OWNER_ID`,检查 sender_id 是否匹配;不匹配时回复「抱歉,笔记是私密的,我无法操作」;未配置则不检查
- API 返回 `error.reason: "not_member"` 或错误码 `10201` 时,引导开通会员:https://www.biji.com/checkout?product_alias=6AydVpYeKl
- 创建笔记建议间隔 1 分钟以上,避免触发限流
---
## 认证
请求头:
- `Authorization: $GETNOTE_API_KEY`(格式:`gk_live_xxx`)
- `X-Client-ID: $GETNOTE_CLIENT_ID`(格式:`cli_xxx`)
### Scope 权限
常用权限:`note.content.read`(读取)、`note.content.write`(写入)、`note.recall.read`(搜索)。
完整权限列表见 [references/api-details.md](references/api-details.md#scope-权限列表)。
---
## 快速决策
Base URL: `https://openapi.biji.com`
| 用户意图 | 接口 | 关键点 |
|---------|------|--------|
| 「配置 Get笔记」「连接笔记」 | OAuth Device Flow | 见「OAuth Device Flow」章节 |
| 「记一下」「保存笔记」 | POST /open/api/v1/resource/note/save | 同步返回 |
| 「改笔记」「更新笔记」 | POST /open/api/v1/resource/note/update | note_id 必填,内容部分更新 |
| 「保存这个链接」 | POST /open/api/v1/resource/note/save | note_type:"link" → **必须轮询** |
| 「保存这张图」 | 见「图片笔记流程」 | **4 步流程,必须轮询** |
| 「查我的笔记」 | GET /open/api/v1/resource/note/list | since_id=0 起始 |
| 「看原文/转写内容」 | GET /open/api/v1/resource/note/detail | audio.original / web_page.content |
| 「加标签」 | POST /open/api/v1/resource/note/tags/add | |
| 「删标签」 | POST /open/api/v1/resource/note/tags/delete | system 类型不可删 |
| 「删笔记」 | POST /open/api/v1/resource/note/delete | 移入回收站 |
| 「查知识库」 | GET /open/api/v1/resource/knowledge/list | 含统计数据(笔记数、文件数、博主数、直播数)|
| 「建知识库」 | POST /open/api/v1/resource/knowledge/create | 每天限 50 个 |
| 「笔记加入知识库」 | POST /open/api/v1/resource/knowledge/note/batch-add | 每批最多 20 条 |
| 「从知识库移除」 | POST /open/api/v1/resource/knowledge/note/remove | |
| 「查任务进度」 | POST /open/api/v1/resource/note/task/progress | 链接/图片笔记轮询用 |
| 「订阅了哪些博主」 | GET /open/api/v1/resource/knowledge/bloggers | 按 topic_id 查 |
| 「博主发了什么内容」 | GET /open/api/v1/resource/knowledge/blogger/contents | 需要 follow_id,列表只含摘要 |
| 「博主内容原文/详情」 | GET /open/api/v1/resource/knowledge/blogger/content/detail | 需要 post_id,含原文 |
| 「有哪些已完成直播」 | GET /open/api/v1/resource/knowledge/lives | 按 topic_id 查 |
| 「直播总结/直播原文」 | GET /open/api/v1/resource/knowledge/live/detail | 需要 live_id |
| 「搜一下」「找找笔记里提到 XX 的」 | POST /open/api/v1/resource/recall | 全局语义召回,见「笔记召回」章节 |
| 「在 XX 知识库搜 XX」 | POST /open/api/v1/resource/recall/knowledge | 知识库语义召回,见「知识库召回」章节 |
---
## 核心功能:记笔记 & 查笔记
### 笔记列表
```
GET /open/api/v1/resource/note/list?since_id=0
```
参数:
- since_id (int64, 必填) - 游标,首次传 0,后续用 next_cursor
返回:notes[], has_more, next_cursor, total(每次固定 20 条)
> ⚠️ **响应 JSON 可能包含未转义的控制字符**(笔记 content 中的原始换行符),建议用支持容错解析的 JSON 库处理,或在解析前对 content 字段做预处理。
> ⚠️ **`id`、`next_cursor`、`parent_id` 均为 int64**,JavaScript 环境必须在 `JSON.parse` 前做字符串化处理(见「笔记 ID 处理规则」)。**务必用字符串保存这些值**,不要做数值运算,后续调详情、加知识库等操作直接透传字符串即可。
**笔记类型 note_type**:
- `plain_text` - 纯文本
- `img_text` - 图片笔记
- `link` - 链接笔记
- `audio` - 即时录音
- `meeting` - 会议录音
- `local_audio` - 本地音频
- `internal_record` - 内录音频
- `class_audio` - 课堂录音
- `recorder_audio` - 录音卡长录
- `recorder_flash_audio` - 录音卡闪念
---
### 笔记详情
```
GET /open/api/v1/resource/note/detail?id={note_id}
```
参数:
- `id` (int64, 必填) - 笔记 ID
- `image_quality` (string, 可选) - 图片质量,传 `original` 返回正文中图片的原图链接(无压缩)
**新增字段**:
- `note_id` (string) - 笔记 ID 的字符串格式,便于 AI Agent 解析,避免 int64 精度问题
- `children_ids` (string[]) - 子笔记 ID 列表(字符串格式),仅当有子笔记时返回
**图片附件**:`attachments[]` 中每个图片包含:
- `url` - 缩略图 URL(720px 压缩)
- `original_url` - 原图 URL(无压缩,适合需要高清图的场景)
**正文原图**:传 `image_quality=original` 时,`content` 中的 markdown 图片链接会返回原图(去掉 OSS 压缩参数)。
**详情独有字段**(列表不返回):`audio.original`、`audio.play_url`、`audio.duration`、`web_page.content`、`web_page.url`、`web_page.excerpt`、`attachments[]`、`children_ids`。详见 [references/api-details.md](references/api-details.md)。
---
### 新建笔记
```
POST /open/api/v1/resource/note/save
Content-Type: application/json
```
**仅支持新建,不支持编辑**。
请求体:
```json
{
"title": "笔记标题",
"content": "Markdown 内容",
"note_type": "plain_text",
"tags": ["标签1", "标签2"],
"parent_id": 0,
"link_url": "https://...",
"image_urls": ["https://..."]
}
```
- `plain_text`:同步返回,立即完成
- `link` / `img_text`:返回 task_id,**必须轮询** /task/progress
详细字段说明见 [references/api-details.md](references/api-details.md)。
---
### 更新笔记
```
POST /open/api/v1/resource/note/update
Content-Type: application/json
```
请求体:
```json
{
"note_id": 123456789,
"title": "新标题",
"content": "新的 Markdown 内容",
"tags": ["标签1", "标签2"]
}
```
参数说明:
- `note_id` (int64, **必填**) - 要更新的笔记 ID
- `title` (string, 可选) - 新标题,不传则不更新
- `content` (string, 可选) - 新内容,不传则不更新
- `tags` (string[], 可选) - 新标签列表,**替换**原有标签(不传则保持原标签)
> ⚠️ **至少需要传 title、content、tags 中的一个**,否则返回错误。
> ⚠️ **仅支持 plain_text 类型笔记**,链接笔记、图片笔记等暂不支持更新。
---
### 查询任务进度
```
POST /open/api/v1/resource/note/task/progress
Content-Type: application/json
```
请求体:
```json
{"task_id": "task_abc123xyz"}
```
返回:
- status: pending | processing | success | failed
- note_id: 成功时返回笔记 ID
- error_msg: 失败时返回错误信息
> ⚠️ **note_id 是 int64**,JavaScript 环境须按「笔记 ID 处理规则」做字符串化,拿到后直接当字符串透传。
**建议 10-30 秒间隔轮询,直到 success 或 failed**。
---
### 删除笔记
```
POST /open/api/v1/resource/note/delete
Content-Type: application/json
```
请求体:
```json
{"note_id": 123456789}
```
笔记移入回收站,需要 note.content.trash scope。
---
## 异步任务流程
> ⚠️ **必须遵循的体验流程**:链接笔记和图片笔记是异步生成的,必须按以下方式与用户沟通。
### 链接笔记完整流程
**步骤 1**:提交任务
```
POST /open/api/v1/resource/note/save {note_type:"link", link_url:"https://..."}
```
返回 task_id 后,**立即发消息给用户**:
> ✅ 链接已保存,正在抓取原文和生成总结,稍后告诉你结果...
> ⚠️ **重复链接处理**:若响应中包含 `duplicate_count > 0` 且没有 `task_id`,说明该链接已存在于你的笔记中,无需轮询,直接告知用户「该链接已存在于你的笔记中」。
**步骤 2**:后台轮询(10-30 秒间隔)
```
POST /open/api/v1/resource/note/task/progress {task_id} → 直到 status=success/failed
```
**步骤 3**:任务完成后,**调详情接口展示价值**
```
GET /open/api/v1/resource/note/detail?id={note_id}
```
然后发第二条消息,包含具体内容:
> ✅ 笔记生成完成!
> - 📄 **原文**:已保存 {web_page.content 字数} 字
> - 📝 **总结**:{content 内容,即 AI 生成的摘要}
> - 🔗 **来源**:{web_page.url}
### 图片笔记完整流程
**步骤 1-3**:获取凭证 → 上传 OSS → 提交任务
```
1. GET /open/api/v1/resource/image/upload_token?mime_type=jpg → 获取上传凭证
2. POST {host} 上传文件到 OSS
3. POST /open/api/v1/resource/note/save {note_type:"img_text", image_urls:[access_url]} → 返回 task_id
```
拿到 task_id 后,**立即发消息给用户**:
> ✅ 图片已保存,正在识别内容,稍后告诉你结果...
**步骤 4**:后台轮询
```
POST /open/api/v1/resource/note/task/progress {task_id} → 直到 status=success/failed
```
**步骤 5**:任务完成后,**调详情接口展示价值**
```
GET /open/api/v1/resource/note/detail?id={note_id}
```
然后发第二条消息:
> ✅ 图片笔记生成完成!
> - 📝 **识别内容**:{content 内容}
> - 🏷️ **标签**:{tags}
### 图片上传凭证
```
GET /open/api/v1/resource/image/upload_token?mime_type=jpg&count=1
```
参数:
- mime_type: jpg | png | gif | webp,默认 png
- count: 需要的 token 数量,默认 1,最大 9
⚠️ **mime_type 必须与实际文件格式一致**,否则 OSS 签名失败。
返回字段说明见 [references/api-details.md](references/api-details.md)。
### OSS 上传示例
> ⚠️ **字段顺序必须严格遵守**,否则 OSS 签名验证失败。
字段顺序:`key → OSSAccessKeyId → policy → signature → callback → Content-Type → file`
完整示例见 [references/api-details.md](references/api-details.md#oss-上传详细示例)。
---
## 笔记召回(全局语义搜索)
> 适用场景:「搜一下」「找找我哪些笔记提到了 XX」
**所需 scope**: `note.recall.read`
```
POST /open/api/v1/resource/recall
Content-Type: application/json
```
请求体:
```json
{
"query": "搜索关键词",
"top_k": 3
}
```
| 参数 | 类型 | 说明 |
|------|------|------|
| query | string, 必填 | 搜索关键词或语义描述 |
| top_k | int, 可选 | 返回数量,默认 **3**,最大 **10** |
返回结构(结果已按相关度**从高到低**排序):
```json
{
"results": [
{
"note_id": "1896830231705320746",
"note_type": "NOTE",
"title": "笔记标题",
"content": "笔记内容片段",
"created_at": "2025-12-24 15:20:15"
}
]
}
```
---
## 知识库召回(指定知识库语义搜索)
> 适用场景:「在我的 XX 知识库搜一下 XX」
**所需 scope**: `note.topic.recall.read`
```
POST /open/api/v1/resource/recall/knowledge
Content-Type: application/json
```
请求体:
```json
{
"topic_id": "知识库 alias id",
"query": "搜索关键词",
"top_k": 3
}
```
| 参数 | 类型 | 说明 |
|------|------|------|
| topic_id | string, 必填 | 知识库 ID(alias id,来自 /knowledge/list 的 topic_id_alias) |
| query | string, 必填 | 搜索关键词或语义描述 |
| top_k | int, 可选 | 返回数量,默认 **3**,最大 **10** |
返回结构同笔记召回。
---
## 召回结果说明
| 字段 | 说明 |
|------|------|
| note_id | 笔记 ID(string);**仅 `NOTE` 类型有值**,其余类型均为空 |
| note_type | 内容类型:NOTE / FILE / BLOGGER / LIVE / URL / DEDAO |
| title | 笔记/文档标题 |
| content | 相关内容片段 |
| created_at | 创建/发布时间(YYYY-MM-DD HH:MM:SS)|
| page_no | `FILE` 类型时表示文件页码,其余类型省略 |
> **后续操作**:`NOTE` 类型可调详情接口获取全文,其余类型只能展示召回片段。
### 示例对话
> 用户:「找找我哪些笔记提到了大模型 API」
> → `POST /recall` `{ "query": "大模型 API", "top_k": 3 }`
> 用户:「在我的 AI 学习知识库里搜一下 RAG」
> → 先调 `/knowledge/list` 找到 `topic_id_alias`,再 `POST /recall/knowledge` `{ "topic_id": "xxx", "query": "RAG", "top_k": 3 }`
---
## 笔记整理
### 添加标签
```
POST /open/api/v1/resource/note/tags/add
Content-Type: application/json
```
请求体:
```json
{
"note_id": 123456789,
"tags": ["工作", "重要"]
}
```
**标签类型 type**:
- ai - AI 自动生成
- manual - 用户手动添加
- system - 系统标签(**不可删除**)
---
### 删除标签
```
POST /open/api/v1/resource/note/tags/delete
Content-Type: application/json
```
请求体:
```json
{
"note_id": 123456789,
"tag_id": "123"
}
```
⚠️ system 类型标签不允许删除。
---
### 知识库列表
```
GET /open/api/v1/resource/knowledge/list?page=1
```
参数:
- page: 页码,从 1 开始,默认 1(固定每页 20 条)
返回:topics[], has_more, total
每个 topic 包含:
- `topic_id` / `topic_id_alias`:知识库 ID
- `name`、`description`、`cover`
- `created_at` / `updated_at`:时间字符串(YYYY-MM-DD HH:MM:SS)
- `stats`:统计数据
- `note_count`:笔记数
- `file_count`:文件数
- `blogger_count`:订阅博主数
- `live_count`:已完成直播数
---
### 创建知识库
```
POST /open/api/v1/resource/knowledge/create
Content-Type: application/json
```
请求体:
```json
{
"name": "知识库名称",
"description": "描述",
"cover": ""
}
```
⚠️ 每天最多创建 50 个知识库(北京时间 00:00 重置)。
---
### 知识库笔记列表
```
GET /open/api/v1/resource/knowledge/notes?topic_id=abc123&page=1
```
参数:
- topic_id (string, 必填) - 知识库 ID(alias id)
- page: 页码,从 1 开始
每页固定 20 条,用 has_more 判断是否有下一页。
---
### 知识库选择逻辑
当用户说「存到对应的知识库」或「存到相关知识库」时:
1. 先调用 GET /knowledge/list 获取所有知识库列表
2. 根据笔记标题、内容、标签,与知识库名称和描述做模糊匹配
3. 匹配置信度高时直接执行,并告知用户存入了哪个知识库
4. 置信度低或有歧义时,列出候选知识库让用户选择
5. 用户未提及知识库时,**不要擅自存入**任何知识库
---
### 添加笔记到知识库
```
POST /open/api/v1/resource/knowledge/note/batch-add
Content-Type: application/json
```
请求体:
```json
{
"topic_id": "abc123",
"note_ids": [123456789, 123456790]
}
```
⚠️ 每批最多 20 条。已存在的笔记会跳过。
---
### 从知识库移除笔记
```
POST /open/api/v1/resource/knowledge/note/remove
Content-Type: application/json
```
请求体:
```json
{
"topic_id": "abc123",
"note_ids": [123456789]
}
```
---
## 知识库:博主订阅
### 博主列表
```
GET /open/api/v1/resource/knowledge/bloggers?topic_id={alias_id}&page=1
```
参数:
- topic_id (string, 必填) - 知识库 AliasID(来自 /knowledge/list 的 topic_id_alias)
- page: 页码,从 1 开始
每页固定 20 条,用 has_more 判断。
返回 bloggers[],每项字段:
| 字段 | 说明 |
|------|------|
| follow_id | 订阅关系 ID,**查博主内容时必用** |
| account_name | 博主名称 |
| account_icon | 博主头像 |
| platform | 平台(如 DEDAO)|
| account_url | 博主主页链接 |
| follow_time | 订阅时间(YYYY-MM-DD HH:MM:SS)|
---
### 博主内容列表
```
GET /open/api/v1/resource/knowledge/blogger/contents?topic_id={alias_id}&follow_id={follow_id}&page=1
```
参数:`topic_id`(知识库 AliasID)、`follow_id`(博主订阅 ID)、`page`(页码)
返回 `contents[]`,关键字段:`post_id_alias`(详情必用)、`post_title`、`post_summary`。
> 列表不含原文,需要原文请调详情接口。完整字段见 [references/api-details.md](references/api-details.md#博主内容字段说明)。
---
### 博主内容详情(含原文)
```
GET /open/api/v1/resource/knowledge/blogger/content/detail?topic_id={alias_id}&post_id={post_id_alias}
```
参数:`topic_id`(知识库 AliasID)、`post_id`(内容 ID,来自列表的 post_id_alias)
返回完整内容,包含 `post_media_text`(原文)。
---
## 知识库:直播订阅
### 已完成直播列表
```
GET /open/api/v1/resource/knowledge/lives?topic_id={alias_id}&page=1
```
参数:`topic_id`(知识库 AliasID)、`page`(页码)
返回 `lives[]`,关键字段:`live_id`(详情必用)、`name`、`status`。
**只返回已结束且 AI 已处理完的直播。** 完整字段见 [references/api-details.md](references/api-details.md#直播字段说明)。
---
### 直播详情(总结 + 原文)
```
GET /open/api/v1/resource/knowledge/live/detail?topic_id={alias_id}&live_id={live_id}
```
参数:`topic_id`(知识库 AliasID)、`live_id`(直播 ID,来自列表)
返回完整内容,包含 `post_summary`(AI 摘要)和 `post_media_text`(原文转写)。
---
## 错误处理
> 详细错误码和限流结构见 [references/api-details.md](references/api-details.md)
### 响应结构
```json
{
"success": false,
"error": {
"code": 10001,
"message": "unauthorized",
"reason": "not_member"
},
"request_id": "xxx"
}
```
### 常见错误码
| 错误码 | 说明 | 处理方式 |
|--------|------|---------|
| 10001 | 鉴权失败 | 检查 API Key 和 Client ID |
| 10201 | 非会员 | 引导开通:https://www.biji.com/checkout?product_alias=6AydVpYeKl |
| 20001 | 笔记不存在 | 确认笔记 ID 正确 |
| 42900 | 限流 | 降低频率,查看 rate_limit 字段 |
| 50000 | 系统错误 | 稍后重试 |
---
## OAuth Device Flow(自动配置)
当用户要求「配置 Get笔记」「连接 Get笔记」时,使用此流程自动获取凭证。
### 流程概述
1. **申请授权码** → 获取 `code` 和 `verification_uri`
2. **展示给用户** → 发送授权链接
3. **轮询等待** → 用户授权后获取 `api_key` 和 `client_id`
4. **写入配置** → 自动配置完成
### 步骤 1:申请授权码
```
POST https://openapi.biji.com/open/api/v1/oauth/device/code
Content-Type: application/json
```
请求体:
```json
{
"client_id": "cli_a1b2c3d4e5f6789012345678abcdef90"
}
```
> ⚠️ **client_id 固定为 `cli_a1b2c3d4e5f6789012345678abcdef90`**,这是 Get笔记 为 OpenClaw 预注册的应用。
返回:
```json
{
"success": true,
"data": {
"code": "abc123...",
"verification_uri": "https://biji.com/openapi/oauth/authorize?code=abc123...",
"user_code": "ABCD-1234",
"expires_in": 600,
"interval": 5
}
}
```
| 字段 | 说明 |
|------|------|
| code | 授权码,轮询时使用 |
| verification_uri | 授权链接,**发送给用户点击** |
| user_code | 确认码,**必须展示给用户核对** |
| expires_in | 授权码有效期(秒),默认 600 |
| interval | 建议轮询间隔(秒),默认 5 |
### 步骤 2:展示授权链接
将 `verification_uri` 和 `user_code` 发送给用户:
> 🔗 请点击链接完成授权:
>
> {verification_uri}
>
> ⚠️ **请核对确认码**:`{user_code}`
>
> 授权页面会显示确认码,请确保与上面一致后再点击授权。授权码 10 分钟内有效。
**安全提醒**:`user_code` 用于防止钓鱼攻击。用户在授权页面看到的确认码必须与 Agent 展示的一致,不一致请勿授权。
**发送后立即启动后台轮询**(步骤 3)。
### 步骤 3:轮询等待授权
发送授权链接给用户后,**立即在后台启动轮询**,无需等待用户回复。
```
POST https://openapi.biji.com/open/api/v1/oauth/token
Content-Type: application/json
```
请求体:
```json
{
"grant_type": "device_code",
"client_id": "cli_a1b2c3d4e5f6789012345678abcdef90",
"code": "{code}"
}
```
**轮询策略**:
- **间隔**:5 秒查询一次
- **超时**:最多轮询 10 分钟(与授权码有效期一致)
- **并行**:轮询在后台进行,不阻塞用户其他操作
**推荐:使用轮询脚本**
```bash
# 方式 1:后台轮询 + process poll 等待结果(OpenClaw 推荐)
# exec 启动后台任务,然后用 process poll 等待完成
exec: python scripts/oauth_poll.py "{code}"
background: true
# 用 process poll 等待结果(最长等 10 分钟)
process: poll
sessionId: {上一步返回的 sessionId}
timeout: 600000
# 方式 2:简单等待(适合短时间)
result=$(python scripts/oauth_poll.py "{code}")
api_key=$(echo "$result" | jq -r '.api_key')
client_id=$(echo "$result" | jq -r '.client_id')
```
脚本会自动处理各种状态,成功时输出 JSON,失败时输出错误到 stderr。
**轮询响应状态**:
| 响应 | 说明 | 处理方式 |
|------|------|---------|
| `{"msg": "authorization_pending"}` | 用户尚未操作 | 继续轮询 |
| `{"msg": "rejected"}` | 用户拒绝授权 | **停止轮询**,告知用户已拒绝 |
| `{"msg": "expired_token"}` | 授权码已过期 | **停止轮询**,提示重新发起 |
| `{"msg": "already_consumed"}` | 授权码已使用 | **停止轮询**,可能已配置成功 |
| `{"api_key": "...", "client_id": "...", ...}` | **授权成功** | 进入步骤 4 |
**授权成功返回**:
```json
{
"success": true,
"data": {
"client_id": "cli_a1b2c3d4e5f6789012345678abcdef90",
"api_key": "gk_live_xxx",
"key_id": "abc123",
"expires_at": 1742000000
}
}
```
| 字段 | 说明 |
|------|------|
| client_id | 应用 ID |
| api_key | API Key,**写入配置文件** |
| key_id | Key ID(管理用) |
| expires_at | 过期时间戳(Unix 秒),有效期 1 年 |
### 步骤 4:写入配置
将获取的凭证写入 `~/.openclaw/openclaw.json`:
```json
{
"skills": {
"entries": {
"getnote": {
"apiKey": "{api_key}",
"env": {
"GETNOTE_CLIENT_ID": "{client_id}"
}
}
}
}
}
```
**告知用户**:
> ✅ Get笔记 配置完成!
>
> - API Key 有效期至 {expires_at 格式化日期}
> - 现在可以使用「记一下」「查笔记」等功能了
> - 如需注销授权,请访问:https://www.biji.com/openapi?tab=keys
don't have the plugin yet? install it then click "run inline in claude" again.