# 嵌入令牌与终端用户 (/docs/cloud/services/agent/embed/tokens)



嵌入令牌是你的后端为一位终端用户签发的短期凭证。用它的人只能以这位用户的身份与指定智能体对话。

## 签发 [#签发]

`POST /agent/v1/embed_tokens`，用 API Key 鉴权，只在后端调用。

```bash
curl https://api-develop.deepraxis.com/agent/v1/embed_tokens \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "agent_xxx",
    "external_user_id": "customer_123",
    "ttl_seconds": 900,
    "system_message": [{ "type": "text", "text": "当前用户会员等级：金卡" }]
  }'
```

示例域名为 develop 环境，正式地址上线后替换。期望：HTTP 200。

| 字段                 | 说明                                                                           |
| ------------------ | ---------------------------------------------------------------------------- |
| `agent`            | 必填。服务型智能体的 id 或短号，必须属于你的组织、未归档，并已设置嵌入策略                                      |
| `external_user_id` | 必填。你系统里终端用户的稳定 ID，1–128 个字符，只含 `A-Z a-z 0-9 _ . : @ -`。在同一组织的同一智能体内唯一标识这位用户  |
| `ttl_seconds`      | 令牌有效期，60–3600 秒。缺省用嵌入策略的默认值，且不能超过策略的上限与 3600 秒                               |
| `version`          | 固定使用的智能体版本。缺省时用策略里固定的版本，否则用签发时的最新版本。令牌签发后版本不再变化                              |
| `system_message`   | 宿主上下文，文本块数组，合计不超过 8192 字节。见[传入上下文](/docs/cloud/services/agent/embed/context) |
| `agent_override`   | `{ "system": "…" }`，替换该令牌的指令。仅当嵌入策略允许覆盖时可用，否则返回 `EMBED_FIELD_NOT_ALLOWED`    |
| `budget_cents`     | 单个会话的预算（分）。缺省用策略默认值，不能超过策略上限                                                 |
| `allowed_origins`  | 来源白名单的子集，缺省为策略里的全部来源                                                         |
| `metadata`         | 原样保存的宿主元数据，最多 16 个键，键 1–64 字符，值字符串不超过 512 字符                                 |
| `hosted`           | 设为 `true` 时额外返回用于 Hosted 跳转的 `hosted_url`                                    |

每次调用都会产生一份新的签发记录，签发不是幂等的。

返回：

```json
{ "id": "emb_…", "token": "…", "expires_at": "2026-10-19T08:00:00.000Z" }
```

`id` 是这次签发的标识，用于吊销与查询；`token` 交给前端；`expires_at` 是 ISO 8601 UTC 时间。

## 跳转到 Hosted [#跳转到-hosted]

验证用户登录后调用以下函数，`userId` 必须来自服务端登录态：

```ts
export async function openHosted(userId: string) {
  const response = await fetch("https://api-develop.deepraxis.com/agent/v1/embed_tokens", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.PRAXIS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ agent: "agent_xxx", external_user_id: userId, hosted: true }),
  });
  if (!response.ok) throw new Error("Hosted issuance failed");
  const { hosted_url } = await response.json();
  return new Response(null, { status: 302, headers: { Location: hosted_url } });
}
```

期望：浏览器跳转后进入托管对话页。`hosted_url` 的 `#c=…` 片段携带**60 秒内有效、只能用一次**的凭证，不是嵌入令牌。托管页自动向 `POST /agent/v1/embed/exchange` 提交 `{ "code": "…" }`，取得 `{ token, expires_at }`。凭证已用、过期或无效时返回 `NOT_FOUND`（404），应重新签发，不要复用或分享这个链接。兑换凭证的期限与令牌有效期是两回事。

## 匿名访客 [#匿名访客]

访客没有宿主登录态时，可向 `POST /agent/v1/embed_tokens/anonymous` 提交 `{ "publishable_key": "PRAXIS_API_KEY" }`。这里的 `PRAXIS_API_KEY` 是**可公开 key 的占位符**，不能填服务器 API Key。响应含 `{ token, expires_at }`，新访客还会收到 `visitor_secret`；后续请求带上这个 `visitor_secret` 可延续访客身份。它是私有访客凭证，不要公开。

接口验证浏览器来源白名单，并执行可公开 key 的匿名限额。不接受调用方指定 `external_user_id`、指令、预算或 Hosted 选项。每日会话与花费上限按 UTC 自然日计算，统计实际会话，不把令牌刷新计为新会话。

## 刷新 [#刷新]

Kit 需要令牌时调用你提供的 `getToken(current)`：首次 `current` 为 `null`，更换时传入当前令牌。在里面请求你自己的后端，由后端重新签发并返回新令牌字符串即可。重新签发前先确认用户仍然登录。令牌只保存在浏览器内存里。

## 吊销 [#吊销]

吊销单张令牌：

```bash
curl -X POST https://api-develop.deepraxis.com/agent/v1/embed_tokens/emb_xxx/revoke \
  -H "Authorization: Bearer $PRAXIS_API_KEY"
```

期望：返回带 `revoked_at` 的签发记录。该令牌立即失效，它打开的事件流会收到 `session.error`（`EMBED_TOKEN_REVOKED`）后关闭，其他令牌不受影响。重复吊销返回同一条记录。上游吊销失败时返回错误，记录保持未吊销，可重试。

`GET /agent/v1/embed_tokens` 列出签发记录，可按 `agent_id`、`external_user_id` 过滤，用 `limit`（1–100，默认 20）和 `page` 翻页。记录里不包含令牌本身。

## 封禁终端用户 [#封禁终端用户]

在智能体的嵌入策略里维护**封禁名单**（`blocked_end_users`）：名单里的 `external_user_id` 不能再获得令牌，签发返回 `EMBED_USER_BLOCKED`。名单在 Console 的嵌入设置里管理。

按终端用户管理（[API 参考](/docs/api/native/agent) 为准）：

* `POST /agent/v1/agents/{agent_id}/end_users/{external_user_id}/revoke_tokens`：一次吊销该用户当前所有有效令牌，进行中的对话随即中断；
* `POST .../block`：封禁，同时立即吊销现有令牌，之后宿主为他签发新令牌会返回 `EMBED_USER_BLOCKED`；
* `POST .../unblock`：解封。

Console「运营与管理 · 终端用户」页提供同样的操作（见 [Console：运营与管理](/docs/cloud/services/agent/embed/console-operate)）。

## 终端用户只能访问自己的会话 [#终端用户只能访问自己的会话]

用嵌入令牌调用 API 的是终端用户，不是你的组织。他只能创建和读取**自己名下**的会话，看不到同一智能体下其他用户的会话，也不能调用签发、吊销、列出令牌这类宿主接口（调用会得到 404）。身份来自令牌，不来自请求里的任何参数。
