接入文档
灵策中台接入指南:站内通知与文件直传
本页目录
接入灵策中台的产品(后台登记的「中台应用」)除了登录(统一登录)和收款(收款与 Webhook),还可以用两项公共能力:
- 站内通知:给登录过你的应用的灵策用户发一条站内信,用户在灵策网页版 / App 的消息中心看到,同时进邮件摘要;
- 文件直传:拿一个签名上传地址,浏览器或服务端直接把文件 PUT 到灵策的对象存储,不经过你的服务器带宽。
两项都只在你的服务端调用,鉴权和收款相同(client_id + client_secret,见 收款与 Webhook 第 2 节)。全部错误码见 错误码。
#1. 开通
找灵策运营(超级管理员)在后台「灵策中台 → 应用 → 能力」里打开「开通通知」「开通文件直传」。两项默认都关,没开时调用返回 403 center_notify_disabled / 403 center_files_disabled。开通前需要先生成应用密钥。
#2. 站内通知 POST /api/center/notify/messages
{
"userId": "cmvsym1j60002eu4x2y8qchxq",
"title": "订单已发货",
"body": "你在演示应用的订单 A-1001 已发货,预计明天送达。",
"eventKey": "order-shipped:A-1001"
}| 字段 | 说明 |
|---|---|
userId |
收件人的灵策账号 sub(用户登录你的应用时,id_token / userinfo 里的 sub)。只能发给授权过你的应用的用户:用户在你的应用完成过一次登录(换令牌)才算 |
title |
≤ 60 字。中台会自动加上「【你的应用名】」前缀,用户能分辨来源,也防止冒充平台公告 |
body |
≤ 2000 字,纯文本。不支持外链跳转 |
eventKey |
幂等键,1-100 位字母、数字或 _ . : -,在你的应用内唯一。网络错误时用同一个 eventKey 重试,不会重复发 |
返回:
{ "code": 0, "data": { "messageId": "…", "idempotent": false } }idempotent = true 表示这个 eventKey 之前已经发过,本次没有新发。
错误:
| 状态 | code | 说明 |
|---|---|---|
| 403 | user_not_authorized |
这个用户没有授权过你的应用(从没登录过)。未知的 userId 也是这个错误,不会告诉你账号是否存在 |
| 404 | user_not_found |
授权过,但账号已停用或注销 |
| 409 | event_key_conflict |
同一 eventKey 之前发给了另一个用户 |
| 429 | user_rate_limited |
这个用户 24 小时内收到你的应用的消息已达上限(默认 20 条) |
| 429 | rate_limited |
你的应用整体发送过快 |
请只发和用户本人相关的通知(订单、到账、审核结果等),不要群发营销内容。
#3. 文件直传 POST /api/center/files/presign
{ "contentType": "image/png", "sizeBytes": 204800, "visibility": "public" }visibility:public(默认,公共读)或 private(私有,见 3.1)。
返回:
{
"code": 0,
"data": {
"key": "center/demo/202610/3f0c…e1.png",
"uploadUrl": "https://…(签名地址,10 分钟有效)",
"method": "PUT",
"headers": { "Content-Type": "image/png", "x-oss-forbid-overwrite": "true" },
"publicUrl": "https://cdn.lynxce.cn/center/demo/202610/3f0c…e1.png",
"maxBytes": 20971520,
"expiresInSeconds": 600
}
}然后用 uploadUrl 直接上传,headers 必须原样带上(它们参与签名)。从浏览器直传时,对象存储要允许你的域名跨域 PUT:找灵策运营把你的站点域名加进存储桶的 CORS 规则(允许方法 PUT、允许头 Content-Type、x-oss-forbid-overwrite),否则浏览器会报跨域错误;服务端直传不受影响。
await fetch(upload.uploadUrl, { method: upload.method, headers: upload.headers, body: file });路径由中台生成,按应用隔离,不能自己指定;同一地址只能上传一次,不能覆盖;
支持的类型和单文件上限:
类型 上限 image/png、image/jpeg、image/webp、image/gif20MB application/pdf50MB text/plain、text/csv10MB audio/mpeg100MB video/mp4500MB 注意
public文件的publicUrl任何人都能访问。身份证、合同、聊天记录这类隐私文件请用private;不支持的类型 / 超出上限返回
400 file_type_not_allowed/400 file_too_large;存储未配置返回503 storage_unavailable。
#3.1 私有文件
visibility: "private" 的文件放在私有存储,返回里 publicUrl 为 null。要读取时,在服务端换一个短时下载地址:
POST /api/center/files/download-url
{ "key": "center/demo/202610/3f0c…e1.pdf", "expiresInSeconds": 600, "downloadFileName": "合同.pdf" }返回 { "downloadUrl": "…", "expiresAt": "…" }。expiresInSeconds 取 60-3600,默认 600。只能换你的应用自己上传的 key,别的 key 一律 404 file_not_found。下载地址谁拿到都能在有效期内下载,请只在用户确有权限时现换现给,不要长期保存。
#4. SDK
import { CenterServerClient } from "@ai-employee/center-sdk";
const center = new CenterServerClient({
baseUrl: "https://center.lynxce.cn",
clientId: process.env.LYNXCE_CLIENT_ID!,
clientSecret: process.env.LYNXCE_CLIENT_SECRET!,
});
await center.sendNotification({ userId: sub, title: "订单已发货", body: "…", eventKey: `order-shipped:${orderId}` });
const upload = await center.presignFileUpload({ contentType: file.type, sizeBytes: file.size });
// 把 upload 交给浏览器,由浏览器直传:fetch(upload.uploadUrl, { method: "PUT", headers: upload.headers, body: file })
const secret = await center.presignFileUpload({ contentType: "application/pdf", sizeBytes: file.size, visibility: "private" });
// …上传后,用户要看时:
const { downloadUrl } = await center.getFileDownloadUrl({ key: secret.key, expiresInSeconds: 300 });