# Session Tokens

Session tokens 是授予游戏或 stream **单次游玩**的一次性令牌。它们通过确保每个令牌只能被消费一次来防止滥用。

:::caution[启用「Require API Session Token」]
要使 session tokens 生效，您必须在每个游戏或 stream 的 **Configuration** 标签中启用 **"Require API Session Token"** 设置。未启用此设置时，游戏可在无任何令牌限制的情况下游玩。
:::

## 令牌生命周期

Session token 会经历以下状态：

```
active  →  consumed   (user played the game)
        →  expired    (token passed its expiration time)
        →  revoked    (manually invalidated)
```

## 第 1 步：创建会话

从服务器使用 API key 调用会话创建端点：

```bash
curl -X POST https://app.dailyplay.ai/api/org-api-keys?action=create-session \
  -H "Content-Type: application/json" \
  -H "x-api-key: dpk_YOUR_API_KEY" \
  -d '{
    "game_id": 42,
    "external_ref": "user-12345",
    "metadata": { "campaign": "summer-promo" },
    "expires_in_minutes": 60
  }'
```

### 请求参数

| 参数 | 类型 | 必填 | 说明 |
| -------------------- | ------ | ------------------------------- | -------------------------------------------------------- |
| `game_id` | number | `game_id` 或 `stream_id` 之一 | 要授予访问权限的游戏 |
| `stream_id` | number | `game_id` 或 `stream_id` 之一 | 要授予访问权限的 stream |
| `external_ref` | string | 否 | 您的追踪参考 ID（例如用户 ID、订单 ID） |
| `metadata` | object | 否 | 供您自己追踪的任意 JSON 数据 |
| `expires_in_minutes` | number | 否 | 令牌过期时间（分钟）（默认：1440 = 24 小时） |

### 响应

```json
{
  "success": true,
  "data": {
    "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "stream_id": null,
    "game_id": 42,
    "expires_at": "2026-02-17T12:00:00.000Z",
    "external_ref": "user-12345"
  }
}
```

## 第 2 步：重定向用户

构建带会话令牌的 URL，并将终端用户重定向到该 URL：

```
https://app.dailyplay.ai/embed/42?session_token=a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

对于 streams：

```
https://app.dailyplay.ai/stream/<org-slug>/<stream_id>?session_token=a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

## 第 3 步：验证令牌

在允许游玩前，验证令牌以确保其仍有效且未过期：

```bash
curl https://app.dailyplay.ai/api/org-api-keys?action=validate-session&token=a1b2c3d4-e5f6-7890-abcd-ef1234567890
```

### 响应

```json
{
  "success": true,
  "data": {
    "valid": true,
    "status": "active",
    "stream_id": null,
    "game_id": 42,
    "expires_at": "2026-02-17T12:00:00.000Z",
    "external_ref": "user-12345",
    "metadata": { "campaign": "summer-promo" }
  }
}
```

## 第 4 步：消费令牌

当用户开始游玩时，令牌被消费，因此无法重复使用：

```bash
curl -X POST https://app.dailyplay.ai/api/org-api-keys?action=consume-session \
  -H "Content-Type: application/json" \
  -d '{
    "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "player_uuid": "player-uuid-here"
  }'
```

### 成功响应

```json
{
  "success": true,
  "data": {
    "session_id": 1,
    "org_id": 5,
    "stream_id": null,
    "game_id": 42,
    "external_ref": "user-12345",
    "metadata": { "campaign": "summer-promo" }
  }
}
```

### 已消费或已过期

若令牌已被使用或已过期，API 返回 `410 Gone` 状态：

```json
{
  "success": false,
  "error": "Session is invalid, already consumed, or expired"
}
```

## 令牌属性

| 属性 | 说明 |
| ----------------------- | ------------------------------------------------------------ |
| `token` | UUID v4 标识符 — 在游戏 URL 中传递 |
| `stream_id` / `game_id` | 目标资源（至少需要一个） |
| `external_ref` | 您用于与系统关联的参考 ID |
| `metadata` | 创建时附加的任意 JSON 载荷 |
| `status` | 当前状态：`active`、`consumed`、`expired` 或 `revoked` |
| `expires_at` | 令牌将自动过期的时间 |

## 过期

- 若未指定 `expires_in_minutes`，令牌默认 **24 小时过期**
- 过期令牌由后台进程自动清理
- 对于时效性用例，可将过期时间设置短至 1 分钟

## 错误处理

| 状态码 | 含义 |
| ----------- | ------------------------------------------------------ |
| `200` | 令牌有效 / 操作成功 |
| `400` | 缺少或无效的参数 |
| `401` | 无效或已撤销的 API key |
| `403` | API key 无权访问所请求的资源 |
| `410` | 令牌已被消费或已过期 |
| `429` | 超过速率限制 |