# 一次性邀请链接

这是 Partner 接入查端流程的入口。你的服务端为**明确选定的组织**生成邀请，玩家在 Screenshare 页面自行提交资料。正式会话使用现有组织工单系统。

## 完整流程

1. 验证你自己系统中的操作人有权为目标服务器发起查端。
2. 根据服务器配置选择 `partner_id` 和绑定该组织的密钥，使用 `/v1/me` 验证映射。
3. 持久化业务请求和随机 `Idempotency-Key`，调用创建邀请。
4. 保存返回的邀请 ID，将完整 `link` 私下发送给目标玩家。
5. 玩家打开链接；预览不会占位，也不会生成正式工单。
6. 玩家填写游戏 ID、联系方式、完整远程信息，以及未预填的备注。
7. 第一次完整有效提交原子认领邀请。并发提交只会有一个成功者。
8. 收到 `invitation.joined` 或通过状态查询确认 `joined`，再查询工单进度。
9. 工作人员完成查端、归档记录；接收记录事件并查询结果。

## 创建邀请

```http
POST /v1/partners/{partner_id}/invitations
Authorization: Bearer YOUR_ORGANIZATION_KEY
Content-Type: application/json
Idempotency-Key: 74e123d0-53f0-47dd-a2ef-678513162c44

{"game":"minecraft","notes":"请配合本次服务器查端。"}
```

需要 `invitations:write`。`partner_id` 必须与密钥绑定组织一致。

| 字段 | 必填 | 规则 |
| --- | --- | --- |
| `game` | 是 | `minecraft` 或 `fps` |
| `notes` | 否 | 可省略或空字符串；非空时为 2–600 字符，去除首尾空白 |

不接受 `holderId`、玩家 Cookie、远程密码、任意扣费账号、有效期或目标组织等额外字段。有效期由平台固定为创建后 7 天，每个组织最多保留 50 个有效待加入/处理中邀请。

```json
{
  "ok":true,
  "data":{
    "id":"8f86690d-76d7-4e87-84d2-27b5fc8b6d41",
    "game":"minecraft",
    "state":"pending",
    "ticket_id":null,
    "created_at":"2026-09-25T03:00:00.000Z",
    "expires_at":"2026-10-02T03:00:00.000Z",
    "archived_at":null,
    "link":"https://dash.screenshare.cn/partner-ticket/ORGANIZATION_ID/8f86690d-76d7-4e87-84d2-27b5fc8b6d41#token=EXAMPLE_TOKEN",
    "replayed":false
  },
  "request_id":"req_EXAMPLE"
}
```

首次成功为 HTTP 201。相同请求重放返回 200 和 `replayed:true`；如果邀请已经被使用、过期或撤销，重放仍返回原邀请，`link:null`，不会创建新邀请。

## 查询邀请

```http
GET /v1/partners/{partner_id}/invitations?page=1
GET /v1/partners/{partner_id}/invitations/{invitation_id}
```

需要 `invitations:read`。列表返回 `data.items`、`data.page`、`data.page_count`，每页 20 条。详情返回单个邀请对象。只返回状态元数据，不返回完整链接、草稿备注或玩家凭证。

| state | 含义 | 接入方处理 |
| --- | --- | --- |
| `pending` | 尚未认领，且仍在有效期内 | 可以分发或撤销 |
| `claiming` | 玩家提交处理中或正在核对结果 | 等待确认，不重复创建工单 |
| `joined` | 玩家已成功加入 | 用邀请 ID 查询工单进度 |
| `expired` | 未使用邀请超过 7 天 | 需要时用新编号创建新邀请 |
| `revoked` | 未使用邀请已被撤销 | 停止分发，旧链接不可用 |

`claiming` 不会仅因原有效期到达就变成可重新使用的邀请；平台先核对上游结果，防止重复工单。

## 取回链接

```http
GET /v1/partners/{partner_id}/invitations/{invitation_id}/link
```

需要 `invitations:write`。仅未认领且未到期的有效邀请可返回 `data.link`；否则为 `409 INVITE_UNAVAILABLE`。只具有读权限的应用不能获取加入凭证。

URL 片段 `#token=...` 必须完整保留。不要把链接放进短链接分析服务、公开群日志或第三方统计参数。玩家加入后页面会清除片段中的密钥。

## 撤销

```http
POST /v1/partners/{partner_id}/invitations/{invitation_id}/revoke
Authorization: Bearer YOUR_ORGANIZATION_KEY
```

需要 `invitations:write`，不需要请求正文。未使用邀请可以撤销；重复撤销同一已撤销邀请返回相同状态。`claiming` 和 `joined` 状态不能通过撤销接口删除或关闭工单。

## 幂等与结果不确定

幂等作用域是 **密钥 ID + 组织 ID + Idempotency-Key**。同一作用域下，`game` 或规范化后的 `notes` 改变，返回 `409 IDEMPOTENCY_CONFLICT`。邀请认领后草稿备注会被清除，但服务器仍保存不可逆请求指纹用于冲突检测。

客户端连接超时、未收到完整响应或收到 5xx 时，保留原请求编号和正文继续恢复。不要为了消除错误直接生成新编号，否则会产生多份真实邀请。旧密钥轮换后不继承幂等作用域。

## 玩家身份和浏览器凭证

玩家不需要注册 Screenshare 账号。成功认领后，访问凭证保存在认领浏览器的安全 HttpOnly Cookie 中，最长 90 天。更换浏览器或清除数据后，不能凭已使用的一次性链接重新认领。组织管理员无法通过开放 API 读取、复制或重置玩家 Cookie。

工单以独立访客身份存在，不会因为玩家填了与网站用户名相同的游戏 ID 就归入该账号个人工单。已登录玩家可使用头像快照，但这不是账号所有权认证。

## 次数规则

生成邀请、预览、玩家加入、查询和回复不会额外扣除 Partner 查端次数。工作人员归档实际查端记录时，按现行组织规则消耗相应游戏的服务次数。邀请 API 不会给组织增加额度，也不保证未来归档时仍有可用额度。

普通个人查端工单仍按个人规则计次，不能通过伪造 Holder 或目标组织改变计费归属。
