身份验证
获取 API 密钥每次调用都要在 Authorization 请求头中携带 Bearer 令牌。Sofia 在每次调用时都会读取令牌背后的权限:被移出 Workspace 的人或被撤销的授权,其令牌会在下一次请求时失效。
| 方式 | 适用于 | 令牌 |
|---|---|---|
| 个人密钥 | 自动化你自己的账户、脚本、智能体 | sofia_pk_live_…,到期前有效 |
| OAuth,授权码 + PKCE | 代表用户在其所选 Workspace 中操作的产品 | sofia_live_at_…,一小时,可续期 |
| OAuth,客户端凭据 | 你自己的服务器,用于自动化你拥有的 Workspace | sofia_live_at_…,一小时 |
个人密钥
在设置 › API › 个人访问中创建密钥。它会代表你在所有 Workspace 中操作:你指定的门店决定 Workspace,你在其中的角色决定密钥能做什么。
curl https://api.sofia-post.com/api/v1/establishments \
-H "Authorization: Bearer sofia_pk_live_…"OAuth:授权码 + PKCE
在设置 › API › 应用客户端中注册客户端:名称、Logo、隐私政策、可申请的权限以及 https 重定向 URL。机密(confidential)客户端有密钥;公开(public)客户端(移动应用、浏览器扩展)没有密钥,仅依靠 PKCE。
携带 PKCE 质询(必须使用 S256)和保存在用户会话中的 state,将用户引导到授权端点:
import { createHash, randomBytes } from 'node:crypto';
const verifier = randomBytes(32).toString('base64url');
const challenge = createHash('sha256').update(verifier).digest('base64url');
const state = randomBytes(16).toString('base64url');
const url = new URL('https://api.sofia-post.com/api/v1/oauth/authorize');
url.search = new URLSearchParams({
response_type: 'code',
client_id: process.env.SOFIA_CLIENT_ID,
redirect_uri: 'https://app.example.com/oauth/callback',
scope: 'establishments:read posts:read posts:write offline_access',
state,
code_challenge: challenge,
code_challenge_method: 'S256',
}).toString();用户登录 Sofia,看到你的名称、Logo 和权限,选择一个 Workspace 并同意。随后 Sofia 会带着 code、你的 state 和签发方 iss 将用户送回你的重定向 URL。校验 state 和 iss 后,再兑换授权码:
curl -X POST https://api.sofia-post.com/api/v1/oauth/token \
-u "$SOFIA_CLIENT_ID:$SOFIA_CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=$CODE \
-d redirect_uri=https://app.example.com/oauth/callback \
-d code_verifier=$VERIFIER{
"access_token": "sofia_live_at_…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "establishments:read posts:read posts:write offline_access",
"refresh_token": "sofia_live_rt_…"
}- 授权码有效期为 5 分钟,且只能使用一次。重复使用会被拒绝,并且它签发的令牌会失效。
- 访问令牌有效期为一小时,且仅对用户所选的 Workspace 生效。
- 只有在你申请了
offline_access时才会返回refresh_token。它只能使用一次:每次都要保存新的。重复使用已用过的刷新令牌会撤销整个授权。
curl -X POST https://api.sofia-post.com/api/v1/oauth/token \
-u "$SOFIA_CLIENT_ID:$SOFIA_CLIENT_SECRET" \
-d grant_type=refresh_token \
-d refresh_token=$REFRESH_TOKENOAuth:客户端凭据
机密客户端也可以在无需用户参与的情况下,对其所有者拥有的 Workspace 进行操作。当客户端持有多个 Workspace 时,请用 workspace_id 指定其中一个。
curl -X POST https://api.sofia-post.com/api/v1/oauth/token \
-u "$SOFIA_CLIENT_ID:$SOFIA_CLIENT_SECRET" \
-d grant_type=client_credentials \
-d workspace_id=$WORKSPACE_ID权限(scope)
| Scope | 允许 |
|---|---|
establishments:read | 列出门店 |
accounts:read, accounts:write | 读取已连接的网络;创建连接链接 |
media:read, media:write | 读取媒体库;上传和导入媒体 |
posts:read, posts:write | 列出和跟踪帖子;发布、编辑和删除帖子 |
insights:read | 读取统计数据 |
webhooks:read, webhooks:write | 列出 webhook;注册、测试和删除 webhook |
offline_access | 仅限 OAuth:接收刷新令牌 |
缺少所需 scope 的调用会返回 403 insufficient_scope。服务器元数据(RFC 8414)列出了所有端点和 scope:
https://api.sofia-post.com/.well-known/oauth-authorization-server/api/v1/oauth