开放 API 面向脚本、程序和大模型。用个人令牌查询、上传图片、发布和远程删除你自己的内容。先读完再调用,不要编造路径或字段。

说明

令牌在登录后的应用中心 → API 创建,明文只显示一次。身份由令牌决定,请求里不要传用户 ID、Cookie 或 CSRF。 应用中心 → API

给大模型:请读取 https://taabb.com/api/open.md ,或 JSON https://taabb.com/api/open/spec.json 。只用文档中的 4 个接口。有图片时必须先上传,再把返回的 path 写进正文的 <img src>。

你正在调用大白笔记开放 API。
站点: https://taabb.com
说明页: GET https://taabb.com/open-api
Markdown: GET https://taabb.com/api/open.md
JSON: GET https://taabb.com/api/open/spec.json
鉴权: Authorization: Bearer <令牌>(令牌即用户,不要传 uid)
只使用这 4 个接口:
  GET/POST https://taabb.com/api/open/exists
  POST      https://taabb.com/api/open/upload
  POST      https://taabb.com/api/open/publish
  POST      https://taabb.com/api/open/delete
响应信封: {success, data, code}。success 不是 true 就停止。
去重键: provider + external_id。重复发布会跳过。
图片: 先 multipart 上传,把返回的 data.path 写进 <img src="upload/post/....jpg">,再把整段 HTML 作为 post_content 发布。上传超时就用同一文件重试,服务端会返回已有路径;不要猜测 path,也不要把没成功的图写进正文。
post_type: 1=小记, 3=笔记(默认)。post_privacy: 0 公开, 1 关心的人, 2 仅自己。
不要编造接口。不要带 Cookie / CSRF。

规则

  1. 先读完本文再调用任何接口。
  2. 只调用下面列出的 4 个接口,不要编造路径或字段。
  3. 不要调用 /api/open/tokens 或其他站点接口。那些需要浏览器登录,不是这把令牌。
  4. 不要传 uid、user_id、用户名、Cookie、CSRF 或 Session。身份只有令牌。
  5. 始终检查 JSON 的 success。HTTP 200 时 success 仍可能是 false。
  6. provider + external_id 是去重键。同一对重复发布会跳过,返回已有 post_id。
  7. 有图片必须先调用 /api/open/upload,再把返回的 path 写进正文 <img src>。外链图片会被丢掉,列表也不会出现缩图。
  8. 不要把令牌写进正文或标题。

鉴权

  • 每个 exists / upload / publish / delete 请求都必须带令牌。
  • 不要带 Cookie、CSRF、uid、user_id。
  • 令牌以 tab_ 开头。发送完整令牌,不要只发前缀。
  • 优先:Authorization: Bearer <令牌>
  • 备选:X-Api-Token: <令牌>,或 POST/JSON 字段 api_token。
  • 缺少或无效令牌 → HTTP 403,success=false。

响应

所有接口返回同一形状。先看 success,不要只看 HTTP 状态码。

{
  "success": true,
  "data": {},
  "code": 200
}
情况 HTTP success 错误信息
鉴权失败 403 false data.message
业务失败 200 false data.message
成功 200 true 读 data

幂等

  • 键:provider(最长 64)+ external_id(最长 191),对每个用户唯一。
  • 给接入方选一个稳定的 provider 短名,例如 claude、gpt、my-bot。
  • external_id 用你自己的源条目 ID。重复发布同一对会跳过,不会覆盖正文。
  • 若帖子已被删,映射会清掉,exists 视为不存在。

流程

  1. 若需要去重:先 GET /api/open/exists。若 exists=true,不要再发,或先删除。
  2. 有图片:每张图单独 POST /api/open/upload,记下返回的 data.path。
  3. 把 path 拼进 HTML:<img src="upload/post/……jpg">,再作为 post_content 去 publish。
  4. 删除:POST /api/open/delete,传 post_id,或同一对 provider + external_id。

图片怎么进正文

上传接口只负责把图片存到服务器,不会自动出现在笔记里。你必须把返回的 path 写进 post_content 的 <img>,发布后才能在正文里看到图,列表里也才会有缩图。

  1. 调用 POST /api/open/upload,表单字段名用 upload_file(或 file),每次一张。
  2. 从 JSON 的 data.path 取出路径,形如 upload/post/202608/xxxx.jpg。推荐用 path,不要只用外站地址。
  3. 把路径写进 HTML:<img src="upload/post/202608/xxxx.jpg">。多张图就写多个 <img>,按你想出现的顺序排列。
  4. 把整段 HTML 作为 post_content,再调用 POST /api/open/publish。

发布成功后,服务端会扫描正文里所有 src 含 upload/post/ 的图片,把它们挂到这篇笔记,并生成列表卡片用的缩略图。你不需要、也不应该自己传缩略图地址。

上传超时、连接被断开、或没有返回 JSON:不要把这张图写进正文,也不要猜测 path。用同一文件再 POST /api/open/upload,直到 success=true 并拿到 data.path。同一文件重试会返回已有路径,不会再存一份。建议客户端超时设为 60–120 秒,一次只传一张。已经成功的图片可以继续用;失败的那张跳过或重试后再拼进 HTML。

post_content 示例(两张图夹在文字中间)

<p>今天路过海边。</p>
<p><img src="upload/post/202608/one.jpg"></p>
<p>晚上的灯也很好看。</p>
<p><img src="upload/post/202608/two.jpg"></p>
  • 只 upload 却不把 <img> 写进 post_content:笔记里没有图,列表也没有缩图。
  • 用其他网站的 https 图片地址:发布时会被清掉,同样没有图和缩图。
  • previewUrl 一般也能用,但请优先写 data.path。

例子:把一篇新浪新闻推到笔记

脚本把新浪新闻写入令牌主人的账号。作者由令牌决定,请求里不要传 uid、user_id 或用户名。

字段怎么对应

新闻 API 字段 本例
来源短名,自己定一个一直用 provider sina-news
文章稳定 ID,建议用 URL 里的 docid external_id doc-inh8example01
标题 post_title 台风路径北抬 东部沿海将有强风雨
正文 HTML post_content 段落 + 已上传图片的 <img src>
原文发布时间 post_time 2026-08-14 09:30:00
作者 不要传 uid 只带 Authorization: Bearer

调用顺序

  1. 先 exists。同一篇文章始终用同一对 provider + external_id。exists=true 说明已经推过,不要再发。
  2. 配图先下载到本地,再 POST /api/open/upload。不要把新浪图片的 https 地址写进 <img>,发布时会被清掉。
  3. 再 publish。不要传 uid。本例 post_privacy=2,只进自己的笔记;若要公开改成 0。

把 TOKEN 换成你的完整令牌。下面的 BASE 已是本站地址。JSON 里没有 uid。

可复制的 curl

TOKEN="tab_YOUR_TOKEN"
BASE="https://taabb.com"

# 1) 查重。同一篇文章用同一对 provider + external_id。不要传 uid。
curl -sS -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/open/exists?provider=sina-news&external_id=doc-inh8example01"

# 若 data.exists 为 true,这篇已经推过,停止。

# 2) 配图先下载到本地再上传。不要把新浪图片 https 地址写进 <img>。
curl -sS -X POST "$BASE/api/open/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "upload_file=@./cover.jpg"
# 记下返回的 data.path,例如 upload/post/202608/abcd1234.jpg

# 3) 发布。作者就是令牌的主人。JSON 里没有 uid。
curl -sS -X POST "$BASE/api/open/publish" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @- <<'EOF'
{
    "provider": "sina-news",
    "external_id": "doc-inh8example01",
    "post_title": "台风路径北抬 东部沿海将有强风雨",
    "post_type": 3,
    "post_privacy": 2,
    "post_time": "2026-08-14 09:30:00",
    "post_content": "<p>据中央气象台消息,台风路径北抬,东部沿海将有强风雨。</p><p><img src=\"upload/post/202608/abcd1234.jpg\"></p><p>原文:<a href=\"https://news.sina.com.cn/c/2026-08-14/doc-inh8example01.shtml\">新浪新闻</a></p>"
}
EOF

枚举

post_type

value含义
1小记。标题可空。
3笔记。默认值。建议带标题。

只允许 1 或 3。

post_privacy

value含义
0公开。默认。
1仅关心的人可见。
2仅自己可见。

只允许 0、1、2。不要传 3。

exists — GET or POST /api/open/exists

查询这对 provider + external_id 是否已经发布过。

  • URL:https://taabb.com/api/open/exists
  • 鉴权:必须
  • Body:GET query | application/x-www-form-urlencoded | application/json

Parameters

nameintyperequirednotes
provider query|body string 来源短名,最长 64。
external_id query|body string 你自己的条目 ID,最长 191。

Success data

{
    "exists": false,
    "post_id": 0
}
  • exists=true 时 post_id 为已有笔记;false 时 post_id 为 0。
  • 不会更新令牌最后使用时间。

示例 — GET

curl -sS -H "Authorization: Bearer tab_YOUR_TOKEN" "https://taabb.com/api/open/exists?provider=demo&external_id=abc-1"

示例 — POST JSON

curl -sS -X POST "https://taabb.com/api/open/exists" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"demo","external_id":"abc-1"}'

upload — POST /api/open/upload

上传一张图片。只是存文件,还不会出现在笔记里。

  • URL:https://taabb.com/api/open/upload
  • 鉴权:必须
  • Body:multipart/form-data

Parameters

nameintyperequirednotes
upload_file file file 推荐字段名。
file file file 没有 upload_file 时可用。

Success data

{
    "message": "上传成功",
    "path": "upload/post/202608/example.jpg",
    "previewUrl": "https://taabb.com/upload/post/202608/example.jpg",
    "width": 1200,
    "height": 800
}
  • 必须 multipart,不支持 JSON 传文件。
  • 一次一张。多张就请求多次。
  • 请把返回的 data.path 写进稍后发布的 <img src>。
  • 超时或中断时用同一文件重试这一张;服务端会返回已有路径,不会重复存文件。不要把未成功的图写进正文。

示例 — multipart

curl -sS -X POST "https://taabb.com/api/open/upload" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -F "upload_file=@/path/to/photo.jpg"

publish — POST /api/open/publish

为令牌所属用户创建一篇内容。同一对 provider+external_id 会跳过,不会覆盖。

  • URL:https://taabb.com/api/open/publish
  • 鉴权:必须
  • Body:application/x-www-form-urlencoded | application/json

Parameters

nameintyperequirednotes
provider body string provider
external_id body string external_id
post_content body string (HTML) HTML
post_title body string max 200
post_type body integer 1 | 3, default 3
post_privacy body integer 0 | 1 | 2, default 0
post_time body string datetime

Success data

{
    "message": "发布成功",
    "skipped": false,
    "exists": false,
    "post_id": 12345
}
  • 已存在时:success=true,skipped=true。
  • 不要传 uid。作者就是令牌的主人。
  • 正文里 src 含 upload/post/ 的 <img> 会自动挂附件并生成缩图。

示例 — 带图片发布

curl -sS -X POST "https://taabb.com/api/open/upload" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -F "upload_file=@./one.jpg"
# 记下返回的 data.path,例如 upload/post/202608/one.jpg

curl -sS -X POST "https://taabb.com/api/open/publish" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  --data-urlencode "provider=demo" \
  --data-urlencode "external_id=abc-1" \
  --data-urlencode "post_title=标题" \
  --data-urlencode "post_content=<p>正文</p><p><img src=\"upload/post/202608/one.jpg\"></p>"

示例 — JSON

curl -sS -X POST "https://taabb.com/api/open/publish" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"demo","external_id":"abc-1","post_title":"标题","post_type":3,"post_privacy":2,"post_content":"<p>正文</p><p><img src=\"upload/post/202608/one.jpg\"></p>"}'

delete — POST /api/open/delete

删除令牌主人的笔记。传 post_id,或传 provider + external_id。

  • URL:https://taabb.com/api/open/delete
  • 鉴权:必须
  • Body:application/x-www-form-urlencoded | application/json

Parameters

nameintyperequirednotes
post_id body integer post_id
provider body string provider
external_id body string external_id

Success data

{
    "message": "删除成功",
    "post_id": 12345
}
  • 令牌的「最后使用」只在真正发布成功(非 skip)或删除成功时更新。exists / 上传 / 跳过发布不算。

示例 — JSON

curl -sS -X POST "https://taabb.com/api/open/delete" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"demo","external_id":"abc-1"}'

HTML 约定

  • post_content 是 HTML。净化后为空会失败。
  • 允许常见文本标签、链接、列表、已上传图片的 <img>、checkbox。
  • 禁止并会去掉:script、iframe、style、textarea、object、embed。
  • JSON 请求体上限 1MB;大 HTML 用 application/x-www-form-urlencoded。
  • post_title 最长 200,纯文本。post_time 例如 2026-08-14 15:00:00,空则取当前时间。

不要调用

  • GET|POST /api/open/tokens
  • POST /api/open/tokens/delete
  • Any /api/* path not listed in endpoints
  • Browser publish /post/add
  • uid, specify_users, post_pass, diary_book_id, topics

令牌的「最后使用」只在真正发布成功(非 skip)或删除成功时更新。exists / 上传 / 跳过发布不算。

规范原文

与下面 Markdown 相同。公开、无需登录、不含密钥。 https://taabb.com/api/open.md

# Taabb Open API

SPEC_VERSION: 1.0
LANG: zh
AUDIENCE: language-model agents and HTTP clients
BASE_URL: https://taabb.com
CANONICAL_HTML: https://taabb.com/open-api
CANONICAL_MARKDOWN: https://taabb.com/api/open.md
CANONICAL_JSON: https://taabb.com/api/open/spec.json

本接口把内容发到令牌主人的大白笔记账号。请完整阅读,不要猜测缺失字段。

## 0. Agent brief

```
你正在调用大白笔记开放 API。
站点: https://taabb.com
说明页: GET https://taabb.com/open-api
Markdown: GET https://taabb.com/api/open.md
JSON: GET https://taabb.com/api/open/spec.json
鉴权: Authorization: Bearer <令牌>(令牌即用户,不要传 uid)
只使用这 4 个接口:
  GET/POST https://taabb.com/api/open/exists
  POST      https://taabb.com/api/open/upload
  POST      https://taabb.com/api/open/publish
  POST      https://taabb.com/api/open/delete
响应信封: {success, data, code}。success 不是 true 就停止。
去重键: provider + external_id。重复发布会跳过。
图片: 先 multipart 上传,把返回的 data.path 写进 <img src="upload/post/....jpg">,再把整段 HTML 作为 post_content 发布。上传超时就用同一文件重试,服务端会返回已有路径;不要猜测 path,也不要把没成功的图写进正文。
post_type: 1=小记, 3=笔记(默认)。post_privacy: 0 公开, 1 关心的人, 2 仅自己。
不要编造接口。不要带 Cookie / CSRF。
```

## 1. 规则

1. 先读完本文再调用任何接口。
2. 只调用下面列出的 4 个接口,不要编造路径或字段。
3. 不要调用 /api/open/tokens 或其他站点接口。那些需要浏览器登录,不是这把令牌。
4. 不要传 uid、user_id、用户名、Cookie、CSRF 或 Session。身份只有令牌。
5. 始终检查 JSON 的 success。HTTP 200 时 success 仍可能是 false。
6. provider + external_id 是去重键。同一对重复发布会跳过,返回已有 post_id。
7. 有图片必须先调用 /api/open/upload,再把返回的 path 写进正文 <img src>。外链图片会被丢掉,列表也不会出现缩图。
8. 不要把令牌写进正文或标题。

## 2. 鉴权

- 每个 exists / upload / publish / delete 请求都必须带令牌。
- 不要带 Cookie、CSRF、uid、user_id。
- 令牌以 tab_ 开头。发送完整令牌,不要只发前缀。
- 优先:Authorization: Bearer <令牌>
- 备选:X-Api-Token: <令牌>,或 POST/JSON 字段 api_token。
- 缺少或无效令牌 → HTTP 403,success=false。

## 3. 响应

所有接口返回同一形状。先看 success,不要只看 HTTP 状态码。

```json
{
  "success": true,
  "data": {},
  "code": 200
}
```

## 4. 幂等

- 键:provider(最长 64)+ external_id(最长 191),对每个用户唯一。
- 给接入方选一个稳定的 provider 短名,例如 claude、gpt、my-bot。
- external_id 用你自己的源条目 ID。重复发布同一对会跳过,不会覆盖正文。
- 若帖子已被删,映射会清掉,exists 视为不存在。

## 5. 流程

- 若需要去重:先 GET /api/open/exists。若 exists=true,不要再发,或先删除。
- 有图片:每张图单独 POST /api/open/upload,记下返回的 data.path。
- 把 path 拼进 HTML:<img src="upload/post/……jpg">,再作为 post_content 去 publish。
- 删除:POST /api/open/delete,传 post_id,或同一对 provider + external_id。

## 6. 图片怎么进正文

上传接口只负责把图片存到服务器,不会自动出现在笔记里。你必须把返回的 path 写进 post_content 的 <img>,发布后才能在正文里看到图,列表里也才会有缩图。

1. 调用 POST /api/open/upload,表单字段名用 upload_file(或 file),每次一张。
2. 从 JSON 的 data.path 取出路径,形如 upload/post/202608/xxxx.jpg。推荐用 path,不要只用外站地址。
3. 把路径写进 HTML:<img src="upload/post/202608/xxxx.jpg">。多张图就写多个 <img>,按你想出现的顺序排列。
4. 把整段 HTML 作为 post_content,再调用 POST /api/open/publish。

发布成功后,服务端会扫描正文里所有 src 含 upload/post/ 的图片,把它们挂到这篇笔记,并生成列表卡片用的缩略图。你不需要、也不应该自己传缩略图地址。

上传超时、连接被断开、或没有返回 JSON:不要把这张图写进正文,也不要猜测 path。用同一文件再 POST /api/open/upload,直到 success=true 并拿到 data.path。同一文件重试会返回已有路径,不会再存一份。建议客户端超时设为 60–120 秒,一次只传一张。已经成功的图片可以继续用;失败的那张跳过或重试后再拼进 HTML。

post_content 示例(两张图夹在文字中间):

```html
<p>今天路过海边。</p>
<p><img src="upload/post/202608/one.jpg"></p>
<p>晚上的灯也很好看。</p>
<p><img src="upload/post/202608/two.jpg"></p>
```

- 只 upload 却不把 <img> 写进 post_content:笔记里没有图,列表也没有缩图。
- 用其他网站的 https 图片地址:发布时会被清掉,同样没有图和缩图。
- previewUrl 一般也能用,但请优先写 data.path。

## 7. 例子:把一篇新浪新闻推到笔记

脚本把新浪新闻写入令牌主人的账号。作者由令牌决定,请求里不要传 uid、user_id 或用户名。

### 字段怎么对应

| 新闻 | API 字段 | 本例 |
| --- | --- | --- |
| 来源短名,自己定一个一直用 | `provider` | sina-news |
| 文章稳定 ID,建议用 URL 里的 docid | `external_id` | doc-inh8example01 |
| 标题 | `post_title` | 台风路径北抬 东部沿海将有强风雨 |
| 正文 HTML | `post_content` | 段落 + 已上传图片的 <img src> |
| 原文发布时间 | `post_time` | 2026-08-14 09:30:00 |
| 作者 | `不要传 uid` | 只带 Authorization: Bearer |

### 调用顺序

1. 先 exists。同一篇文章始终用同一对 provider + external_id。exists=true 说明已经推过,不要再发。
2. 配图先下载到本地,再 POST /api/open/upload。不要把新浪图片的 https 地址写进 <img>,发布时会被清掉。
3. 再 publish。不要传 uid。本例 post_privacy=2,只进自己的笔记;若要公开改成 0。

把 TOKEN 换成你的完整令牌。下面的 BASE 已是本站地址。JSON 里没有 uid。

```bash
TOKEN="tab_YOUR_TOKEN"
BASE="https://taabb.com"

# 1) 查重。同一篇文章用同一对 provider + external_id。不要传 uid。
curl -sS -H "Authorization: Bearer $TOKEN" \
  "$BASE/api/open/exists?provider=sina-news&external_id=doc-inh8example01"

# 若 data.exists 为 true,这篇已经推过,停止。

# 2) 配图先下载到本地再上传。不要把新浪图片 https 地址写进 <img>。
curl -sS -X POST "$BASE/api/open/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "upload_file=@./cover.jpg"
# 记下返回的 data.path,例如 upload/post/202608/abcd1234.jpg

# 3) 发布。作者就是令牌的主人。JSON 里没有 uid。
curl -sS -X POST "$BASE/api/open/publish" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d @- <<'EOF'
{
    "provider": "sina-news",
    "external_id": "doc-inh8example01",
    "post_title": "台风路径北抬 东部沿海将有强风雨",
    "post_type": 3,
    "post_privacy": 2,
    "post_time": "2026-08-14 09:30:00",
    "post_content": "<p>据中央气象台消息,台风路径北抬,东部沿海将有强风雨。</p><p><img src=\"upload/post/202608/abcd1234.jpg\"></p><p>原文:<a href=\"https://news.sina.com.cn/c/2026-08-14/doc-inh8example01.shtml\">新浪新闻</a></p>"
}
EOF
```

## 8. 枚举

### post_type

| value | 含义 |
| --- | --- |
| 1 | 小记。标题可空。 |
| 3 | 笔记。默认值。建议带标题。 |

只允许 1 或 3。

### post_privacy

| value | 含义 |
| --- | --- |
| 0 | 公开。默认。 |
| 1 | 仅关心的人可见。 |
| 2 | 仅自己可见。 |

只允许 0、1、2。不要传 3。

## 9. Endpoints

| method | path |
| --- | --- |
| GET or POST | `/api/open/exists` |
| POST | `/api/open/upload` |
| POST | `/api/open/publish` |
| POST | `/api/open/delete` |

### exists — GET or POST /api/open/exists

查询这对 provider + external_id 是否已经发布过。

- URL: `https://taabb.com/api/open/exists`
- 鉴权:必须
- Body: GET query | application/x-www-form-urlencoded | application/json

| name | in | type | required | notes |
| --- | --- | --- | --- | --- |
| `provider` | query|body | string | 是 | 来源短名,最长 64。 |
| `external_id` | query|body | string | 是 | 你自己的条目 ID,最长 191。 |

```json
{
    "exists": false,
    "post_id": 0
}
```

- exists=true 时 post_id 为已有笔记;false 时 post_id 为 0。
- 不会更新令牌最后使用时间。

示例 — GET:

```bash
curl -sS -H "Authorization: Bearer tab_YOUR_TOKEN" "https://taabb.com/api/open/exists?provider=demo&external_id=abc-1"
```

示例 — POST JSON:

```bash
curl -sS -X POST "https://taabb.com/api/open/exists" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"demo","external_id":"abc-1"}'
```

### upload — POST /api/open/upload

上传一张图片。只是存文件,还不会出现在笔记里。

- URL: `https://taabb.com/api/open/upload`
- 鉴权:必须
- Body: multipart/form-data

| name | in | type | required | notes |
| --- | --- | --- | --- | --- |
| `upload_file` | file | file | 是 | 推荐字段名。 |
| `file` | file | file | 否 | 没有 upload_file 时可用。 |

```json
{
    "message": "上传成功",
    "path": "upload/post/202608/example.jpg",
    "previewUrl": "https://taabb.com/upload/post/202608/example.jpg",
    "width": 1200,
    "height": 800
}
```

- 必须 multipart,不支持 JSON 传文件。
- 一次一张。多张就请求多次。
- 请把返回的 data.path 写进稍后发布的 <img src>。
- 超时或中断时用同一文件重试这一张;服务端会返回已有路径,不会重复存文件。不要把未成功的图写进正文。

示例 — multipart:

```bash
curl -sS -X POST "https://taabb.com/api/open/upload" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -F "upload_file=@/path/to/photo.jpg"
```

### publish — POST /api/open/publish

为令牌所属用户创建一篇内容。同一对 provider+external_id 会跳过,不会覆盖。

- URL: `https://taabb.com/api/open/publish`
- 鉴权:必须
- Body: application/x-www-form-urlencoded | application/json

| name | in | type | required | notes |
| --- | --- | --- | --- | --- |
| `provider` | body | string | 是 | provider |
| `external_id` | body | string | 是 | external_id |
| `post_content` | body | string (HTML) | 是 | HTML |
| `post_title` | body | string | 否 | max 200 |
| `post_type` | body | integer | 否 | 1 \| 3, default 3 |
| `post_privacy` | body | integer | 否 | 0 \| 1 \| 2, default 0 |
| `post_time` | body | string | 否 | datetime |

```json
{
    "message": "发布成功",
    "skipped": false,
    "exists": false,
    "post_id": 12345
}
```

- 已存在时:success=true,skipped=true。
- 不要传 uid。作者就是令牌的主人。
- 正文里 src 含 upload/post/ 的 <img> 会自动挂附件并生成缩图。

示例 — 带图片发布:

```bash
curl -sS -X POST "https://taabb.com/api/open/upload" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -F "upload_file=@./one.jpg"
# 记下返回的 data.path,例如 upload/post/202608/one.jpg

curl -sS -X POST "https://taabb.com/api/open/publish" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  --data-urlencode "provider=demo" \
  --data-urlencode "external_id=abc-1" \
  --data-urlencode "post_title=标题" \
  --data-urlencode "post_content=<p>正文</p><p><img src=\"upload/post/202608/one.jpg\"></p>"
```

示例 — JSON:

```bash
curl -sS -X POST "https://taabb.com/api/open/publish" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"demo","external_id":"abc-1","post_title":"标题","post_type":3,"post_privacy":2,"post_content":"<p>正文</p><p><img src=\"upload/post/202608/one.jpg\"></p>"}'
```

### delete — POST /api/open/delete

删除令牌主人的笔记。传 post_id,或传 provider + external_id。

- URL: `https://taabb.com/api/open/delete`
- 鉴权:必须
- Body: application/x-www-form-urlencoded | application/json

| name | in | type | required | notes |
| --- | --- | --- | --- | --- |
| `post_id` | body | integer | 否 | post_id |
| `provider` | body | string | 否 | provider |
| `external_id` | body | string | 否 | external_id |

```json
{
    "message": "删除成功",
    "post_id": 12345
}
```

- 令牌的「最后使用」只在真正发布成功(非 skip)或删除成功时更新。exists / 上传 / 跳过发布不算。

示例 — JSON:

```bash
curl -sS -X POST "https://taabb.com/api/open/delete" \
  -H "Authorization: Bearer tab_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"provider":"demo","external_id":"abc-1"}'
```

## 10. HTML 约定

- post_content 是 HTML。净化后为空会失败。
- 允许常见文本标签、链接、列表、已上传图片的 <img>、checkbox。
- 禁止并会去掉:script、iframe、style、textarea、object、embed。
- JSON 请求体上限 1MB;大 HTML 用 application/x-www-form-urlencoded。
- post_title 最长 200,纯文本。post_time 例如 2026-08-14 15:00:00,空则取当前时间。

## 11. 不要调用

- `GET|POST /api/open/tokens`
- `POST /api/open/tokens/delete`
- `Any /api/* path not listed in endpoints`
- `Browser publish /post/add`
- `uid, specify_users, post_pass, diary_book_id, topics`

令牌的「最后使用」只在真正发布成功(非 skip)或删除成功时更新。exists / 上传 / 跳过发布不算。