---
title: "数字人 API：安装、配置和对话"
description: "Halo HTTP API 数字人接口：安装和卸载、暂停和恢复、修改配置、查看运行记录与活动，以及与数字人对话。"
type: "reference"
audience: ["developer"]
lang: "zh-CN"
canonical: "https://hello-halo.cc/docs/developers/api/digital-human.html"
last_updated: "2026-10-19T00:11:17+08:00"
source: "Halo 3.0.4 docs"
---
# API：数字人

Halo HTTP API 数字人接口：安装和卸载、暂停和恢复、修改配置、查看运行记录与活动，以及与数字人对话。

数字人按计划、按事件或在收到消息时开始工作（API 中叫 `app`）。用这些接口安装和卸载、暂停和恢复、修改配置、查看运行记录与活动，并与数字人对话。

所有路径都相对于你的 Halo 地址，每个请求都要带上访问密码。示例中的 `$HALO_URL` 和 `$HALO_TOKEN` 按[快速上手](https://hello-halo.cc/docs/developers/quickstart.html)设置；地址、鉴权和错误约定见 [HTTP API 概览](https://hello-halo.cc/docs/developers/api/)。

| 操作 | 端点 |
| --- | --- |
| [列出工作区中的数字人（及其他已安装应用）](#get-api-apps) | `GET /api/apps` |
| [按 ID 获取一个已安装的数字人（或其他应用）](#get-api-apps-appid) | `GET /api/apps/:appId` |
| [卸载数字人（可通过重新安装恢复）](#delete-api-apps-appid) | `DELETE /api/apps/:appId` |
| [获取数字人的活动和运行记录](#get-api-apps-appid-activity) | `GET /api/apps/:appId/activity` |
| [列出数字人可加载的技能（全局和工作区内）](#get-api-apps-appid-available-skills) | `GET /api/apps/:appId/available-skills` |
| [清空与数字人的聊天记录](#post-api-apps-appid-chat-clear) （不可撤销） | `POST /api/apps/:appId/chat/clear` |
| [读取与数字人的聊天记录](#get-api-apps-appid-chat-messages) | `GET /api/apps/:appId/chat/messages` |
| [重启数字人的聊天进程（重新加载系统提示词和配置）](#post-api-apps-appid-chat-restart) | `POST /api/apps/:appId/chat/restart` |
| [给数字人发消息并让它回复](#post-api-apps-appid-chat-send) | `POST /api/apps/:appId/chat/send` |
| [查询数字人聊天是否正在生成回复](#get-api-apps-appid-chat-status) | `GET /api/apps/:appId/chat/status` |
| [停止数字人正在生成的聊天回复](#post-api-apps-appid-chat-stop) | `POST /api/apps/:appId/chat/stop` |
| [一次清空数字人全部聊天的记录](#post-api-apps-appid-chats-clear-all) （不可撤销） | `POST /api/apps/:appId/chats/clear-all` |
| [统计清空数字人全部聊天会涉及的会话数](#get-api-apps-appid-chats-clearable) | `GET /api/apps/:appId/chats/clearable` |
| [删除数字人的全部记忆文件](#post-api-apps-appid-clear-memory) （不可撤销） | `POST /api/apps/:appId/clear-memory` |
| [替换数字人的用户配置值](#post-api-apps-appid-config) | `POST /api/apps/:appId/config` |
| [回答数字人运行中提出的问题](#post-api-apps-appid-escalation-entryid-respond) （不可撤销） | `POST /api/apps/:appId/escalation/:entryId/respond` |
| [修改数字人的运行频率，不改动 spec 的其他部分](#post-api-apps-appid-frequency) | `POST /api/apps/:appId/frequency` |
| [清空数字人在已绑定即时通讯聊天中的记录](#post-api-apps-appid-im-chat-clear) （不可撤销） | `POST /api/apps/:appId/im-chat/clear` |
| [读取数字人在已绑定即时通讯聊天中的记录](#get-api-apps-appid-im-chat-messages) | `GET /api/apps/:appId/im-chat/messages` |
| [停止数字人在已绑定即时通讯聊天中正在生成的回复（保留记录）](#post-api-apps-appid-im-chat-stop) | `POST /api/apps/:appId/im-chat/stop` |
| [读取数字人记忆的状态](#get-api-apps-appid-memory) | `GET /api/apps/:appId/memory` |
| [立即整合数字人的记忆](#post-api-apps-appid-memory-consolidate) | `POST /api/apps/:appId/memory/consolidate` |
| [设置数字人在哪个工作区开始新的工作](#post-api-apps-appid-move-space) | `POST /api/apps/:appId/move-space` |
| [按合并补丁修改单个数字人的覆盖设置（通知级别、模型）](#patch-api-apps-appid-overrides) | `PATCH /api/apps/:appId/overrides` |
| [暂停数字人（停止计划任务）](#post-api-apps-appid-pause) | `POST /api/apps/:appId/pause` |
| [永久删除数字人及其全部数据](#delete-api-apps-appid-permanent) （不可撤销） | `DELETE /api/apps/:appId/permanent` |
| [给数字人授予一项权限](#post-api-apps-appid-permissions-grant) | `POST /api/apps/:appId/permissions/grant` |
| [撤销数字人的一项权限](#post-api-apps-appid-permissions-revoke) | `POST /api/apps/:appId/permissions/revoke` |
| [重新安装已卸载的数字人](#post-api-apps-appid-reinstall) | `POST /api/apps/:appId/reinstall` |
| [列出数字人在会话中设置的提醒](#get-api-apps-appid-reminders) | `GET /api/apps/:appId/reminders` |
| [取消数字人在会话中设置的一个提醒](#delete-api-apps-appid-reminders-reminderid) （不可撤销） | `DELETE /api/apps/:appId/reminders/:reminderId` |
| [恢复已暂停的数字人](#post-api-apps-appid-resume) | `POST /api/apps/:appId/resume` |
| [继续一次因出错而提前停止的运行](#post-api-apps-appid-runs-runid-continue) （不可撤销） | `POST /api/apps/:appId/runs/:runId/continue` |
| [向正在进行的运行插入一条消息](#post-api-apps-appid-runs-runid-inject) （不可撤销） | `POST /api/apps/:appId/runs/:runId/inject` |
| [逐步查看一次运行实际做了什么](#get-api-apps-appid-runs-runid-session) | `GET /api/apps/:appId/runs/:runId/session` |
| [与数字人开始一个新的聊天线程](#post-api-apps-appid-sessions-create) | `POST /api/apps/:appId/sessions/create` |
| [删除与数字人的一个聊天线程](#post-api-apps-appid-sessions-delete) （不可撤销） | `POST /api/apps/:appId/sessions/delete` |
| [把现有聊天线程连同记录复制成一个新线程](#post-api-apps-appid-sessions-fork) | `POST /api/apps/:appId/sessions/fork` |
| [修改数字人 spec 的部分字段，不整体替换](#patch-api-apps-appid-spec) | `PATCH /api/apps/:appId/spec` |
| [获取数字人的实时运行状态（运行中、排队、空闲等）](#get-api-apps-appid-state) | `GET /api/apps/:appId/state` |
| [手动让数字人立即运行](#post-api-apps-appid-trigger) | `POST /api/apps/:appId/trigger` |
| [设置从商店安装的数字人如何处理版本升级](#post-api-apps-appid-upgrade-strategy) | `POST /api/apps/:appId/upgrade-strategy` |
| [用 YAML 格式的 spec 文本安装数字人（或其他类型的应用）](#post-api-apps-import-spec) | `POST /api/apps/import-spec` |
| [用完整的 spec 对象安装数字人（或其他类型的应用）](#post-api-apps-install) | `POST /api/apps/install` |
| [列出数字人的全部聊天线程，包括它自己的和即时通讯中的](https://hello-halo.cc/docs/developers/api/channels.html#get-api-im-sessions) (消息通道) | `GET /api/im-sessions` |
| [列出数字人借另一位数字人的机器人推送到的聊天](https://hello-halo.cc/docs/developers/api/channels.html#get-api-im-sessions-linked) (消息通道) | `GET /api/im-sessions/linked` |
| [从会话登记中移除一个聊天绑定](https://hello-halo.cc/docs/developers/api/channels.html#post-api-im-sessions-remove) (消息通道) | `POST /api/im-sessions/remove` |
| [为聊天绑定设置自定义显示名称](https://hello-halo.cc/docs/developers/api/channels.html#post-api-im-sessions-set-custom-name) (消息通道) | `POST /api/im-sessions/set-custom-name` |
| [设置运行结束后是否自动把结果推送到这个聊天](https://hello-halo.cc/docs/developers/api/channels.html#post-api-im-sessions-set-proactive) (消息通道) | `POST /api/im-sessions/set-proactive` |
| [允许或停止数字人借另一位数字人的机器人推送到某个聊天](https://hello-halo.cc/docs/developers/api/channels.html#post-api-im-sessions-set-push-link) (消息通道) | `POST /api/im-sessions/set-push-link` |
| [绑定知识库，让数字人可以读取](https://hello-halo.cc/docs/developers/api/knowledge-base.html#post-api-tlon-kbid-bind-app) (知识库) | `POST /api/tlon/:kbId/bind-app` |
| [解除知识库与数字人的绑定](https://hello-halo.cc/docs/developers/api/knowledge-base.html#post-api-tlon-kbid-unbind-app) (知识库) | `POST /api/tlon/:kbId/unbind-app` |

## 列出工作区中的数字人（及其他已安装应用） {#get-api-apps}

`GET /api/apps` （助手可调用）

- Optional query parameters: `spaceId` (only that workspace) and `status`. Without `status`, uninstalled Digital Humans are left out. Without `spaceId`, the list covers every workspace, as the Halo app shows it.
- `userConfig` and the `env` and `headers` of an MCP app's `spec.mcp_server` can hold credentials in plain text, and this API returns them as stored. Treat the response like a secret.

**查询参数**：`spaceId`、`status`

**响应**

```ts
{success:true,data:[{id,specId,spaceId,spec,status,userConfig,userOverrides,permissions,installedAt,lastRunAt?,lastRunOutcome?,errorMessage?}]}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 按 ID 获取一个已安装的数字人（或其他应用） {#get-api-apps-appid}

`GET /api/apps/:appId` （助手可调用）

- `userConfig` and the `env` and `headers` of an MCP app's `spec.mcp_server` can hold credentials in plain text, and this API returns them as stored. Treat the response like a secret.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:InstalledApp|null}  // same shape as the entries of GET /api/apps
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 卸载数字人（可通过重新安装恢复） {#delete-api-apps-appid}

`DELETE /api/apps/:appId` （可撤销） （助手可调用）

- Uninstalls the Digital Human but keeps its data, so [`POST /api/apps/:appId/reinstall`](#post-api-apps-appid-reinstall) brings it back. The `purge=true` query parameter is accepted but currently has no effect.
- To delete it for good, call [`DELETE /api/apps/:appId/permanent`](#delete-api-apps-appid-permanent) afterwards.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**查询参数**：`purge`

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX DELETE "$HALO_URL/api/apps/<appId>" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 获取数字人的活动和运行记录 {#get-api-apps-appid-activity}

`GET /api/apps/:appId/activity` （助手可调用）

- Optional query parameters: `limit` (most entries), `before` (only entries older than this time, epoch ms), `beforeId`, `offset`, `type` (`run_complete`, `run_skipped`, `run_error`, `milestone`, `escalation` or `output`), and `teamId` with `epochId` (entries of one team run).

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**查询参数**：`before`、`beforeId`、`epochId`、`limit`、`offset`、`teamId`、`type`

**响应**

```ts
{success:true,data:[{id,appId,runId,type:"run_complete"|"run_skipped"|"run_error"|"milestone"|"escalation"|"output",ts,content,userResponse?}]}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/activity" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 列出数字人可加载的技能（全局和工作区内） {#get-api-apps-appid-available-skills}

`GET /api/apps/:appId/available-skills` （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:[{name,description,scope:"global"|"space",dirName,path}]}
```

**错误**

- 404-equivalent = {success:false,error:"App not found or has no space"} — the app must have a spaceId

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/available-skills" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 清空与数字人的聊天记录 {#post-api-apps-appid-chat-clear}

`POST /api/apps/:appId/chat/clear` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- spaceId is required even though the app is already scoped to one. Optional conversationId clears one specific session instead of the default one.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "spaceId": "<spaceId — the app's own spaceId from GET /api/apps>"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/chat/clear" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId — the app'\''s own spaceId from GET /api/apps>"}'
```

## 读取与数字人的聊天记录 {#get-api-apps-appid-chat-messages}

`GET /api/apps/:appId/chat/messages` （助手可调用）

- Reads the main thread by default. For another thread pass `?conversationId=`, the same key [`POST /api/apps/:appId/chat/send`](#post-api-apps-appid-chat-send) takes.
- An empty list means the thread has no history yet, or the id is wrong — the two look the same. List the threads with [`GET /api/im-sessions`](https://hello-halo.cc/docs/developers/api/channels.html#get-api-im-sessions) rather than guessing.
- `thoughts` holds the raw tool calls and results behind each reply and is large.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**查询参数**：`conversationId`

**响应**

```ts
{success:true,data:[{id,role:"user"|"assistant",content,timestamp,thoughts?,thoughtsSummary?,images?}]}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/chat/messages" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 重启数字人的聊天进程（重新加载系统提示词和配置） {#post-api-apps-appid-chat-restart}

`POST /api/apps/:appId/chat/restart` （助手可调用）

- Interrupts any in-flight turn. Conversation history is preserved.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{sessionsClosed:number}}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/chat/restart" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 给数字人发消息并让它回复 {#post-api-apps-appid-chat-send}

`POST /api/apps/:appId/chat/send` （助手可调用）

- `spaceId` 是数字人自己的工作区：[`GET /api/apps`](#get-api-apps) 中它那一项的 `spaceId`。
- `conversationId` 决定消息进入哪个线程。不传时进入主线程。新建线程时，使用 [`POST /api/apps/:appId/sessions/create`](#post-api-apps-appid-sessions-create) 返回的 `conversationId`。要接着用户在 Halo 里开始的线程，用 [`GET /api/im-sessions`](https://hello-halo.cc/docs/developers/api/channels.html#get-api-im-sessions) 的数据拼成 `app-chat:&lt;appId&gt;:&lt;channel&gt;:&lt;chatType&gt;:&lt;chatId&gt;`。
- 只接受 `local` 和 `http` 通道上的线程。属于 IM 聊天的线程会被拒绝，所以这个 API 永远不会替你在与真人的对话里发言。格式错误的标识返回 400 并给出正确格式；`chatId` 只能包含字母、数字、`_` 和 `-`。
- 在数字人读到消息之前就会返回。通过 WebSocket 跟进回复，或轮询 [`GET /api/apps/:appId/chat/status`](#get-api-apps-appid-chat-status) 直到 `isGenerating` 为 false，再读取 [`GET /api/apps/:appId/chat/messages`](#get-api-apps-appid-chat-messages)，两者都传同一个 `conversationId`。
- 同一线程还在回复时发来的消息不会另起一个处理过程：它会在当前步骤之后并入正在进行的回复，或等这一轮结束后合并处理。请一次发一条，等回复完成再发下一条，这样每条回复对应一条消息。
- 可选的请求体字段：`thinkingEnabled`、`reasoningEffort`（`off` 到 `max`；会覆盖 `thinkingEnabled`，但数字人自己的设置优先）、`images`。

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "spaceId": "<spaceId>",
  "message": "What did you find today?"
}
```

**响应**

```ts
{success:true,data:{conversationId}}  // accepted, not answered
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/chat/send" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId>","message":"What did you find today?"}'
```

## 查询数字人聊天是否正在生成回复 {#get-api-apps-appid-chat-status}

`GET /api/apps/:appId/chat/status` （助手可调用）

- Checks the main thread by default; pass `?conversationId=` for another thread, the same key [`POST /api/apps/:appId/chat/send`](#post-api-apps-appid-chat-send) takes.
- Poll with a growing interval (2 s, then double) instead of a fixed wait.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**查询参数**：`conversationId`

**响应**

```ts
{success:true,data:{isGenerating:boolean,conversationId}}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/chat/status" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 停止数字人正在生成的聊天回复 {#post-api-apps-appid-chat-stop}

`POST /api/apps/:appId/chat/stop` （助手可调用）

- Name the thread to stop with `conversationId`. To stop every thread of this Digital Human, send `{"all": true}` instead. Sending neither returns 400.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "conversationId": "<conversationId>"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/chat/stop" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"conversationId":"<conversationId>"}'
```

## 一次清空数字人全部聊天的记录 {#post-api-apps-appid-chats-clear-all}

`POST /api/apps/:appId/chats/clear-all` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- Each chat is cleared as /clear clears it and starts afresh with its next message; replies in progress stop. API sessions, team chats, memory and reminders are left alone. Nothing is posted into the chats.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{cleared:number,failed:number}}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/chats/clear-all" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 统计清空数字人全部聊天会涉及的会话数 {#get-api-apps-appid-chats-clearable}

`GET /api/apps/:appId/chats/clearable` （助手可调用）

- total: its default and local chats and its IM chats that have history (im of them in IM); API sessions and team chats are not counted. teamIm: IM chats with history that a team answers — a clear-all leaves them alone; /clear in each clears it.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{total:number,im:number,teamIm:number}}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/chats/clearable" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 删除数字人的全部记忆文件 {#post-api-apps-appid-clear-memory}

`POST /api/apps/:appId/clear-memory` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{filesRemoved:number}}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/clear-memory" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 替换数字人的用户配置值 {#post-api-apps-appid-config}

`POST /api/apps/:appId/config` （可撤销） （助手可调用）

- Body is the full config object, keyed by config_schema field id. This replaces userConfig wholesale; read current values first with GET /:appId to avoid dropping fields.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "someField": "value"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/config" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"someField":"value"}'
```

## 回答数字人运行中提出的问题 {#post-api-apps-appid-escalation-entryid-respond}

`POST /api/apps/:appId/escalation/:entryId/respond` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- Answers a question a Digital Human asked during a run (an activity entry of type `escalation`; the Digital Human's status is `waiting_user` while it waits). Send `choice`, `text`, or both, as the question asked for; for a question with several parts, send `answers`, one `{choice?, text?}` per part, in order.
- This is the human decision the run was waiting for. Send the user's actual answer: it cannot be withdrawn afterwards.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |
| `entryId` | 活动记录 ID，来自 [`GET /api/apps/:appId/activity`](#get-api-apps-appid-activity)。 |

**请求体**

```json
{
  "choice": "approve",
  "text": "optional free-text explanation"
}
```

**响应**

```ts
{success:true,data:{…the answered activity entry}} | {success:false,error}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/escalation/<entryId>/respond" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"choice":"approve","text":"optional free-text explanation"}'
```

## 修改数字人的运行频率，不改动 spec 的其他部分 {#post-api-apps-appid-frequency}

`POST /api/apps/:appId/frequency` （可撤销） （助手可调用）

- frequency can also be a cron expression (e.g. "0 8 * * *"); either one replaces the other. subscriptionId must name an existing schedule subscription — read app.spec.subscriptions via GET /:appId first. This rewrites that subscription's schedule in the spec, which is the only interval the scheduler reads and the one the settings panel shows; the rest of the spec is untouched.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "subscriptionId": "<subscriptionId — read app.spec.subscriptions via GET /api/apps/<appId>>",
  "frequency": "30m"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/frequency" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"subscriptionId":"<subscriptionId — read app.spec.subscriptions via GET /api/apps/<appId>>","frequency":"30m"}'
```

## 清空数字人在已绑定即时通讯聊天中的记录 {#post-api-apps-appid-im-chat-clear}

`POST /api/apps/:appId/im-chat/clear` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- All four body fields are required, 400 otherwise. channel, chatType and chatId are checked as for GET im-chat/messages; chatType is "direct" or "group".

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "spaceId": "<spaceId — the app's own spaceId from GET /api/apps>",
  "channel": "wecom-bot",
  "chatType": "direct",
  "chatId": "<chatId — from GET /api/im-sessions>"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/im-chat/clear" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId — the app'\''s own spaceId from GET /api/apps>","channel":"wecom-bot","chatType":"direct","chatId":"<chatId — from GET /api/im-sessions>"}'
```

## 读取数字人在已绑定即时通讯聊天中的记录 {#get-api-apps-appid-im-chat-messages}

`GET /api/apps/:appId/im-chat/messages` （助手可调用）

- All three query params are required, 400 otherwise. spaceId must be the app's own — a different but valid one is accepted and reads the wrong space. Optional ?chatType=group, defaults to direct.
- channel is the session's IM channel (wecom-bot, feishu-bot, dingtalk-bot, weixin-ilink-bot); chatId is taken as [`GET /api/im-sessions`](https://hello-halo.cc/docs/developers/api/channels.html#get-api-im-sessions) gives it. Any other channel, chatType or a chatId with whitespace, ":", "/", "\" or ".." is a 400. Read "http" and "local" sessions with [`GET /api/apps/:appId/chat/messages`](#get-api-apps-appid-chat-messages)?conversationId=.
- This is real correspondence with a person on the other end — read it only when the task needs it.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**查询参数**：`channel`（必填）、`chatId`（必填）、`spaceId`（必填）、`chatType`

**响应**

```ts
{success:true,data:[{id,role:"user"|"assistant",content,timestamp,thoughts?,thoughtsSummary?,images?}]}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/im-chat/messages?channel=wecom-bot&chatId=<chatId — from GET /api/im-sessions>&spaceId=<spaceId — the app's own spaceId from GET /api/apps>" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 停止数字人在已绑定即时通讯聊天中正在生成的回复（保留记录） {#post-api-apps-appid-im-chat-stop}

`POST /api/apps/:appId/im-chat/stop` （助手可调用）

- channel, chatType and chatId are checked as for GET im-chat/messages; chatType is "direct" or "group". Unlike im-chat/clear, this only aborts the current turn — the session and its history continue on the next inbound message.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "channel": "wecom-bot",
  "chatType": "direct",
  "chatId": "<chatId — from GET /api/im-sessions>"
}
```

**响应**

```ts
{success:true,data:{stopped:boolean}}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/im-chat/stop" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"channel":"wecom-bot","chatType":"direct","chatId":"<chatId — from GET /api/im-sessions>"}'
```

## 读取数字人记忆的状态 {#get-api-apps-appid-memory}

`GET /api/apps/:appId/memory` （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{exists,totalBytes,topicCount,lastConsolidatedAt,lastAttempt,consolidating}}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/memory" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 立即整合数字人的记忆 {#post-api-apps-appid-memory-consolidate}

`POST /api/apps/:appId/memory/consolidate` （可撤销） （助手可调用）

- Returns once started; runs in the background with the digital human's model. Keeps a restorable snapshot.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{started:boolean,reason?:"already-running"|"empty"|"not-found"}}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/memory/consolidate" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 设置数字人在哪个工作区开始新的工作 {#post-api-apps-appid-move-space}

`POST /api/apps/:appId/move-space` （可撤销） （助手可调用）

- Changes the default only: work already running, existing chat sessions and team work keep the environment they started in. A digital human must belong to a space, so newSpaceId is required — null is rejected. Nothing is stopped or restarted. 404 if appId does not exist; 400 if newSpaceId is an empty string.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "newSpaceId": "<spaceId — a uuid from GET /api/spaces>"
}
```

**响应**

```ts
{success:true,data:{}}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/move-space" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"newSpaceId":"<spaceId — a uuid from GET /api/spaces>"}'
```

## 按合并补丁修改单个数字人的覆盖设置（通知级别、模型） {#patch-api-apps-appid-overrides}

`PATCH /api/apps/:appId/overrides` （可撤销） （助手可调用）

- JSON Merge Patch: send only the fields to change; `null` clears one (for example `{"modelSourceId":null}` falls back to the global model).
- Fields: `notificationLevel`, `modelSourceId`, `modelId`, `spaceMemoryAccess` (true lets the Digital Human read its workspace's memory topics), `chatReasoningEffort` (`off` to `max`, for its chats, IM replies and runs), `memory` (`{enabled, autoConsolidate, cadence}`, always the whole object; `cadence` is `diligent`, `balanced` or `economical`) and `disabledTools` (MCP apps only: tool names to leave out, the whole list each time; `[]` or `null` turns them all back on).
- A `modelSourceId` for an account that no longer exists is accepted; the Digital Human then runs on the current global account until the override changes.
- The run schedule is not set here; use [`POST /api/apps/:appId/frequency`](#post-api-apps-appid-frequency).

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "notificationLevel": "important"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX PATCH "$HALO_URL/api/apps/<appId>/overrides" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notificationLevel":"important"}'
```

## 暂停数字人（停止计划任务） {#post-api-apps-appid-pause}

`POST /api/apps/:appId/pause` （可撤销） （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true}
```

**错误**

- 404-equivalent: manager.pause throws for an unknown appId

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/pause" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 永久删除数字人及其全部数据 {#delete-api-apps-appid-permanent}

`DELETE /api/apps/:appId/permanent` （不可撤销）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
只想做范围更小的操作时，改用 [`DELETE /api/apps/:appId`](#delete-api-apps-appid)
:::

- Deletes the Digital Human and all its data. It must be uninstalled first with [`DELETE /api/apps/:appId`](#delete-api-apps-appid); otherwise the call fails.
- Built-in Digital Humans cannot be deleted. Failures carry `errorName` so a client can tell the cases apart.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true} | {success:false,error,errorName}
```

**示例**

```bash
curl -sX DELETE "$HALO_URL/api/apps/<appId>/permanent" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 给数字人授予一项权限 {#post-api-apps-appid-permissions-grant}

`POST /api/apps/:appId/permissions/grant` （可撤销） （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "permission": "ai-browser"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/permissions/grant" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"permission":"ai-browser"}'
```

## 撤销数字人的一项权限 {#post-api-apps-appid-permissions-revoke}

`POST /api/apps/:appId/permissions/revoke` （可撤销） （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "permission": "ai-browser"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/permissions/revoke" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"permission":"ai-browser"}'
```

## 重新安装已卸载的数字人 {#post-api-apps-appid-reinstall}

`POST /api/apps/:appId/reinstall` （可撤销） （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{activationWarning?:string}} — activationWarning is set (non-fatal) if restarting it failed
```

**错误**

- 404-equivalent: manager.reinstall throws if appId was never installed or is not currently uninstalled

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/reinstall" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 列出数字人在会话中设置的提醒 {#get-api-apps-appid-reminders}

`GET /api/apps/:appId/reminders` （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:[{id,conversationId,conversation:{kind,name?},message,schedule,nextAt,createdAt,setBy?}]}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/reminders" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 取消数字人在会话中设置的一个提醒 {#delete-api-apps-appid-reminders-reminderid}

`DELETE /api/apps/:appId/reminders/:reminderId` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |
| `reminderId` | 提醒 ID，来自 [`GET /api/apps/:appId/reminders`](#get-api-apps-appid-reminders)。 |

**响应**

```ts
{success:true} | {success:false,error:"Reminder not found"}
```

**示例**

```bash
curl -sX DELETE "$HALO_URL/api/apps/<appId>/reminders/<reminderId>" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 恢复已暂停的数字人 {#post-api-apps-appid-resume}

`POST /api/apps/:appId/resume` （可撤销） （助手可调用）

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true}
```

**错误**

- 404-equivalent: manager.resume throws for an unknown appId

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/resume" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 继续一次因出错而提前停止的运行 {#post-api-apps-appid-runs-runid-continue}

`POST /api/apps/:appId/runs/:runId/continue` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- Only valid for a run that ended in a premature-stop error, not any run. This re-opens the run with full tool permissions (bypassPermissions) and can take real-world action that Halo cannot undo.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |
| `runId` | 数字人一次运行的 ID，即 [`GET /api/apps/:appId/activity`](#get-api-apps-appid-activity) 中的 runId。 |

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/runs/<runId>/continue" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 向正在进行的运行插入一条消息 {#post-api-apps-appid-runs-runid-inject}

`POST /api/apps/:appId/runs/:runId/inject` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- The message reaches the running Digital Human, which keeps its full tool permissions and can act on it in ways Halo cannot undo.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |
| `runId` | 数字人一次运行的 ID，即 [`GET /api/apps/:appId/activity`](#get-api-apps-appid-activity) 中的 runId。 |

**请求体**

```json
{
  "text": "Also check the March invoices"
}
```

**响应**

```ts
{success:true}
```

**错误**

- 400 when `text` is missing or the run is not active.

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/runs/<runId>/inject" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"Also check the March invoices"}'
```

## 逐步查看一次运行实际做了什么 {#get-api-apps-appid-runs-runid-session}

`GET /api/apps/:appId/runs/:runId/session` （助手可调用）

- The transcript behind the "View process" panel — same shape as chat/messages, but for one automation run. It is the only way to see why a run did what it did rather than what it reported.
- Get the runId from [`GET /api/apps/<appId>/activity`](#get-api-apps-appid-activity). Read this when a run went wrong; for what it concluded, its activity entry is shorter and enough.
- Only a person's newest 200 runs keep their transcript. An older run answers {success:false,code:"RUN_PROCESS_CLEARED"}; its activity entry still says what it did, and it can no longer be continued.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |
| `runId` | 数字人一次运行的 ID，即 [`GET /api/apps/:appId/activity`](#get-api-apps-appid-activity) 中的 runId。 |

**响应**

```ts
{success:true,data:[{id,role:"user"|"assistant",content,timestamp,thoughts?,thoughtsSummary?,images?}]}
```

**错误**

- 404 when the app or its space is gone. An empty array means that run left no transcript, not that the ids were wrong.

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/runs/<runId>/session" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 与数字人开始一个新的聊天线程 {#post-api-apps-appid-sessions-create}

`POST /api/apps/:appId/sessions/create` （助手可调用）

- Returns a new thread's `conversationId`, ready to pass to [`POST /api/apps/:appId/chat/send`](#post-api-apps-appid-chat-send) and [`GET /api/apps/:appId/chat/messages`](#get-api-apps-appid-chat-messages). Give each end user a thread of their own to keep their contexts apart.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{conversationId}}  // already in the form chat/send takes
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/sessions/create" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 删除与数字人的一个聊天线程 {#post-api-apps-appid-sessions-delete}

`POST /api/apps/:appId/sessions/delete` （不可撤销） （助手可调用）

::: danger 不可撤销
执行后无法撤销，数据也不会进入回收站。调用前请确认这是用户想要的结果。
:::

- The id names one of this digital human's own threads, never a conversation from [`GET /api/spaces/<spaceId>/conversations`](https://hello-halo.cc/docs/developers/api/conversation.html#get-api-spaces-spaceid-conversations).

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "spaceId": "<spaceId — the app's own spaceId from GET /api/apps>",
  "conversationId": "<conversationId — a thread of THIS app, from GET /api/im-sessions?appId=>"
}
```

**响应**

```ts
{success:true}
```

**错误**

- 400 if spaceId or conversationId is missing.

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/sessions/delete" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId — the app'\''s own spaceId from GET /api/apps>","conversationId":"<conversationId — a thread of THIS app, from GET /api/im-sessions?appId=>"}'
```

## 把现有聊天线程连同记录复制成一个新线程 {#post-api-apps-appid-sessions-fork}

`POST /api/apps/:appId/sessions/fork` （助手可调用）

- A digital human's threads are not the conversations in a space — do not take an id from [`GET /api/spaces/<spaceId>/conversations`](https://hello-halo.cc/docs/developers/api/conversation.html#get-api-spaces-spaceid-conversations), it belongs to the user's chats with the Halo assistant and this route will not find it.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "spaceId": "<spaceId — the app's own spaceId from GET /api/apps>",
  "sourceConversationId": "<conversationId — a thread of THIS app, from POST /api/apps/<appId>/sessions/create or assembled from GET /api/im-sessions?appId=>"
}
```

**响应**

```ts
{success:true,data:{conversationId}}
```

**错误**

- 400 if spaceId or sourceConversationId is missing.

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/sessions/fork" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId — the app'\''s own spaceId from GET /api/apps>","sourceConversationId":"<conversationId — a thread of THIS app, from POST /api/apps/<appId>/sessions/create or assembled from GET /api/im-sessions?appId=>"}'
```

## 修改数字人 spec 的部分字段，不整体替换 {#patch-api-apps-appid-spec}

`PATCH /api/apps/:appId/spec` （可撤销）

- JSON Merge Patch on the top-level spec fields: each field you send replaces the stored one, `null` removes it, and the merged spec is validated again.
- A changed `subscriptions` takes effect without interrupting a run in progress. To change only how often it runs, [`POST /api/apps/:appId/frequency`](#post-api-apps-appid-frequency) is simpler.
- A build can refuse remote changes that touch MCP apps (403).

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "description": "Checks the product page every weekday at 9:00"
}
```

**响应**

```ts
{success:true} | {success:false,error}
```

**示例**

```bash
curl -sX PATCH "$HALO_URL/api/apps/<appId>/spec" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"description":"Checks the product page every weekday at 9:00"}'
```

## 获取数字人的实时运行状态（运行中、排队、空闲等） {#get-api-apps-appid-state}

`GET /api/apps/:appId/state` （助手可调用）

- More detail than the "status" field on GET /:appId alone (adds nextRunAtMs, queued/idle distinction)

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{status:"running"|"queued"|"idle"|"paused"|"waiting_user"|"error",nextRunAtMs?,runningRunId?,lastRunAtMs?,lastStatus?,lastError?,consecutiveErrors?,pendingEscalationId?}}
```

**示例**

```bash
curl -s "$HALO_URL/api/apps/<appId>/state" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 手动让数字人立即运行 {#post-api-apps-appid-trigger}

`POST /api/apps/:appId/trigger`

- Runs the Digital Human now and answers only after the run has finished, which can take minutes. Use a long client timeout, or follow the run with [`GET /api/apps/:appId/state`](#get-api-apps-appid-state) and the WebSocket instead of waiting on this call.
- If a run is already in progress or queued, the call fails with that message.

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**响应**

```ts
{success:true,data:{appId,runId,sessionKey,outcome:"useful"|"noop"|"error"|"skipped",startedAt,finishedAt,durationMs,tokensUsed?,errorMessage?,finalText?}} | {success:false,error}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/trigger" \
  -H "Authorization: Bearer $HALO_TOKEN"
```

## 设置从商店安装的数字人如何处理版本升级 {#post-api-apps-appid-upgrade-strategy}

`POST /api/apps/:appId/upgrade-strategy` （可撤销） （助手可调用）

- strategy is one of "auto" (silent patch/minor, notify on major), "notify" (always notify), or "manual" (no automatic checks)

**路径参数**

| 名称 | 说明 |
| --- | --- |
| `appId` | 数字人 ID，来自 [`GET /api/apps`](#get-api-apps)。 |

**请求体**

```json
{
  "strategy": "auto"
}
```

**响应**

```ts
{success:true}
```

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/<appId>/upgrade-strategy" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"strategy":"auto"}'
```

## 用 YAML 格式的 spec 文本安装数字人（或其他类型的应用） {#post-api-apps-import-spec}

`POST /api/apps/import-spec`

- Installs a Digital Human (or another app type) from the text of a `spec.yaml`, validated the same way as in the app. Optional `userConfig` fills its configuration fields.
- Unlike installing in the Halo app or through its assistant, this call does not install the skills the spec requires and does not undo a half-finished install.
- A build can refuse remote installs of MCP apps (403).

**请求体**

```json
{
  "spaceId": "<spaceId>",
  "yamlContent": "<contents of spec.yaml>"
}
```

**响应**

```ts
{success:true,data:{appId}} | {success:false,error,code}
```

**错误**

- 400 when `spaceId` or `yamlContent` is missing or invalid; other failures carry a `code`.

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/import-spec" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId>","yamlContent":"<contents of spec.yaml>"}'
```

## 用完整的 spec 对象安装数字人（或其他类型的应用） {#post-api-apps-install}

`POST /api/apps/install`

- Installs a Digital Human (or a skill or MCP app) from a full spec object, the parsed form of a `spec.yaml`. `spaceId: null` installs a skill or MCP app for every workspace. `userConfig` is optional.
- Unlike installing in the Halo app or through its assistant, this call does not install the skills the spec requires and does not undo a half-finished install. Check [`GET /api/apps/:appId`](#get-api-apps-appid) afterwards.

**请求体**

```ts
{"spaceId": "<spaceId> | null", "spec": {…the parsed spec.yaml…}, "userConfig": {…}}
```

**响应**

```ts
{success:true,data:{appId}} | {success:false,error,code?}
```

**错误**

- 400 missing `spec` or a `spaceId` that is neither a string nor null, 403 `MCP_COMMAND_BLOCKED` (an MCP command this build's policy blocks), 409 `ALREADY_INSTALLED`, 503 `NOT_INITIALIZED`.

**示例**

```bash
curl -sX POST "$HALO_URL/api/apps/install" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId>","spec":{"name":"Price watcher","version":"1.0.0","author":"example","description":"Checks a product page every morning","type":"automation","system_prompt":"Check the product page and report price changes."}}'
```
