# UAP 管理 API 对接说明（X-Admin-Api-Key，面向自动化阅读）

本文档**独立成篇**，描述如何在业务系统中以 **管理员级永久 API Key**（`X-Admin-Api-Key`）调用 **Unified Authentication Platform（UAP）** 的 `/admin-api/*` 接口。阅读者应能仅凭本文完成：配置项填写、用户与组织的查询/创建/更新、白名单字段约束的正确处理。

> **范围与边界**：本文不覆盖 `uap-api` Skill 的内容（应用 SSO / 消息 / HMAC 签名 / M2M Access Token）。本文涉及的**唯一**凭据是 `X-Admin-Api-Key`。两套体系互不兼容，不要混用 Header。

### 示例约定

下文示例中：

- `https://api.hnyunzhu.com/api/v1` 表示 `UAP_API_BASE`（生产环境）；
- `ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` 为占位符，真实 Key 须由用户从 UAP 后台「API Key 管理」获取，**仅创建 Key 那一刻平台返回一次明文**；
- 所有 JSON 示例字段值均为占位（如 `13800138000`、`研发中心` 等），不可直接复制使用。

---

## 1. 基础地址

| 变量名 | 含义 | 示例 |
|--------|------|------|
| `UAP_API_BASE` | HTTP(S) API 根路径，**必须**包含版本前缀 `/api/v1` | `https://api.hnyunzhu.com/api/v1` |

下文所有接口路径均相对 `UAP_API_BASE` 拼接。例：`UAP_API_BASE` + `/admin-api/users/` → `https://api.hnyunzhu.com/api/v1/admin-api/users/`。

> 注意：`/admin-api-keys/*`（一组管理 Key 自身的接口，仅供 UAP 超管在前端控制台使用，需要登录 JWT + 操作密码 + 短信验证码）**不**在本文范围内。本文只覆盖**用 Key 调业务**的 `/admin-api/users/*` 与 `/admin-api/organizations/*`。

---

## 2. 凭据与生命周期

### 2.1 配置项

| 配置项 | 说明 |
|--------|------|
| `ADMIN_API_KEY` | 完整明文 Key，形如 `ak_live_<32 字节 token_urlsafe>`。**仅服务端**使用，禁止写入前端、客户端或公开仓库。 |

环境变量放在 `.env`，示例在 `.env.example`；**绝不**把真实 Key 写入 Skill、README 或提交的源码。

### 2.2 生命周期（由 UAP 后台超管控制）

```
新建 → 启用(is_active=true, is_revoked=false)
       │
       ├── 禁用 (is_active=false)         ← 可在后台一键恢复
       │       │
       │       └── 启用                    ← 重新放行
       │
       └── 吊销 (is_revoked=true)          ← 不可恢复，必须新建一把
```

- **明文一次性返回**：创建 Key 时响应中带 `key_once` 字段，是平台返回明文的**唯一时机**；之后数据库只存 bcrypt 哈希与 12 字节前缀。
- **不暴露物理删除**：只能 禁用 / 启用 / 吊销。吊销后 `is_active` 自动置 `false` 且不可恢复。
- **每次调用都留痕**：命中后平台异步刷新 `last_used_at`、`last_used_ip`，并把 `use_count` 加 1。

### 2.3 应对策略（写在对接代码里的常识）

- Key 丢失 → 引导用户在后台**吊销旧 Key 并新建一把**，不可"找回"。
- Key 被吊销 → 调用全部 403，必须切换到新 Key。
- Key 被禁用 → 调用全部 403；让管理员到后台启用即可恢复。
- Key 轮换 → 先建新 Key → 调用方切换并验证 → 再吊销旧 Key（避免空窗）。

---

## 3. 鉴权 Header 与 Scopes

### 3.1 Header

```http
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

- 平台校验顺序：前缀粗筛（前 12 字节） → bcrypt 校验完整 Key → `is_active is True` 且 `is_revoked is False` → 命中即放行；任一步失败返回 403。
- **不要**额外携带 `X-App-Id` / `X-App-Access-Token` / `X-Sign` / `X-Timestamp` 等字段，那些属于 `uap-api` 的应用签名/M2M 体系，对 `/admin-api/*` 无效（也不会被使用）。

### 3.2 Scopes 模型

Scope 由超管在创建 Key 时分配，逗号分隔；**白名单**集合：

```
user:read, user:write, org:read, org:write
```

接口 ↔ Scope 映射：

| Scope | 接口 |
|-------|------|
| `user:read` | `GET /admin-api/users/`、`GET /admin-api/users/search`、`GET /admin-api/users/{id}` |
| `user:write` | `POST /admin-api/users/`、`PUT /admin-api/users/{id}` |
| `org:read` | `GET /admin-api/organizations/` |
| `org:write` | `POST /admin-api/organizations/`、`PUT /admin-api/organizations/{id}` |

平台在每个 endpoint 内部都会做 `scope in api_key.scopes` 检查，缺 scope **直接** 返回：

```json
{ "detail": "API Key 缺少 user:read 权限" }
```

HTTP 状态 **403**。

---

## 4. 用户接口

> 公共要求：**所有**请求必须带 `X-Admin-Api-Key` Header。响应字段对齐 `UserList` / `User`（见 `backend/app/schemas/user.py`）。

### 4.1 `GET /admin-api/users/`（用户列表）

- **Scope**：`user:read`
- **认证**：`X-Admin-Api-Key`

#### 查询参数

| 参数 | 类型 | 必填 | 默认 | 说明 |
|------|------|------|------|------|
| `skip` | int | 否 | 0 | 跳过条数（分页） |
| `limit` | int | 否 | 50 | 每页条数 |
| `status` | string | 否 | — | 用户状态精确匹配：`PENDING` / `ACTIVE` / `DISABLED` |
| `role` | string | 否 | — | 角色精确匹配：`SUPER_ADMIN` / `DEVELOPER` / `ORDINARY_USER` |
| `mobile` | string | 否 | — | 手机号模糊匹配（`ilike %x%`） |
| `name` | string | 否 | — | 中文名模糊匹配 |
| `english_name` | string | 否 | — | 英文名模糊匹配 |
| `keyword` | string | 否 | — | 同时模糊匹配 `mobile / name / english_name` 任一 |
| `organization_id` | int | 否 | — | 组织 ID 精确匹配；传 `0` 表示**未归属任何组织** |

只过滤 `is_deleted == 0` 的用户；按 `id desc` 排序。

#### 响应 `200`：`UserList`

| 字段 | 类型 | 说明 |
|------|------|------|
| `total` | int | 命中总数（不分页） |
| `items` | array | 用户列表 |

`items[]` 元素 `User`：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | int | 平台用户主键 |
| `mobile` | string | 手机号 |
| `name` | string \| null | 中文名 |
| `english_name` | string \| null | 英文名（拼音） |
| `organization_id` | int \| null | 组织 ID |
| `organization_name` | string \| null | 组织名（由 `Organization` 关联展开后扁平化返回，免二次请求） |
| `status` | string | `PENDING` / `ACTIVE` / `DISABLED` |
| `role` | string | `SUPER_ADMIN` / `DEVELOPER` / `ORDINARY_USER` |
| `is_deleted` | int | 0/1 |
| `created_at` | datetime | ISO8601 |
| `updated_at` | datetime | ISO8601 |

#### 调用与返回示例

```http
GET https://api.hnyunzhu.com/api/v1/admin-api/users/?skip=0&limit=20&status=ACTIVE&keyword=zhang HTTP/1.1
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

**响应 `200`：**

```json
{
  "total": 128,
  "items": [
    {
      "id": 1001,
      "mobile": "13800138000",
      "name": "张三",
      "english_name": "zhangsan",
      "organization_id": 7,
      "organization_name": "研发中心",
      "status": "ACTIVE",
      "role": "ORDINARY_USER",
      "is_deleted": 0,
      "created_at": "2026-01-10T08:00:00",
      "updated_at": "2026-04-01T12:00:00"
    }
  ]
}
```

#### 常见错误

| 状态 | 含义 |
|------|------|
| 401 | 缺少 `X-Admin-Api-Key` |
| 403 | Key 失效 / 缺 `user:read` |

---

### 4.2 `GET /admin-api/users/search`（关键词搜索）

- **Scope**：`user:read`
- **说明**：与 4.1 类似，但只返回 **激活** 且 **未删除** 的用户（`is_deleted=0` 且 `status=ACTIVE`），按命中关键词模糊匹配 `mobile / name / english_name`。

#### 查询参数

| 参数 | 类型 | 必填 | 默认 | 说明 |
|------|------|------|------|------|
| `q` | string | 否 | — | 关键词；不传时按 `limit` 截取一批激活用户 |
| `limit` | int | 否 | 20 | 最大返回条数 |

#### 响应 `200`

数组（非分页对象），元素结构与 4.1 `items[]` 一致。

#### 调用与返回示例

```http
GET https://api.hnyunzhu.com/api/v1/admin-api/users/search?q=13800&limit=10 HTTP/1.1
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

```json
[
  {
    "id": 1001,
    "mobile": "13800138000",
    "name": "张三",
    "english_name": "zhangsan",
    "organization_id": 7,
    "organization_name": "研发中心",
    "status": "ACTIVE",
    "role": "ORDINARY_USER",
    "is_deleted": 0,
    "created_at": "2026-01-10T08:00:00",
    "updated_at": "2026-04-01T12:00:00"
  }
]
```

---

### 4.3 `GET /admin-api/users/{user_id}`（用户详情）

- **Scope**：`user:read`

#### 路径参数

| 参数 | 类型 | 说明 |
|------|------|------|
| `user_id` | int | 平台用户主键 |

#### 响应 `200`

单个 `User` 对象，结构同 4.1 `items[]`。

#### 错误

| 状态 | 含义 |
|------|------|
| 404 | `用户不存在`（含 `is_deleted=1` 的软删用户，对外不可见） |

#### 调用与返回示例

```http
GET https://api.hnyunzhu.com/api/v1/admin-api/users/1001 HTTP/1.1
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

```json
{
  "id": 1001,
  "mobile": "13800138000",
  "name": "张三",
  "english_name": "zhangsan",
  "organization_id": 7,
  "organization_name": "研发中心",
  "status": "ACTIVE",
  "role": "ORDINARY_USER",
  "is_deleted": 0,
  "created_at": "2026-01-10T08:00:00",
  "updated_at": "2026-04-01T12:00:00"
}
```

---

### 4.4 `POST /admin-api/users/`（创建用户）

- **Scope**：`user:write`
- **Content-Type**：`application/json`
- **平台强制**：新建用户始终为 `role=ORDINARY_USER`、`status=ACTIVE`。**请求体不要传 role / status**（schema 不接收）。

#### 请求体（`AdminApiUserCreate`）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `mobile` | string | **是** | 中国大陆手机号，正则 `^1[3-9]\d{9}$` |
| `password` | string | **是** | 6–64 位 |
| `name` | string \| null | 否 | 中文名；不传时平台自动生成 `用户XXXXXX`（6 位字母数字） |
| `english_name` | string \| null | 否 | 英文名；不传时按 `name` 自动生成拼音并去重 |
| `organization_id` | int \| null | 否 | 组织 ID；非 null 时必须存在，否则 400 |

> ⚠️ 即便你的客户端模型多带了字段，平台也只读取以上 5 项。但请**不要**主动加 `role` / `status` / `is_deleted` 等字段，行为属于「越权伪装」，会让代码评审显得很危险。

#### 响应 `200`

完整 `User` 对象（结构同 4.1 `items[]`，含 `id` 与 `organization_name`）。

#### 错误

| 状态 | 含义 |
|------|------|
| 403 | 缺 `user:write` |
| 400 | `该手机号已在系统中注册` / `密码不能为空` / `组织不存在` |
| 422 | 字段校验失败（手机号正则不匹配、密码长度越界等） |

#### 调用与返回示例

```http
POST https://api.hnyunzhu.com/api/v1/admin-api/users/ HTTP/1.1
Content-Type: application/json
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

{
  "mobile": "13900139000",
  "password": "Init@2026",
  "name": "李四",
  "organization_id": 7
}
```

```json
{
  "id": 1042,
  "mobile": "13900139000",
  "name": "李四",
  "english_name": "lisi",
  "organization_id": 7,
  "organization_name": "研发中心",
  "status": "ACTIVE",
  "role": "ORDINARY_USER",
  "is_deleted": 0,
  "created_at": "2026-04-28T10:00:00",
  "updated_at": "2026-04-28T10:00:00"
}
```

---

### 4.5 `PUT /admin-api/users/{user_id}`（更新用户）

- **Scope**：`user:write`
- **Content-Type**：`application/json`

#### 请求体（`AdminApiUserUpdate`，`extra="forbid"`）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string \| null | 否 | 改名后若**未**显式传 `english_name`，平台会按新姓名自动重生成拼音并去重 |
| `english_name` | string \| null | 否 | 显式覆盖英文名；同样会做去重 |
| `password` | string | 否 | 6–64 位；空字符串视为「未提供」会被拒 |
| `organization_id` | int \| null | 否 | 改组织；`null` 表示取消归属；非 null 必须存在 |

**严禁出现以下字段**（一旦出现，pydantic 直接 422）：

```
mobile, role, status, is_deleted
```

——这些动作属于敏感操作，**只允许**在 UAP 后台由超管完成。

至少要带一个白名单字段，否则 400 `未提供需更新的字段` / `未检测到变更`。

#### 响应 `200`

更新后的 `User` 对象，结构同 4.1 `items[]`。

#### 错误

| 状态 | 含义 |
|------|------|
| 403 | 缺 `user:write` |
| 404 | `用户不存在` |
| 400 | `姓名不能为空` / `新密码不能为空` / `组织不存在` / `未提供需更新的字段` / `未检测到变更` |
| 422 | 出现 `mobile / role / status / is_deleted` 等被 forbid 的字段 |

#### 调用与返回示例

**只改组织：**

```http
PUT https://api.hnyunzhu.com/api/v1/admin-api/users/1042 HTTP/1.1
Content-Type: application/json
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

{
  "organization_id": 12
}
```

**改姓名（英文名平台自动重生成）：**

```http
PUT https://api.hnyunzhu.com/api/v1/admin-api/users/1042 HTTP/1.1
Content-Type: application/json
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

{
  "name": "李四_新"
}
```

**反例（会 422）：**

```json
{
  "mobile": "13900139001",
  "role": "DEVELOPER"
}
```

```json
{
  "detail": [
    { "loc": ["body", "mobile"], "msg": "Extra inputs are not permitted", "type": "extra_forbidden" }
  ]
}
```

---

## 5. 组织接口

> 公共要求：所有请求带 `X-Admin-Api-Key`。响应字段对齐 `OrganizationResponse`（见 `backend/app/schemas/organization.py`）。

### 5.1 `GET /admin-api/organizations/`（组织列表）

- **Scope**：`org:read`
- **说明**：返回**全量**组织（不分页），按 `sort_order asc, id asc` 排序。

#### 响应 `200`

数组，元素 `OrganizationResponse`：

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | int | 组织主键 |
| `name` | string | 组织名（唯一） |
| `description` | string \| null | 描述 |
| `sort_order` | int | 排序序号 |
| `created_at` | datetime | ISO8601 |
| `updated_at` | datetime | ISO8601 |

#### 调用与返回示例

```http
GET https://api.hnyunzhu.com/api/v1/admin-api/organizations/ HTTP/1.1
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

```json
[
  {
    "id": 7,
    "name": "研发中心",
    "description": "技术研发部门",
    "sort_order": 0,
    "created_at": "2025-12-01T08:00:00",
    "updated_at": "2026-01-15T10:30:00"
  }
]
```

---

### 5.2 `POST /admin-api/organizations/`（创建组织）

- **Scope**：`org:write`
- **Content-Type**：`application/json`

#### 请求体（`AdminApiOrganizationCreate`）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string | **是** | 1–100，**唯一** |
| `description` | string \| null | 否 | 描述文本 |
| `sort_order` | int | 否 | 默认 0 |

#### 响应 `200`

`OrganizationResponse`，结构同 5.1 数组元素。

#### 错误

| 状态 | 含义 |
|------|------|
| 403 | 缺 `org:write` |
| 400 | `组织名称已存在`（DB 唯一约束冲突） |

#### 调用与返回示例

```http
POST https://api.hnyunzhu.com/api/v1/admin-api/organizations/ HTTP/1.1
Content-Type: application/json
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

{
  "name": "市场部",
  "description": "对外市场与品牌",
  "sort_order": 5
}
```

```json
{
  "id": 23,
  "name": "市场部",
  "description": "对外市场与品牌",
  "sort_order": 5,
  "created_at": "2026-04-28T10:05:00",
  "updated_at": "2026-04-28T10:05:00"
}
```

---

### 5.3 `PUT /admin-api/organizations/{organization_id}`（更新组织）

- **Scope**：`org:write`
- **Content-Type**：`application/json`

#### 请求体（`AdminApiOrganizationUpdate`，`extra="forbid"`）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | string \| null | 否 | 1–100 |
| `description` | string \| null | 否 | — |
| `sort_order` | int \| null | 否 | — |

至少需要一个白名单字段被改动；无变更返回 400。任何其他字段 → 422。

#### 错误

| 状态 | 含义 |
|------|------|
| 403 | 缺 `org:write` |
| 404 | `组织不存在` |
| 400 | `组织名称已存在` / `请至少修改一项内容` / `未检测到变更` |
| 422 | 出现非白名单字段 |

#### 调用与返回示例

```http
PUT https://api.hnyunzhu.com/api/v1/admin-api/organizations/23 HTTP/1.1
Content-Type: application/json
X-Admin-Api-Key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

{
  "description": "市场部 + 品牌运营",
  "sort_order": 3
}
```

```json
{
  "id": 23,
  "name": "市场部",
  "description": "市场部 + 品牌运营",
  "sort_order": 3,
  "created_at": "2026-04-28T10:05:00",
  "updated_at": "2026-04-28T10:30:00"
}
```

---

## 6. 操作审计

每次 `POST` / `PUT` 写操作（`/admin-api/users/`、`/admin-api/organizations/`）平台都会写入一条 `operation_log`，结构稳定：

| 字段 | 含义 |
|------|------|
| `operator_id` | Key 创建者用户 ID（`AdminApiKey.created_by`） |
| `action_type` | `MANUAL_ADD`（创建用户）/ `UPDATE`（更新用户）/ `ORG_CREATE` / `ORG_UPDATE` |
| `target_user_id` / `target_mobile` | 仅用户接口提供 |
| `ip_address` | 调用来源 IP（取自请求） |
| `details` | 至少包含： |

```json
{
  "via": "admin_api_key",
  "key_id": 12,
  "key_prefix": "ak_live_xxx",
  "...": "其余按 endpoint 提供，例如 changes / role / resource / organization_id"
}
```

含义：

- 任何由 Key 触发的写动作都能在审计日志里追溯到**这把 Key**（`key_id` + `key_prefix`），并通过 `created_by` 反查到发放它的超管。
- 因此团队**不要**共享同一把 Key——共享会丧失追溯粒度。每个对接方/服务/环境最好各自一把。

---

## 7. 安全要点

1. **仅服务端**：`ADMIN_API_KEY` 是**永久凭证**，泄露等同于把"超管手里的几个常用功能键"交出去。**绝不**在浏览器、移动端、桌面端、客户端代码里出现；**绝不**提交到 git。
2. **HTTPS**：生产环境**必须**走 HTTPS。HTTP 调用会让 Key 直接以明文出现在网络抓包中。
3. **最小 scope**：发放方应按需勾选 scopes（如只需查用户就只给 `user:read`），调用方拿到后也应在代码里**断言**自己用到了哪些 scope，避免静默降级。
4. **轮换流程**：周期性 / 离职时换 Key：先建新 → 切换 → 验证 → 吊销旧（吊销不可恢复，注意先后顺序）。
5. **错误日志可观察**：把 `X-Admin-Api-Key` 的**前缀**（前 12 字节）写进调用方的失败日志，便于双向对账；**不要**把完整明文 Key 写进任何日志。
6. **环境隔离**：测试环境与生产环境**必须**用不同的 Key；不要拿生产 Key 去跑联调。

---

## 8. 调试入口

- **Swagger UI**：`{UAP_API_BASE_HOST}/api/v1/docs`（如 `https://api.hnyunzhu.com/api/v1/docs`），可在 "Authorize" 处填 `X-Admin-Api-Key` 后直接试调本组接口。
- 若本文与线上部署不一致，以**实际 OpenAPI 与接口返回**为准。

---

## 9. 完整对接示例（Python）

```python
import os
import requests

UAP_API_BASE = os.environ["UAP_API_BASE"].rstrip("/")  # 含 /api/v1
ADMIN_API_KEY = os.environ["ADMIN_API_KEY"]            # ak_live_...

HEADERS = {
    "X-Admin-Api-Key": ADMIN_API_KEY,
    "Content-Type": "application/json",
}


def list_users(keyword: str | None = None, limit: int = 20):
    params = {"limit": limit}
    if keyword:
        params["keyword"] = keyword
    r = requests.get(f"{UAP_API_BASE}/admin-api/users/", headers=HEADERS, params=params, timeout=10)
    r.raise_for_status()
    return r.json()  # {"total": ..., "items": [...]}


def create_user(mobile: str, password: str, name: str, organization_id: int | None = None):
    body = {"mobile": mobile, "password": password, "name": name}
    if organization_id is not None:
        body["organization_id"] = organization_id
    r = requests.post(f"{UAP_API_BASE}/admin-api/users/", headers=HEADERS, json=body, timeout=10)
    r.raise_for_status()
    return r.json()


def update_user_org(user_id: int, organization_id: int | None):
    # 注意：禁止传 mobile / role / status / is_deleted —— 平台直接 422
    body = {"organization_id": organization_id}
    r = requests.put(
        f"{UAP_API_BASE}/admin-api/users/{user_id}", headers=HEADERS, json=body, timeout=10
    )
    r.raise_for_status()
    return r.json()


def list_orgs():
    r = requests.get(f"{UAP_API_BASE}/admin-api/organizations/", headers=HEADERS, timeout=10)
    r.raise_for_status()
    return r.json()
```

错误处理建议（节选）：

```python
try:
    create_user("13900139000", "Init@2026", "李四", organization_id=7)
except requests.HTTPError as e:
    code = e.response.status_code
    detail = e.response.json().get("detail")
    # 401 → 缺 Header；403 → Key 失效或缺 scope；
    # 400 → 业务错（手机号已注册等）；422 → 字段越权（一般是误传 role/status）；
    # 404 → 路径中的 id 不存在
    log.error("[admin-api] create_user failed: HTTP %s %r", code, detail)
    raise
```

---

## 10. 附录：与 `uap-api` Skill 的并存关系

| 维度 | `uap-api` | `uap-admin-api`（本文档） |
|------|-----------|---------------------------|
| 凭据 | `APP_ID` + `APP_SECRET`（HMAC 签名）/ `APP_ACCESS_TOKEN` | `X-Admin-Api-Key` |
| 路径前缀 | `/simple/*`、`/messages/`、`/apps/mapping/*`、`/users/search` 等 | `/admin-api/users/*`、`/admin-api/organizations/*` |
| 主要场景 | 业务应用对接 UAP（登录、SSO、消息、映射） | 平台运维/总部系统替超管管理 UAP 上的用户与组织 |
| 是否签名 | A/B 两类需要 HMAC-SHA256 签名 | **不签名**，仅 Header 鉴权 |
| 是否多角色 | 应用为发起方 | 单一 Key 角色，scopes 控制粒度 |

把这两套 Skill 当作**两条平行轨**：选错轨 → 平台直接 401/403。

---

*文档版本：与当前仓库后端 Schema/路由一致时，API 前缀为 `/api/v1`。*
