好友申请
用户之间加好友通常由用户在客户端发起:A 向 B 发送好友申请,B 在客户端同意或拒绝。发送、同意和拒绝申请都只能由用户本人在客户端操作,服务端 REST API 只能查询用户的好友申请。业务服务端需要直接建立好友关系时,调用添加好友即可,不必经过申请。
申请的处理流程
A 发送申请后,按 B 的加好友方式处理:
| B 的加好友方式 | 结果 |
|---|---|
need_confirm(需要验证,默认) | 生成一条待处理的申请,B 的在线设备收到通知。B 同意后双方成为好友,A 收到新好友的通知;B 拒绝后 A 收到被拒绝的通知 |
allow_any(允许任何人添加) | 不需要 B 同意,双方直接成为好友。申请同样记录下来,状态为“已成为好友”,B 能看到是谁加了自己 |
deny_any(拒绝任何人添加) | 申请发不出去,A 收到 403 friend_add_denied |
- 互相申请:B 已经向 A 发出了未过期的待处理申请,A 再向 B 申请时,两人直接成为好友,即使 B 的加好友方式是
deny_any:B 的申请本身就表示同意。 - 一对用户只有一条申请:A 对 B 的申请还没有处理时,A 再次申请会更新附言、预设的备注、来源和发送时间,B 再收到一次通知,有效期从这次发送重新计算。之前被拒绝或已过期的,再次申请同样更新这一条。
- 有效期:申请在最近一次发送后 30 天内未处理即过期,过期后不能再同意或拒绝,状态显示为
expired。申请记录在发送 60 天后自动删除,所以处理过的申请至少会保留 30 天。 - 不能撤回:申请人不能撤回已发出的申请。A 把 B 加入黑名单时,A 发给 B、尚未处理的申请会被一并撤回(删除),B 不能再同意它。
- 服务端直接添加:调用添加好友让两人成为好友时,两人之间未过期的待处理申请改为“已成为好友”,申请人在申请中预设的备注会写入他这一侧的好友信息。
- 申请的内容:附言最长 256 个字符;申请人可以预先给对方设置备注(最长 64 个字符),成为好友后自动写入;还可以带添加来源(规则同好友对象中的添加来源)。附言和来源会展示给对方。
- 内容检查:附言和添加来源在发送时经过内容安全检查。不通过时申请不会发出,A 收到
403 content_rejected,details.field为message或add_source,见调用方看到的错误。附言命中替换规则的保存替换后的文字,所以查询到的附言中可能有*;添加来源是标识,命中替换规则时直接按拒绝处理。备注不检查。 - 加好友前回调:应用开启了加好友前回调时,A 发送申请(包括会被直接通过的申请)之前先请求你的服务端,还可以设置 B 同意申请时也调用。回调排在内容检查之后、下文的“是否已是好友、黑名单、对方的加好友方式、好友数”等检查之前。被拒绝时 A 收到
403 permission_denied,details.reason为app_rejected(你的服务端拒绝)或callback_unavailable(回调失败后按“拒绝”策略处理),details.app_reason为你的服务端给出的原因代码,message为你的服务端给出的提示,没有给出时为“操作被拒绝”;申请不会写入,B 不会收到;B 同意时被拒绝的,B 收到同样的错误,申请保持待处理。服务端添加好友不调用加好友前回调。
发送申请的限制
为了防止骚扰,发送好友申请有以下限制,超出时 A 收到 429 rate_limited,Retry-After 响应头为需要等待的秒数,details.reason 说明是哪一种限制:
| 限制 | details.reason |
|---|---|
| 每个用户每分钟最多发送 20 个申请、每天最多 500 个 | request_rate |
| 同一个人对同一个人每天最多申请 5 次:连续申请 5 次后,约每 4.8 小时恢复 1 次 | pair_daily_limit |
| 申请被对方拒绝后,24 小时内不能再向对方申请 | declined_cooldown |
发送申请时还会检查双方的关系:已经是好友返回 409 already_exists;A 自己的好友数已满,或者 B 的加好友方式为 allow_any 而 B 的好友数已满,返回 409 limit_exceeded;A 把 B 拉黑了,或 B 把 A 拉黑了,返回 403 user_blocked(应用关闭了黑名单拦截时不检查,见黑名单)。
申请的状态
status | 说明 |
|---|---|
pending | 待处理,且没有过期 |
accepted | 已成为好友:对方同意了、对方的加好友方式为 allow_any 而自动通过、双方互相申请,或者服务端直接把两人添加为好友 |
declined | 已被拒绝 |
expired | 已过期:待处理的申请在最近一次发送后 30 天内没有处理 |
查询好友申请
查询一个用户收到的或发出的好友申请,按发送时间从新到旧排列,可以按状态筛选。
GET
/{org_name}/{app_name}/users/{username}/friend-requests路径参数
org_name、app_name 见接入概述。
| 参数 | 类型 | 说明 |
|---|---|---|
username | String | 用户名 |
查询参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
direction | String | 是 | received 为该用户收到的申请,sent 为该用户发出的申请 |
status | String | 否 | 按状态筛选:pending、accepted、declined 或 expired,见申请的状态。不传时返回全部 |
limit | Number | 否 | 每页条数,默认 20,取值 1 到 100 |
cursor | String | 否 | 下一页的游标,见分页。翻页时不要改变 direction 和 status |
请求示例
bash
curl "$IM_API/$ORG/$APP/users/alice/friend-requests?direction=received" \
-H "Authorization: Bearer $APP_TOKEN"响应
成功返回 200 OK,items 为本页的申请,每项为好友申请对象;next_cursor 为下一页的游标,为 null 时没有下一页。已被删除的用户的申请不出现在列表中,一页的条数可能少于 limit,是否还有下一页只看 next_cursor。
收到的申请:
json
{
"items": [
{
"username": "dave",
"nickname": "戴夫",
"avatar_url": "",
"message": "你好,我是戴夫",
"add_source": "search",
"status": "pending",
"unread": true,
"requested_at": "2026-10-02T19:12:59.406Z",
"expires_at": "2026-11-01T19:12:59.406Z",
"handled_at": null
},
{
"username": "erin",
"nickname": "艾琳",
"avatar_url": "",
"message": "你好,我是艾琳",
"add_source": "qrcode",
"status": "accepted",
"unread": false,
"requested_at": "2026-10-02T19:08:09.923Z",
"expires_at": null,
"handled_at": "2026-10-02T19:09:09.491Z"
}
],
"next_cursor": null
}发出的申请(direction=sent)没有 unread,另有申请人预设的备注 remark:
json
{
"items": [
{
"username": "alice",
"nickname": "爱丽丝",
"avatar_url": "",
"message": "你好,我是戴夫",
"remark": "小爱",
"add_source": "search",
"status": "pending",
"requested_at": "2026-10-02T19:12:59.406Z",
"expires_at": "2026-11-01T19:12:59.406Z",
"handled_at": null
}
],
"next_cursor": null
}错误
| HTTP 状态码 | code | 说明 |
|---|---|---|
| 400 | invalid_argument | direction 缺失或不是 received、sent;status 不是可选的值;limit 不在 1 到 100 之间;cursor 无效 |
| 404 | not_found | 用户不存在或已删除 |
数据结构
好友申请对象
站在 {username} 的角度描述一条申请:收到的申请中“对方”是申请人,发出的申请中“对方”是被申请人。
| 字段 | 类型 | 说明 |
|---|---|---|
username | String | 对方的用户名 |
nickname | String | 对方的昵称,没有设置时为空字符串 |
avatar_url | String | 对方的头像地址,没有设置时为空字符串 |
message | String | 申请人填写的附言,没有时为空字符串 |
remark | String | 只在发出的申请中出现:申请人预先给对方设置的备注,没有时为空字符串 |
add_source | String | 添加来源,没有时为空字符串 |
status | String | 状态:pending、accepted、declined 或 expired,见申请的状态 |
unread | Boolean | 只在收到的申请中出现:用户是否还没有在客户端查看过这条申请。只有 pending 的申请可能为 true |
requested_at | String | 最近一次发送的时间 |
expires_at | String | 过期时间,只有 pending 的申请有值,其他状态为 null |
handled_at | String | 同意、拒绝或成为好友的时间,pending 和 expired 的申请为 null |
