# 聊天服务 API 参考

本页列出当前聊天服务源码的 HTTP 契约，包括星线 App 使用的个人业务接口、独立 MLS 接口及共享后台中属于星企的企业接口。基地址为 `https://chat.thestarline.cn`。路由存在仅代表代码实现，不能证明生产依赖已经配置、客户端已经接入或真实设备路径已经验收。

## 通用约定

除 `GET /health`、`GET /auth/start`、`POST /auth/native/start`、`GET /auth/callback` 和 `POST /auth/exchange` 外，以下接口均要求 `Authorization: Bearer <chat_access_token>`。这是聊天服务签发的 token，不能用 Account access token、OIDC ID token 或 LiveKit 媒体 token 替代。星企接口还要求相应企业成员和角色权限。

JSON 请求使用 `Content-Type: application/json`，根必须为对象，默认请求体上限为 16,384 字节；本文标注的接口有单独上限。二进制上传直接发送文件字节，不是 multipart。JSON 响应为 UTF-8，使用 `Cache-Control: no-store` 与 `X-Content-Type-Options: nosniff`。媒体下载使用单独响应头。

时间值是 Unix 毫秒时间戳。UUID 字段用字符串；普通消息、事件、新事、评论等 ID 为安全整数；企业消息 ID 对外为字符串。分页游标均作用于指定资源，不能互换。下表中的 `{id}` 表示路径参数，不含字面花括号。

错误响应统一为：

```json
{"error":{"code":"INVALID_CURSOR","message":"游标无效"}}
```

| HTTP 状态 | 常见代码与含义 |
| --- | --- |
| 400 | INVALID_JSON、INVALID_CURSOR、INVALID_MESSAGE 等；请求字段或格式无效 |
| 401 | UNAUTHORIZED、INVALID_TICKET；登录失效或票据证明无效 |
| 403 | FORBIDDEN、CONTACT_BLOCKED、NOT_FRIENDS、UNKNOWN_DEVICE；无权执行 |
| 404 | NOT_FOUND、PROFILE_NOT_FOUND、NOT_MEMBER；资源不存在或当前不可访问 |
| 409 | CLIENT_ID_REUSED、E2EE_REQUIRED、CALL_BUSY、CALL_STATE 及 MLS 状态冲突 |
| 413 | TOO_LARGE、STORAGE_LIMIT；单次请求或账号配额超限 |
| 415 | CONTENT_TYPE；JSON 接口未发送 JSON 内容类型 |
| 416 | INVALID_RANGE；新事媒体 Range 无效 |
| 429 | TOO_MANY_LOOKUPS、TOO_MANY_CODES、TOO_MANY_RECOGNITIONS |
| 503 | LOGIN_UNAVAILABLE、E2EE_UNAVAILABLE、CALL_MEDIA_UNAVAILABLE、MEMORY_MODEL_UNAVAILABLE 等依赖不可用 |
| 500 | INTERNAL；未映射的服务异常 |

错误码必须与 HTTP 状态和具体路由一起判断。例如 NOT_FRIENDS 在联系动作中可为403，而建群/邀请成员返回400，邀请还可能是总人数超过50人；不能只按代码名推断唯一原因。详见[限制与错误](https://document.thestarline.cn/app/limits-and-errors.md)。

未支持的路由通常为 404，不保证返回 405。`/__preview/enterprise/session` 被明确禁用，始终返回 404，不是登录或测试会话入口。

## 健康与授权

这些接口无需聊天 Bearer token；授权完成后才获得业务会话。完整信任与到期语义见[登录与会话](https://document.thestarline.cn/app/authentication.md)。

| Method 与路径 | 参数 | 成功响应与限制 |
| --- | --- | --- |
| GET `/health` | 无 | 200 `{status:"ok"}`；仅 HTTP 存活性，不检查全部下游 |
| GET `/auth/start` | query：app 可选 starline/xingqi；screen=register 可进入注册 | 302 Account 授权地址；兼容流程约 40 分钟，原生客户端应使用下一接口 |
| POST `/auth/native/start` | JSON：app 默认 starline；codeChallenge 必填，规范 43 字符 base64url；screen 可选 login/register | 200 `{authorizationURL,state,expiresAt}`；flow 10 分钟，Account PKCE 另由服务端生成 |
| GET `/auth/callback` | Account 回传 query：state、code；授权失败可含 error | 302 App 返回 URL，携带 ticket 与原生 state，或 error=login_failed；正式地址 `https://chat.thestarline.cn/auth/callback` |
| POST `/auth/exchange` | JSON：ticket 字符串最多 128；原生必须另有 state、codeVerifier，verifier 为 43–128 字符 base64url | 200 `{accessToken,expiresAt,user,eventCursor}`；ticket 60 秒且一次性；session 默认 24 小时 |
| POST `/auth/logout` | Bearer；无需业务字段 | 200 `{status:"signed_out"}`；撤销当前聊天 session |

native/start 的 app 不合法返回 INVALID_APP，挑战错误返回 INVALID_CHALLENGE，screen 错误返回 INVALID_SCREEN。服务端 Account token 交换使用机密客户端认证与服务端 PKCE verifier，App 不发送客户端 secret。原生 proof 失败不会签发会话；兼容票据不能携带原生 proof 伪装成新流程。两端本机退出都可能忽略远端logout失败，本机已清除不证明服务端撤销；Account密码变化也不自动撤销独立聊天session。

以下仅示意请求形状。占位挑战、ticket、state 和 verifier 必须由同一次测试授权流程产生，不能照抄为生产凭据：

```sh
curl -X POST 'https://chat.thestarline.cn/auth/native/start' \
  -H 'Content-Type: application/json' \
  --data '{"app":"starline","screen":"login","codeChallenge":"<S256_BASE64URL_43>"}'

curl -X POST 'https://chat.thestarline.cn/auth/exchange' \
  -H 'Content-Type: application/json' \
  --data '{"ticket":"<ONE_USE_TICKET>","state":"<SAME_STATE>","codeVerifier":"<LOCAL_VERIFIER>"}'
```

## 当前用户与资料

所有接口要求 Bearer。`User` 形状为 `{id,displayName,friendId}`。`Profile` 形状为 `{user,bio,contactPhone}`，本人读取时另含 phoneVisibility、visibleToUserIds；不可见手机号为 null。

| Method 与路径 | 参数与权限 | 成功响应 |
| --- | --- | --- |
| GET `/me` | 当前 session | 200 `{user,eventCursor}` |
| GET `/profiles/{userId}` | 目标用户 UUID；按当前用户可见性过滤联系方式 | 200 `{profile}`；用户不存在为 PROFILE_NOT_FOUND |
| PUT `/me/profile` | bio 必填字符串，trim 后 ≤160 字；contactPhone 必填字符串，空字符串清除；phoneVisibility 必填 private/selected/friends/everyone；visibleToUserIds 默认 [] | 200 `{profile}`；指定可见用户最多 100 位、不得重复，必须当前好友，非 selected 不得传非空列表 |
| GET `/blocks` | 当前用户 | 200 `{users:[User...]}` |
| PUT `/blocks/{userId}` | 不能是自己；目标需存在 | 200 `{status:"blocked",conversationId}`；有进行中的单聊通话时先处理媒体与通话状态 |
| DELETE `/blocks/{userId}` | 目标需存在，不能是自己 | 200 `{status:"unblocked",conversationId}` |

手机号删除空格与短横线，11 位中国大陆手机号补 `+86`，其他形式需符合 `+` 开头的 8–15 位国际号码。资料手机号可见性不等于 Account 安全手机号设置。错误包括 INVALID_BIO、INVALID_PHONE、INVALID_VISIBILITY、INVALID_VIEWERS、SELF_BLOCK；屏蔽中媒体服务失败可返回 CALL_MEDIA_UNAVAILABLE。

## 好友与单聊

### 好友建立

| Method 与路径 | 请求或查询 | 成功响应与限制 |
| --- | --- | --- |
| POST `/friend-codes` | JSON `{}` | 201 `{code,expiresAt}`；12 位十六进制临时码，10 分钟；生成新码替换旧码；每用户每 10 分钟最多 10 次生成 |
| GET `/friend-requests` | 无 | 200 `{incoming:[{id,peer,createdAt}],outgoing:[...]}` |
| POST `/friend-requests` | JSON 中 friendId、phone、temporaryCode 三种字符串恰选一种 | 新申请 201、已有同向申请 200；`{status,peer,requestId}` |
| POST `/friend-requests/{requestId}/accept` | 当前用户必须为接收者 | 200 `{status:"accepted",conversationId}` |
| DELETE `/friend-requests/{requestId}` | 当前用户必须为申请双方之一 | 200 `{status:"deleted"}` |
| DELETE `/friends/{conversationId}` | 路径是会话 UUID，当前仍是好友；不是用户 UUID | 200 `{status:"deleted"}`；处理进行中的通话、好友关系与加密阻塞 |

friendId 去空格/短横线并转大写后须 11 位字母数字；temporaryCode 同样规范化后须 12 位 A–F/0–9。手机号和临时码查找分别按用户、方法每 10 分钟最多 20 次。错误包括 INVALID_INPUT、INVALID_FRIEND_ID、INVALID_FRIEND_CODE、SELF_FRIEND、ALREADY_FRIENDS、INCOMING_REQUEST、CONTACT_BLOCKED 和 TOO_MANY_LOOKUPS。未知或过期临时码返回 NOT_FOUND；收到对方申请时应处理 incoming，而不是重复反向申请。

### 单聊列表与消息

| Method 与路径 | 参数与行为 | 成功响应 |
| --- | --- | --- |
| GET `/conversations` | query includeHidden=1 可包含本人隐藏聊天 | 200 `{conversations:[...]}` |
| DELETE `/conversations/{conversationId}` | 会话参与者；推进本人历史边界并隐藏 | 200 `{status:"deleted"}` |
| POST `/conversations/{conversationId}/restore` | 会话参与者；只取消隐藏 | 200 `{status:"restored"}` |
| GET `/conversations/{conversationId}/messages` | query after 默认0；before 可选；limit 默认50、1–100；非零 after 与 before 不可同时使用 | 200 `{messages:[...]}`，按消息 ID 升序 |
| POST `/conversations/{conversationId}/messages` | JSON clientId UUID；text 与 mediaId 恰选一个；当前好友、未屏蔽且模式为 legacy | 首次 201，重复 200，`{message}` |
| DELETE `/conversations/{conversationId}/messages/{messageId}` | 会话参与者；只对本人隐藏指定消息 | 200 `{status:"deleted"}` |
| POST `/conversations/{conversationId}/read` | JSON `{lastMessageId}`；非负安全整数且必须是本会话消息 | 200 `{status:"read"}`，已读位置只前进 |

会话对象含 id、peer、lastMessage、unreadCount、peerReadMessageId、createdAt、isFriend、isBlocked、isHidden。普通消息含 id、senderId、clientId、body、media、createdAt；发送回执另含 conversationId、duplicate。media 不存在时为 null。文字 trim 后须非空且 ≤2000 Unicode 码点；媒体必须属于当前发送者。

相同发送者的 clientId 在普通单聊消息表中唯一；不同会话、正文或媒体复用同一 ID 返回 CLIENT_ID_REUSED。preparing/active/blocked 的 MLS 会话拒绝普通明文发送并返回 E2EE_REQUIRED。读取仍受个人历史边界和隐藏消息过滤。恢复聊天不恢复被历史边界排除的内容。

```sh
CHAT_TOKEN='<AUTHORIZED_TEST_SESSION>'
CONVERSATION_ID='11111111-1111-4111-8111-111111111111'
curl "https://chat.thestarline.cn/conversations/${CONVERSATION_ID}/messages?limit=50" \
  -H "Authorization: Bearer ${CHAT_TOKEN}"
curl -X POST "https://chat.thestarline.cn/conversations/${CONVERSATION_ID}/messages" \
  -H "Authorization: Bearer ${CHAT_TOKEN}" -H 'Content-Type: application/json' \
  --data '{"clientId":"22222222-2222-4222-8222-222222222222","text":"这是合成测试消息"}'
```

示例 UUID 是合成值，必须换成测试账号实际有权访问的范围；发送属于写操作，请仅在授权测试会话中使用。

## 个人群聊

企业频道使用另一组 enterprise 路由，不能与以下个人群互换。

| Method 与路径 | 请求或查询、权限 | 成功响应 |
| --- | --- | --- |
| POST `/groups` | JSON name trim 后1–40字；memberIds 为1–49个不同好友 UUID，不含自己 | 201 `{group}`；加发起者后最多50人 |
| GET `/groups` | query includeHidden=1 | 200 `{groups:[...]}` |
| GET `/groups/{groupId}` | 当前群成员 | 200 `{group,members:[User...]}` |
| DELETE `/groups/{groupId}` | 当前群成员，删除本人视图并推进历史边界 | 200 `{status:"deleted"}` |
| POST `/groups/{groupId}/restore` | 当前群成员 | 200 `{status:"restored"}` |
| POST `/groups/{groupId}/mute` | 当前群成员；JSON `{muted:boolean}` | 200 `{group}` |
| POST `/groups/{groupId}/name` | 群主；JSON name为1–40字 | 200 `{group}` |
| POST `/groups/{groupId}/members` | 群主；JSON memberIds为1–49个不同好友UUID、不含自己；总人数≤50 | 200 `{group,members}`；已有加密范围进入blocked |
| DELETE `/groups/{groupId}/members/{userId}` | 群主；不能移除自己；目标必须为成员 | 200 `{group,members}`；有通话先清理媒体与状态 |
| POST `/groups/{groupId}/leave` | 当前成员 | 200 `{status:"left"}`；有通话处理成员变化；加密范围blocked |
| POST `/groups/{groupId}/disband` | 群主 | 200 `{status:"disbanded"}`；解散群与相关数据 |
| GET `/groups/{groupId}/messages` | 当前成员；query before可选，limit默认50、1–100 | 200 `{messages:[...]}`，升序；不支持单聊的after增量契约 |
| POST `/groups/{groupId}/messages` | 当前成员且legacy；JSON clientId、text/mediaId与单聊相同 | 201/200 `{message}`，返回groupId及duplicate |
| DELETE `/groups/{groupId}/messages/{messageId}` | 当前成员；对本人隐藏 | 200 `{status:"deleted"}` |
| POST `/groups/{groupId}/read` | JSON lastMessageId必须属于本群 | 200 `{status:"read"}` |

group 对象含 id、name、ownerId、createdAt、memberCount、lastMessage、unreadCount、allReadMessageId、isMuted、isHidden。群消息额外含 senderDisplayName。常见错误是 INVALID_GROUP_NAME、INVALID_GROUP_MEMBERS、NOT_FRIENDS、FORBIDDEN、SELF_REMOVE、INVALID_MESSAGE、CLIENT_ID_REUSED、E2EE_REQUIRED；媒体处理失败可使成员操作返回503。

## 聊天文件、贴纸与城市

| Method 与路径 | 请求、权限和限制 | 成功响应 |
| --- | --- | --- |
| POST `/media` | query kind=file/sticker、name；Content-Type为实际MIME；body原始字节 | 201 `{media}` |
| GET `/media/{mediaId}` | 所有者或当前可见消息的接收用户 | 200 原始字节；attachment下载；private缓存3600秒；无Range契约 |
| GET `/stickers` | 当前用户 | 200 `{stickers:[Media...]}`；仅自定义贴纸 |
| POST `/stickers/city` | JSON恰有一个字段cityId，须来自随客户端分发的城市目录 | 200 `{media}`；按当前账号与城市素材去重 |
| GET `/location/city` | 无业务参数；按可信请求来源粗略定位 | 200 `{city:string或null,countryCode:string或null,source:"ip"}` |

`Media={id,kind,name,mimeType,size,createdAt}`。file最大10 MiB，sticker最大512 KiB；账号聊天媒体总量100 MiB，自定义贴纸最多100个。文件名trim后1–120码点，禁止控制字符与路径分隔符。MIME需符合类型格式；贴纸必须有效PNG或JPEG，PNG宽高均≤2048。城市ID格式为小写字母开头、随后小写字母/数字/短横线、总长≤64，还必须存在于实际目录。

错误包括 INVALID_KIND、INVALID_NAME、INVALID_TYPE、EMPTY_FILE、INVALID_STICKER、TOO_LARGE、STORAGE_LIMIT、INVALID_CITY、CITY_STICKER_UNAVAILABLE、NOT_FOUND。普通上传保存原始字节，没有自动附件加密；上传后仍需发送引用mediaId的消息。下载的owner分支独立，非所有者的单聊可见消息分支不重查is_friend/block；群消息分支要求当前成员。因此删好友或屏蔽不保证撤销原先仍可见普通媒体。依据：`chat-service/src/database.mjs:239-254`。文件头校验不等于病毒扫描。

```sh
curl -X POST 'https://chat.thestarline.cn/media?kind=file&name=example.txt' \
  -H "Authorization: Bearer ${CHAT_TOKEN}" \
  -H 'Content-Type: text/plain' --data-binary '合成附件内容'
```

## 新事与互动

所有新事接口均需Bearer。visibility可为friends、selected、public、private，默认friends；public指已登录API用户可见，不是匿名网页。selected必须指定当前好友，屏蔽和个人隐藏会进一步过滤。

| Method 与路径 | 请求或查询 | 成功响应与限制 |
| --- | --- | --- |
| POST `/moments/media` | query name；原始字节；MIME仅image/jpeg、image/png、video/mp4、video/quicktime | 201 `{media}`；图片≤10MiB，视频≤30MiB；每账号500MiB |
| GET `/moments/media/{mediaId}` | 按帖子可见性；可选单个Range：bytes=start-end，end可空 | 200/206二进制；支持Accept-Ranges；private缓存300秒 |
| POST `/moments` | JSON text可选且≤2000字；visibility；viewerIds默认[]；mediaIds默认[] | 201 `{post}`；JSON≤20000字节；文字或媒体至少一项 |
| GET `/moments/feed` | query before默认最大安全整数；limit默认20、1–50 | 200 `{posts,nextBefore}` |
| GET `/moments/users/{userId}` | 同上，且目标用户存在 | 200 `{posts,nextBefore}`，按作者及可见性过滤 |
| GET `/moments/{postId}` | 当前用户可见 | 200 `{post}` |
| DELETE `/moments/{postId}` | 仅帖子作者 | 200 `{status:"deleted"}` |
| PUT `/moments/{postId}/like` | 当前用户可见 | 200 `{post}`；重复点赞幂等 |
| DELETE `/moments/{postId}/like` | 当前用户可见 | 200 `{post}` |
| GET `/moments/{postId}/comments` | before默认0；后续用nextBefore | 200 `{comments,nextBefore}`；每页30条，按ID倒序 |
| POST `/moments/{postId}/comments` | JSON text为1–500字；replyToId可选，须本帖评论正整数ID | 201 `{comment}` |
| DELETE `/moments/{postId}/comments/{commentId}` | 评论作者或帖子作者 | 200 `{status:"deleted"}` |
| POST `/moments/{postId}/hide` | 对本人隐藏 | 200 `{status:"hidden"}` |
| POST `/moments/{postId}/report` | JSON reason为1–200字 | 200 `{status:"reported"}` |
| GET `/moments/notifications` | before默认最大安全整数 | 200 `{notifications,nextBefore,unreadCount}`；每页30条可见通知 |
| POST `/moments/notifications/read` | 标记当前用户全部未读通知 | 200 `{status:"read"}` |

帖子输入viewerIds最多100、不重复、UUID，非selected时必须为空；selected至少一位有效未屏蔽好友。mediaIds最多9、不重复、UUID；必须为自己尚未绑定其他帖子的媒体。图片最多9张，或仅1段视频，不得混发。未绑定媒体超过24小时清理。PNG仍执行宽高≤2048校验，视频检查ftyp标识。

post含id、authorId、text、visibility、createdAt、author、media、likeCount、commentCount、liked、recentComments；作者读取selected帖子可得到viewerIds。comment含id、postId、authorId、replyToId、text、createdAt、authorName。通知含actorId、actorName、postId、commentId、kind、createdAt、readAt，kind为like/comment/reply。

错误包括INVALID_POST、INVALID_COMMENT、INVALID_REPORT、INVALID_MEDIA、INVALID_RANGE、INVALID_LIMIT、NOT_FOUND、FORBIDDEN。某些输入被权限过滤后得到404；不能仅因知道帖子或媒体ID就访问内容。

## 实时位置

以下路径模板中的 `{resource}` 必须替换为 `conversations` 或 `groups`，两类范围各实现相同四个Method。

| Method 与路径 | 请求与权限 | 成功响应 |
| --- | --- | --- |
| GET `/live-locations/mine` | 当前用户 | 200 `{sessions:[{scopeKind,scopeId}]}` |
| GET `/{resource}/{scopeId}/live-locations` | 当前范围成员 | 200 `{locations:[...]}` |
| POST `/{resource}/{scopeId}/live-locations` | JSON latitude、longitude、accuracy；单聊需好友且未屏蔽 | 201 `{location}`，显式开始或更新已有共享 |
| PUT `/{resource}/{scopeId}/live-locations` | 同上，且已存在自己的共享 | 200 `{status:"updated"}`；未开始为409 NOT_SHARING |
| DELETE `/{resource}/{scopeId}/live-locations` | 当前范围成员，停止本人 | 200 `{status:"stopped"}` |

经纬度与精度需有限数字，latitude为-90–90，longitude为-180–180，accuracy为0–100000。位置对象含userId、displayName、latitude、longitude、accuracy、startedAt、updatedAt。当前没有服务端自动到期字段，也没有位置写入普通events的契约。此数据不是MLS密文。

## 普通变更长轮询

| Method 与路径 | 查询参数 | 成功响应 |
| --- | --- | --- |
| GET `/events` | after默认0；wait默认20、0–25秒；Bearer | 200 `{events,cursor}`，每批最多100条 |

`GET /events` 的query为after（默认0，非负安全整数）、wait（默认20，0–25秒）。响应200 `{events,cursor}`，每批最多100条、按ID升序；空批次保持原cursor。event为`{id,conversationId,groupId,kind,messageId,createdAt}`，可选引用可能null。客户端收到事件后重新获取对应业务资源，不应把它当完整消息内容。

```sh
curl 'https://chat.thestarline.cn/events?after=0&wait=20' \
  -H "Authorization: Bearer ${CHAT_TOKEN}"
```

客户端应连续维护轮询和断线重连。更多游标和客户端恢复行为见[实时同步与通话](https://document.thestarline.cn/app/realtime-and-calls.md)。

## MLS 设备与加密事件

这些接口要求Bearer且服务器已加载可用MLS校验器，否则503 E2EE_UNAVAILABLE。deviceId格式为`[A-Za-z0-9_-]{1,80}`；keyPackage与payload用规范标准base64，不是base64url。服务端验证MLS封装，终端负责完整消息认证与解密。

| Method 与路径 | 参数与限制 | 成功响应 |
| --- | --- | --- |
| GET `/e2ee/devices/{deviceId}/key-packages` | 当前用户的设备 | 200 `{registered,available}` |
| POST `/e2ee/devices/{deviceId}/key-packages` | JSON keyPackage；解码1–65536字节，JSON≤100000字节 | 201或已领取重复包200，`{keyPackageId}`；每设备最多25个未用包 |

下表 `{resource}` 为conversations或groups，每种各有8条Method+path。范围必须属于当前用户；除state只查范围外，其余需相应已注册设备。单聊prepare、events POST、activate还需当前好友且未屏蔽。

| Method 与路径 | JSON或query | 成功响应与状态限制 |
| --- | --- | --- |
| GET `/e2ee/{resource}/{scopeId}/state` | 无额外字段 | 200 `{mode,creatorUserId,creatorDeviceId}`；无范围记录时mode=legacy |
| GET `/e2ee/{resource}/{scopeId}/devices` | query deviceId必填 | 200 `{devices:[{userId,deviceId,signatureKey,keyPackageId,keyPackage,invited,joined}]}`；公钥与包base64，包可null |
| POST `/e2ee/{resource}/{scopeId}/prepare` | JSON deviceId | 201 `{mode:"preparing"}`；仅legacy，每参与者至少一个注册设备 |
| GET `/e2ee/{resource}/{scopeId}/events` | query deviceId、after默认0 | 200 `{events,cursor}`；立即查询，最多100条按sequence升序，仅当前设备收件 |
| POST `/e2ee/{resource}/{scopeId}/events` | JSON deviceId、clientId UUID、kind、payload；Welcome另需targetUserId、targetDeviceId、keyPackageId | 首次201、相同重试200，`{sequence}`；kind为welcome/commit/application；payload≤1048576字节，JSON≤1500000字节 |
| POST `/e2ee/{resource}/{scopeId}/welcome-acks` | JSON deviceId、sequence安全整数 | 200 `{status:"acknowledged"}`；必须是发给本设备的Welcome |
| POST `/e2ee/{resource}/{scopeId}/commit-acks` | JSON deviceId、sequence安全整数 | 200 `{status:"acknowledged"}`；必须是本设备收到的Commit |
| POST `/e2ee/{resource}/{scopeId}/activate` | JSON deviceId | 200 `{mode:"active"}`；仅创建者、所有当前设备完成必要Welcome/Commit确认 |

加密event含sequence、kind、clientId、senderUserId、senderDeviceId、payload、createdAt、targetUserId、targetDeviceId。服务端按发送者、设备、clientId去重，检查范围、类型、payload摘要、收件人和包ID完全相同。Welcome定向单设备并消费相应密钥包；application仅active可发；preparing阶段仅创建者建立群；后续Commit协调及activate受创建者限制。

关键错误包括INVALID_KEY_PACKAGE、DEVICE_KEY_CHANGED、TOO_MANY_PACKAGES、NOT_MEMBER、UNKNOWN_DEVICE、MISSING_DEVICE、ALREADY_PREPARED、INVALID_EVENT、NOT_PREPARED、CLIENT_ID_REUSED、NOT_ACTIVE、PREPARING、NOT_CREATOR、INVALID_RECIPIENT、PACKAGE_USED、INVALID_WELCOME、INVALID_COMMIT、AWAITING_WELCOME、AWAITING_COMMIT、AWAITING_REKEY、INVALID_SCOPE。HTTP映射为NOT_MEMBER→404，UNKNOWN_DEVICE→403，INVALID_KEY_PACKAGE/INVALID_EVENT/INVALID_RECIPIENT/INVALID_WELCOME→400，其余具名MLS错误通常409。

当前iOS独立MLS传输器实现文字payload，Android与主聊天尚未完成同等接入；本节不提供虚构密文可直接发送的curl。联调需调用真实加密核心生成有效TLS字节，随意base64字符串会被拒绝。完整顺序见[消息与媒体](https://document.thestarline.cn/app/messaging-and-media.md)。

## 通话与 VoIP 设备

| Method 与路径 | 参数与权限 | 成功响应 |
| --- | --- | --- |
| POST `/calls` | JSON scopeKind=conversation/group、scopeId UUID、mode=audio/video、clientId UUID；范围成员，单聊另需好友且未屏蔽 | 201 `{call}`；媒体未配置503、冲突409 CALL_BUSY |
| GET `/calls/active` | 当前用户 | 200 `{call}`，无则null |
| GET `/calls/current` | query scopeKind、scopeId；当前范围成员 | 200 `{call}`，无则null |
| GET `/calls/history` | 同上 | 200 `{calls}`，最近100条终态记录 |
| POST `/calls/{callId}/accept` | JSON `{}`；当前受邀者，单聊需仍可联系 | 200 `{call}`；状态冲突409 CALL_STATE |
| POST `/calls/{callId}/decline` | JSON `{}`；当前仍在响铃的受邀者 | 200 `{call}` |
| POST `/calls/{callId}/end` | JSON `{}`；响铃时仅发起者取消；active需实际参与者 | 200 `{call}`；群离开不必结束整场 |
| POST `/calls/{callId}/join` | JSON `{}`；仅active群通话的当前群成员 | 200 `{call}` |
| POST `/calls/{callId}/media-token` | JSON `{}`；active且尚未离开的参与者 | 200 `{url,token}`；TTL30秒，当前房间发布订阅，禁止data发布 |
| POST `/voip/devices` | JSON deviceId为UUID，pushToken为64–512个十六进制字符；绑定当前session | 200 `{status:"registered"}` |
| DELETE `/voip/devices` | JSON deviceId为UUID | 200 `{status:"unregistered"}` |

Call对象含id、scopeKind、scopeId、callerId、mode、state、createdAt、startedAt、endedAt、endedReason，不含内部roomName及请求clientId。state为ringing/active/ended/missed/cancelled；startedAt和endedAt可null。所有其他成员忙时，POST也可返回201且state=ended、endedReason=busy；必须检查对象状态。

媒体token不是聊天会话，不能公开缓存、写入静态站或长期复用。错误包括INVALID_CALL、INVALID_CALL_SCOPE、INVALID_VOIP_DEVICE、CALL_BUSY、CALL_STATE、CALL_MEDIA_UNAVAILABLE。状态机、空房回收与真实跨网络验收边界见[实时同步与通话](https://document.thestarline.cn/app/realtime-and-calls.md)。

## 记忆识别与转出桥接

| Method 与路径 | 参数与限制 | 成功响应 |
| --- | --- | --- |
| POST `/memory/recognize` | JSON text，trim后2–2000字；每用户每10分钟最多10次；需模型配置 | 200 `{schema}`；失败503 MEMORY_MODEL_UNAVAILABLE，限流429 TOO_MANY_RECOGNITIONS |
| GET `/memory-export/start` | 当前登录用户；需Account配置 | 200 `{url,state}`；5分钟转出flow，打开Account额外验证 |
| POST `/memory-export/exchange` | JSON state、ticket，均32–128个字母/数字/下划线/短横线 | 200 `{authorized:true}`；一次性flow，校验当前Account subject一致 |

schema含category、icon、fields；字段含id、title、type、options，type为服务端归一化的表单字段类型，签证和宠物疫苗主题还会补充专用字段。请求的text会发送至聊天服务及配置模型，用于生成结构；这不形成持久记忆库或云同步，不能把“未持久保存”理解为“没有上传”。当前权威Account主源码没有`/api/memory-export/redeem`路由；App工作树的历史Account副本虽有实现，也不能证明当前服务支持。因此聊天侧转出桥接不能当成已验收能力。错误包括EXPORT_UNAVAILABLE、INVALID_EXPORT_TICKET、ACCOUNT_MISMATCH、INVALID_MEMORY_TEXT。

## 共享后台：独立星企企业接口

本节用于解释共享服务的完整路由范围。这些是星企接口，不是星线 App 个人前台功能。全部要求聊天Bearer；进入指定企业路径先验证企业成员，随后按操作检查manager（admin/owner）或owner。企业ID与个人群ID不能互换。

令以下表格的`{org}`表示`/enterprise/organizations/{organizationId}`，仅为排版缩写，实际请求必须展开完整路径。

| Method 与完整路径或展开规则 | 参数与权限 | 成功响应 |
| --- | --- | --- |
| GET `/enterprise/organizations` | 当前用户 | 200 `{organizations}` |
| POST `/enterprise/organizations` | JSON name为1–80字 | 201 `{organization}`，创建者为owner并建立全员群 |
| POST `/enterprise/invitations/preview` | JSON code为43字符base64url，有效未撤销邀请码 | 200 `{invitation}`，含organizationId、organizationName、expiresAt、joinMode、membership |
| GET `/enterprise/join-requests` | 当前用户 | 200 `{requests}`，最多100条本人申请 |
| POST `/enterprise/join-requests` | JSON code，有效邀请 | 200 `{status,organizationId,organizationName}`，status为pending或joined |
| GET `{org}` | 当前企业成员 | 200企业快照：organization、members、departments、channels、tasks、announcements、services、joinRequests、invitations；最后两项对普通member为空 |
| PATCH `{org}/members/me/card` | JSON恰含name，单行1–80字或null；本人 | 200 `{member}` |
| POST `{org}/invitations` | JSON `{}`；manager | 201 `{invitation:{id,code,expiresAt}}`，3日有效；原始code仅创建响应给出 |
| DELETE `{org}/invitations/{invitationId}` | manager | 200 `{ok:true}` |
| POST `{org}/join-requests/{requestId}` | JSON approve为boolean；manager且不能审批自己 | 200 `{ok:true}`；重复相同决定幂等，冲突409 DECISION_CONFLICT |
| POST `{org}/departments` | JSON name为1–80字；manager | 201 `{department}` |
| PATCH `{org}/departments/{departmentId}` | JSON name；manager | 200 `{department}`；同企业重名409 DUPLICATE_DEPARTMENT |
| DELETE `{org}/departments/{departmentId}` | manager | 200 `{ok:true}`，关联成员部门置null |
| PATCH `{org}/members/{userId}` | 可传role=admin/member、departmentId、jobTitle≤80字、cardName单行1–80字或null | 200 `{ok:true}`；manager，角色调整仅owner且不能修改owner角色 |
| DELETE `{org}/members/{userId}` | manager；不能移除owner，admin不能移除admin | 200 `{ok:true}` |
| POST `{org}/channels` | JSON kind=direct/group、memberIds非空≤200个不重复同企业ID；group需name≤80字 | 201 `{channel}`；direct最终双方必须恰为2人，重复同事单聊复用频道 |
| GET `{org}/channels/{channelId}/messages` | query before可选正安全整数；企业和频道成员 | 200 `{messages}`，最多100条，返回升序；受入群历史边界限制 |
| POST `{org}/channels/{channelId}/messages` | JSON body为1–2000字、clientId为1–128字母/数字/下划线/短横线；JSON≤32768字节 | 201/200 `{message}`；同频道同发送者重试去重 |
| POST `{org}/channels/{channelId}/read` | JSON `{}`；企业和频道成员 | 200 `{ok:true}`，读到当前最新消息 |
| POST `{org}/tasks` | JSON title≤120、notes≤4000（可空）、dueAt为null或非负安全整数、assigneeId为当前同企业成员；JSON≤65536字节 | 201 `{task}`，初始未完成 |
| PATCH `{org}/tasks/{taskId}` | 更新上述字段及isCompleted；JSON≤65536字节 | 200 `{task}`；manager或创建者可管理，其他受指派者只能改isCompleted |
| DELETE `{org}/tasks/{taskId}` | manager或创建者 | 200 `{ok:true}` |
| POST `{org}/announcements` | JSON title≤120字、body≤10000字；JSON≤131072字节；manager | 201 `{announcement}` |
| DELETE `{org}/announcements/{announcementId}` | manager | 200 `{ok:true}` |
| PUT `{org}/announcements/{announcementId}/read` | JSON `{}`；当前企业成员 | 200 `{ok:true}` |
| POST `{org}/services` | JSON name≤80字、url≤2000；manager；只允许无用户名密码的HTTPS URL | 201 `{service:{id,name,url}}` |
| DELETE `{org}/services/{serviceId}` | manager | 200 `{ok:true}` |

企业常见错误为FORBIDDEN、INVALID_INPUT、NOT_FOUND、INVALID_INVITATION、INVITATION_ALREADY_USED、DECISION_CONFLICT、INVALID_DEPARTMENT、INVALID_MEMBER、INVALID_ASSIGNEE、INVALID_URL与CLIENT_ID_REUSED。邀请被撤销或过期不可批准加入；已被决定的同一邀请不能复活旧申请。企业频道保存服务端可读文字，不属于个人聊天MLS范围，也没有据此推导的企业附件路由。

## 调用与联调顺序

建议先在受控测试账号完成native/start→Account→callback→exchange，再调用/me确认身份与事件游标；之后读取列表，使用合成文字及小文件验证发送、重试、下载与已读。MLS联调必须使用真实生成的测试密钥包和密文；通话联调要在API状态之外验证双设备媒体与后台来电。

本页所有示例为合成或占位内容，没有生产凭据。接口细节与文档不一致时，以当前源码和可复核测试结果更新契约；不要通过去掉proof、改用旧接口或绕过成员检查“修复”失败。继续阅读[数据模型](https://document.thestarline.cn/app/data-model.md)、[安全和隐私](https://document.thestarline.cn/app/security-and-privacy.md)、[测试](https://document.thestarline.cn/app/testing.md)及[运维](https://document.thestarline.cn/app/operations.md)。

按业务流程联调时可结合[联系人与个人群](https://document.thestarline.cn/app/contacts-and-groups.md)、[新事](https://document.thestarline.cn/app/moments.md)、[位置共享](https://document.thestarline.cn/app/location-sharing.md)、[MLS协议](https://document.thestarline.cn/app/mls-protocol.md)和[限制与错误](https://document.thestarline.cn/app/limits-and-errors.md)读取状态与失败分支。
