Skip to content

API: Digital Humans ​

Halo HTTP API for Digital Humans: install and uninstall, pause and resume, configure, read run history and activity, and chat with them.

Digital Humans (app in the API) start work on a schedule, on events, or when someone messages them. Use these operations to install and remove them, pause and resume them, change their configuration, read their run history and activity, and chat with them.

Paths are relative to your Halo base URL, and every request carries the access password. Set $HALO_URL and $HALO_TOKEN as in the quickstart; base URL, authentication and error conventions are in the HTTP API overview.

OperationEndpoint
List digital humans (and other installed apps) in a spaceGET /api/apps
Get one installed digital human (or other app) by idGET /api/apps/:appId
Soft-delete (uninstall) a digital human; recoverable via reinstallDELETE /api/apps/:appId
Get the activity/run-history entries for a digital humanGET /api/apps/:appId/activity
List skills this digital human can load (global + space-local)GET /api/apps/:appId/available-skills
Clear a digital human's chat history IrreversiblePOST /api/apps/:appId/chat/clear
Read a digital human chat transcriptGET /api/apps/:appId/chat/messages
Restart a digital human's chat agent process (reloads system prompt and config)POST /api/apps/:appId/chat/restart
Send a message to a digital human and let it replyPOST /api/apps/:appId/chat/send
Check whether a digital human chat is currently generating a replyGET /api/apps/:appId/chat/status
Stop a digital human's in-progress chat replyPOST /api/apps/:appId/chat/stop
Clear the history of all of a digital human's chats at once IrreversiblePOST /api/apps/:appId/chats/clear-all
Count the conversations clearing all of a digital human's chats would clearGET /api/apps/:appId/chats/clearable
Delete all of a digital human's memory files IrreversiblePOST /api/apps/:appId/clear-memory
Replace a digital human's user configuration valuesPOST /api/apps/:appId/config
Answer a digital human's pending question (it stopped to ask a human before continuing) IrreversiblePOST /api/apps/:appId/escalation/:entryId/respond
Change how often a digital human runs, without editing the rest of its definition (spec)POST /api/apps/:appId/frequency
Clear a digital human's chat history in a bound IM chat IrreversiblePOST /api/apps/:appId/im-chat/clear
Read a digital human's transcript in a bound IM chatGET /api/apps/:appId/im-chat/messages
Stop a digital human's in-progress reply in a bound IM chat (keeps history)POST /api/apps/:appId/im-chat/stop
Read the state of a digital human's memoryGET /api/apps/:appId/memory
Consolidate a digital human's memory nowPOST /api/apps/:appId/memory/consolidate
Set which space a digital human starts new work inPOST /api/apps/:appId/move-space
Merge-patch per-installation overrides (notification level, model)PATCH /api/apps/:appId/overrides
Pause a digital human (stops scheduling)POST /api/apps/:appId/pause
Permanently delete a digital human and all its data IrreversibleDELETE /api/apps/:appId/permanent
Grant a named permission to a digital humanPOST /api/apps/:appId/permissions/grant
Revoke a named permission from a digital humanPOST /api/apps/:appId/permissions/revoke
Reinstall a previously soft-deleted (uninstalled) digital humanPOST /api/apps/:appId/reinstall
List the reminders a digital human set in its conversationsGET /api/apps/:appId/reminders
Cancel a reminder a digital human set in one of its conversations IrreversibleDELETE /api/apps/:appId/reminders/:reminderId
Resume a paused digital humanPOST /api/apps/:appId/resume
Resume a run that stopped prematurely on an error IrreversiblePOST /api/apps/:appId/runs/:runId/continue
Inject a text message into a currently active run IrreversiblePOST /api/apps/:appId/runs/:runId/inject
Read what a run actually did, step by stepGET /api/apps/:appId/runs/:runId/session
Start a new chat thread with a digital humanPOST /api/apps/:appId/sessions/create
Delete a chat thread with a digital human IrreversiblePOST /api/apps/:appId/sessions/delete
Branch an existing chat thread into a new one, copying its historyPOST /api/apps/:appId/sessions/fork
Edit part of a digital human's definition (spec) without replacing all of itPATCH /api/apps/:appId/spec
Get real-time run state of a digital human (running/queued/idle/etc.)GET /api/apps/:appId/state
Manually trigger a digital human to run immediatelyPOST /api/apps/:appId/trigger
Set how a store-installed digital human handles version upgradesPOST /api/apps/:appId/upgrade-strategy
Install a digital human (or other app type) from a YAML definition (spec) stringPOST /api/apps/import-spec
Install a digital human (or other app type) from its full definition (spec) objectPOST /api/apps/install
List every chat thread a digital human has, its own and IM ones (Message channels)GET /api/im-sessions
List the IM chats a digital human pushes to through another digital human's bot (Message channels)GET /api/im-sessions/linked
Remove a chat binding from the IM session registry (Message channels)POST /api/im-sessions/remove
Set a custom display name for a chat binding (Message channels)POST /api/im-sessions/set-custom-name
Toggle whether a run's result is auto-pushed to this chat when it finishes (Message channels)POST /api/im-sessions/set-proactive
Let a digital human push to an IM chat another digital human's bot knows, or stop it (Message channels)POST /api/im-sessions/set-push-link
Bind a knowledge collection so a digital human can read it (Knowledge bases)POST /api/tlon/:kbId/bind-app
Unbind a knowledge collection from a digital human (Knowledge bases)POST /api/tlon/:kbId/unbind-app

List digital humans (and other installed apps) in a space ​

GET /api/apps Assistant

  • 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.

Query parameters: spaceId, status

Response

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

Example

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

Get one installed digital human (or other app) by id ​

GET /api/apps/:appId Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Soft-delete (uninstall) a digital human; recoverable via reinstall ​

DELETE /api/apps/:appId Reversible Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Query parameters: purge

Response

ts
{success:true}

Example

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

Get the activity/run-history entries for a digital human ​

GET /api/apps/:appId/activity Assistant

  • 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).

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Query parameters: before, beforeId, epochId, limit, offset, teamId, type

Response

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

Example

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

List skills this digital human can load (global + space-local) ​

GET /api/apps/:appId/available-skills Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Errors

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

Example

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

Clear a digital human's chat history ​

POST /api/apps/:appId/chat/clear Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true}

Example

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>"}'

Read a digital human chat transcript ​

GET /api/apps/:appId/chat/messages Assistant

  • Reads the main thread by default. For another thread pass ?conversationId=, the same key 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 rather than guessing.
  • thoughts holds the raw tool calls and results behind each reply and is large.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Query parameters: conversationId

Response

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

Example

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

Restart a digital human's chat agent process (reloads system prompt and config) ​

POST /api/apps/:appId/chat/restart Assistant

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Send a message to a digital human and let it reply ​

POST /api/apps/:appId/chat/send Assistant

  • spaceId is the Digital Human's own workspace: the spaceId of its entry in GET /api/apps.
  • conversationId picks the thread. Leave it out for the main thread. For a new thread, use the conversationId that POST /api/apps/:appId/sessions/create returns. To continue a thread the user started in Halo, build it from GET /api/im-sessions as app-chat:&lt;appId&gt;:&lt;channel&gt;:&lt;chatType&gt;:&lt;chatId&gt;.
  • Only threads on the local and http channels are accepted. A thread that belongs to an IM chat is refused, so this API never speaks into a real conversation with a person. A malformed id returns 400 with the expected form; chatId may contain only letters, digits, _ and -.
  • Returns before the Digital Human has read the message. Follow the reply over the WebSocket, or poll GET /api/apps/:appId/chat/status until isGenerating is false and then read GET /api/apps/:appId/chat/messages, passing the same conversationId to both.
  • A message sent while the same thread is still answering doesn't start a second session: it joins the running turn after its current step, or is merged into the next one. Send one message at a time and wait for the reply, so each reply answers one message.
  • Optional body fields: thinkingEnabled, reasoningEffort (off to max; overrides thinkingEnabled, but the Digital Human's own setting wins), images.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

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

Example

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?"}'

Check whether a digital human chat is currently generating a reply ​

GET /api/apps/:appId/chat/status Assistant

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Query parameters: conversationId

Response

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

Example

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

Stop a digital human's in-progress chat reply ​

POST /api/apps/:appId/chat/stop Assistant

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true}

Example

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

Clear the history of all of a digital human's chats at once ​

POST /api/apps/:appId/chats/clear-all Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Count the conversations clearing all of a digital human's chats would clear ​

GET /api/apps/:appId/chats/clearable Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Delete all of a digital human's memory files ​

POST /api/apps/:appId/clear-memory Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Replace a digital human's user configuration values ​

POST /api/apps/:appId/config Reversible Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

json
{
  "someField": "value"
}

Response

ts
{success:true}

Example

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

Answer a digital human's pending question (it stopped to ask a human before continuing) ​

POST /api/apps/:appId/escalation/:entryId/respond Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.
entryIdActivity entry id, from GET /api/apps/:appId/activity.

Request body

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

Response

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

Example

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"}'

Change how often a digital human runs, without editing the rest of its definition (spec) ​

POST /api/apps/:appId/frequency Reversible Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true}

Example

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"}'

Clear a digital human's chat history in a bound IM chat ​

POST /api/apps/:appId/im-chat/clear Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

  • 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".

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true}

Example

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>"}'

Read a digital human's transcript in a bound IM chat ​

GET /api/apps/:appId/im-chat/messages Assistant

  • 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 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?conversationId=.
  • This is real correspondence with a person on the other end — read it only when the task needs it.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Query parameters: channel (required), chatId (required), spaceId (required), chatType

Response

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

Example

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"

Stop a digital human's in-progress reply in a bound IM chat (keeps history) ​

POST /api/apps/:appId/im-chat/stop Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

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

Example

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>"}'

Read the state of a digital human's memory ​

GET /api/apps/:appId/memory Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Consolidate a digital human's memory now ​

POST /api/apps/:appId/memory/consolidate Reversible Assistant

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Set which space a digital human starts new work in ​

POST /api/apps/:appId/move-space Reversible Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true,data:{}}

Example

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>"}'

Merge-patch per-installation overrides (notification level, model) ​

PATCH /api/apps/:appId/overrides Reversible Assistant

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

json
{
  "notificationLevel": "important"
}

Response

ts
{success:true}

Example

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

Pause a digital human (stops scheduling) ​

POST /api/apps/:appId/pause Reversible Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

ts
{success:true}

Errors

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

Example

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

Permanently delete a digital human and all its data ​

DELETE /api/apps/:appId/permanent Irreversible

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it. If you only need something narrower, use DELETE /api/apps/:appId

  • Deletes the Digital Human and all its data. It must be uninstalled first with 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Grant a named permission to a digital human ​

POST /api/apps/:appId/permissions/grant Reversible Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true}

Example

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"}'

Revoke a named permission from a digital human ​

POST /api/apps/:appId/permissions/revoke Reversible Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

ts
{success:true}

Example

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"}'

Reinstall a previously soft-deleted (uninstalled) digital human ​

POST /api/apps/:appId/reinstall Reversible Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Errors

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

Example

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

List the reminders a digital human set in its conversations ​

GET /api/apps/:appId/reminders Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Cancel a reminder a digital human set in one of its conversations ​

DELETE /api/apps/:appId/reminders/:reminderId Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.
reminderIdReminder id, from GET /api/apps/:appId/reminders.

Response

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

Example

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

Resume a paused digital human ​

POST /api/apps/:appId/resume Reversible Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

ts
{success:true}

Errors

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

Example

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

Resume a run that stopped prematurely on an error ​

POST /api/apps/:appId/runs/:runId/continue Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

  • 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.
runIdId of one Digital Human run, the runId in GET /api/apps/:appId/activity.

Response

ts
{success:true}

Example

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

Inject a text message into a currently active run ​

POST /api/apps/:appId/runs/:runId/inject Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.
runIdId of one Digital Human run, the runId in GET /api/apps/:appId/activity.

Request body

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

Response

ts
{success:true}

Errors

  • 400 when text is missing or the run is not active.

Example

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"}'

Read what a run actually did, step by step ​

GET /api/apps/:appId/runs/:runId/session Assistant

  • 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. 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.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.
runIdId of one Digital Human run, the runId in GET /api/apps/:appId/activity.

Response

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

Errors

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

Example

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

Start a new chat thread with a digital human ​

POST /api/apps/:appId/sessions/create Assistant

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Delete a chat thread with a digital human ​

POST /api/apps/:appId/sessions/delete Irreversible Assistant

Irreversible

This cannot be undone, and nothing goes to a trash folder. Make sure it is what the user wants before you call it.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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=>"
}

Response

ts
{success:true}

Errors

  • 400 if spaceId or conversationId is missing.

Example

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=>"}'

Branch an existing chat thread into a new one, copying its history ​

POST /api/apps/:appId/sessions/fork Assistant

  • A digital human's threads are not the conversations in a space — do not take an id from GET /api/spaces/<spaceId>/conversations, it belongs to the user's chats with the Halo assistant and this route will not find it.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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=>"
}

Response

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

Errors

  • 400 if spaceId or sourceConversationId is missing.

Example

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=>"}'

Edit part of a digital human's definition (spec) without replacing all of it ​

PATCH /api/apps/:appId/spec Reversible

  • 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 is simpler.
  • A build can refuse remote changes that touch MCP apps (403).

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

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

Response

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

Example

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 real-time run state of a digital human (running/queued/idle/etc.) ​

GET /api/apps/:appId/state Assistant

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Manually trigger a digital human to run immediately ​

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 and the WebSocket instead of waiting on this call.
  • If a run is already in progress or queued, the call fails with that message.

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Response

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

Example

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

Set how a store-installed digital human handles version upgrades ​

POST /api/apps/:appId/upgrade-strategy Reversible Assistant

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

Path parameters

NameDescription
appIdDigital Human id, from GET /api/apps.

Request body

json
{
  "strategy": "auto"
}

Response

ts
{success:true}

Example

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

Install a digital human (or other app type) from a YAML definition (spec) string ​

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).

Request body

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

Response

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

Errors

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

Example

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>"}'

Install a digital human (or other app type) from its full definition (spec) object ​

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 afterwards.

Request body

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

Response

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

Errors

  • 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.

Example

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."}}'

基于 MIT 协议开源 · Open source under the MIT license