Skip to content

API: Store ​

Halo HTTP API for the Store: browse and search listings, install and uninstall Digital Humans and skills, and manage Store sources and updates.

Browse and search the Store, read a listing, install and uninstall Digital Humans and skills, install from a local .dhpkg file, and manage Store sources and updates. Publishing happens in the Halo app; see Publish to the Store.

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
Set how a store-installed digital human handles version upgrades (Digital Humans)POST /api/apps/:appId/upgrade-strategy
Preview the slash-command name a skill title would install underGET /api/skills/command-name
Get a listing's SKILL.md/README documentGET /api/store/app-document
List store listings (legacy, non-paginated; prefer POST /api/store/query)GET /api/store/apps
Get full detail for one store listing by slugGET /api/store/apps/:slug
Install an app from the store by slug (REST-style path variant)POST /api/store/apps/:slug/install
Get which store features this build supports (publish, reviews, install counts)GET /api/store/capabilities
Get the store category enum used for browsing and publishingGET /api/store/category-taxonomy
Get the curated store home page (featured/trending sections)GET /api/store/discover-page
Dismiss an available update so it stops appearing as pendingPOST /api/store/ignore-version
Install a digital human from a local .dhpkg filePOST /api/store/import-dhpkg
Install an app from the store by slugPOST /api/store/install
List the signed-in creator's own published appsGET /api/store/my-publications
Find the installed app that a store slug would resolve toPOST /api/store/publish/find-app
Preview the slug and version a publish of an app would producePOST /api/store/publish/preview
Paginated search over store listings (primary browse endpoint)POST /api/store/query
Force a full re-download of the store registry indexPOST /api/store/refresh
List configured store registry sourcesGET /api/store/registries
Add a new registry sourcePOST /api/store/registries
Remove a configured registry sourceDELETE /api/store/registries/:registryId
Update a registry source's adapter config (e.g. its API key)PATCH /api/store/registries/:registryId/adapter-config
Enable or disable a configured registry sourcePOST /api/store/registries/:registryId/toggle
Cheap conditional freshness check for the store indexPOST /api/store/revalidate
Take down one of the creator's own published appsPOST /api/store/unpublish
List available updates for installed appsGET /api/store/updates
Apply an available update to an installed appPOST /api/store/updates/:appId/apply
Re-check all installed apps for available updates right nowPOST /api/store/updates/check-now

Preview the slash-command name a skill title would install under ​

GET /api/skills/command-name Assistant

  • Query: ?name=. A missing name returns an empty string, not a 400. Pure string transform — it does not check whether that name is already taken.

Query parameters: name

Response

ts
{success:true,data:string}  // e.g. "My Skill" -> "my-skill"

Example

bash
curl -s "$HALO_URL/api/skills/command-name" \
  -H "Authorization: Bearer $HALO_TOKEN"

Get a listing's SKILL.md/README document ​

GET /api/store/app-document Assistant

  • slug is a query param (?slug=...), not a path segment — scoped slugs like "owner/repo" contain "/". content is null when the source has no document.

Query parameters: slug

Response

ts
{success:true,data:{content:string|null}}

Example

bash
curl -s "$HALO_URL/api/store/app-document" \
  -H "Authorization: Bearer $HALO_TOKEN"

List store listings (legacy, non-paginated; prefer POST /api/store/query) ​

GET /api/store/apps Assistant

  • Query params: search, locale, category, type, tags (comma-separated).

Query parameters: category, locale, search, tags, type

Response

ts
{success:true,data:[{slug,name,version,author,description,type,category,tags,...}]}

Example

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

Get full detail for one store listing by slug ​

GET /api/store/apps/:slug Assistant

Path parameters

NameDescription
slugStore listing slug, from GET /api/store/apps.

Response

ts
{success:true,data:{entry:{slug,name,version,...},spec:<AppSpec>,registryId}} or {success:false,error}

Example

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

Install an app from the store by slug (REST-style path variant) ​

POST /api/store/apps/:slug/install Reversible Assistant

  • Always send spaceId. It is used exactly as sent: leave it out and the item installs for every workspace; name another workspace and it installs there. Both answer success: true, and the response does not say where it went.
  • Send "spaceId": null only for a deliberate global install of a skill or MCP app. Optional field: userConfig (object).
  • Same as POST /api/store/install with the slug in the path, including the 403 MCP_COMMAND_BLOCKED case.

Path parameters

NameDescription
slugStore listing slug, from GET /api/store/apps.

Request body

json
{
  "spaceId": "<spaceId>"
}

Response

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

Example

bash
curl -sX POST "$HALO_URL/api/store/apps/<slug>/install" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spaceId":"<spaceId>"}'

Get which store features this build supports (publish, reviews, install counts) ​

GET /api/store/capabilities Assistant

  • Reports which Store features this build supports. Publishing happens in the Halo app, not through this API.

Response

ts
{success:true,data:{catalog:true,installs:boolean,publish:boolean,reviewWorkflow:boolean,identity:"none"|string}}

Example

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

Get the store category enum used for browsing and publishing ​

GET /api/store/category-taxonomy Assistant

  • Categories are keyed per app type (skill/automation/mcp/extension) with "default" as the fallback list for types without their own entry.

Response

ts
{success:true,data:{default:[{id,label|labelKey,icon?}],<appType>?:[...]}}

Example

bash
curl -s "$HALO_URL/api/store/category-taxonomy" \
  -H "Authorization: Bearer $HALO_TOKEN"

Get the curated store home page (featured/trending sections) ​

GET /api/store/discover-page Assistant

  • Query params: locale?, pageSize?. Falls back to a single paginated catalog node when no curated layout is configured.

Query parameters: locale, pageSize

Response

ts
{success:true,data:{version,nodes:[{type,title?,subtitle?,entries?:[RegistryEntry],collections?,hasMore?}]}}

Example

bash
curl -s "$HALO_URL/api/store/discover-page" \
  -H "Authorization: Bearer $HALO_TOKEN"

Dismiss an available update so it stops appearing as pending ​

POST /api/store/ignore-version Reversible Assistant

  • Only hides that one version; a newer update still surfaces normally.

Request body

json
{
  "appId": "<appId — a uuid from GET /api/apps>",
  "version": "1.2.0"
}

Response

ts
{success:true,data:null}

Example

bash
curl -sX POST "$HALO_URL/api/store/ignore-version" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"appId":"<appId — a uuid from GET /api/apps>","version":"1.2.0"}'

Install a digital human from a local .dhpkg file ​

POST /api/store/import-dhpkg Reversible Assistant

  • Installs and activates the package's Digital Human; it then runs on its own schedule. Undo with DELETE /api/apps/:appId.
  • Unlike installing in the Halo app or through its assistant, this does not fetch the package's required skills or undo a half-finished install. Check GET /api/apps/:appId afterwards; an activation that fails after the install still answers success: true.
  • filePath is a path on the computer running Halo. spaceId is used exactly as sent: left out means an install for every workspace, another workspace installs there, and neither is reported back.

Request body

json
{
  "filePath": "/absolute/path/to/app.dhpkg",
  "spaceId": "<spaceId>"
}

Response

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

Errors

  • 400 when filePath is missing.

Example

bash
curl -sX POST "$HALO_URL/api/store/import-dhpkg" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filePath":"/absolute/path/to/app.dhpkg","spaceId":"<spaceId>"}'

Install an app from the store by slug ​

POST /api/store/install Reversible Assistant

  • Always send spaceId. It is used exactly as sent: leave it out and the item installs for every workspace; name another workspace and it installs there. Both answer success: true, and the response does not say where it went.
  • Send "spaceId": null only for a deliberate global install of a skill or MCP app. Optional field: userConfig (object).
  • Same operation as POST /api/store/apps/:slug/install, with the slug in the body.

Request body

json
{
  "slug": "author/app-id",
  "spaceId": "<spaceId>"
}

Response

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

Errors

  • 400 = missing slug.
  • 403 with code: "MCP_COMMAND_BLOCKED" when the item is an MCP server this build's policy blocks.

Example

bash
curl -sX POST "$HALO_URL/api/store/install" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"slug":"author/app-id","spaceId":"<spaceId>"}'

List the signed-in creator's own published apps ​

GET /api/store/my-publications Assistant

  • Fails when no creator identity is signed in — this build may not support publishing at all (check GET /api/store/capabilities first).

Response

ts
{success:true,data:[{slug,name?,displayName?,type?,version,status:"listed"|"hidden"|"rejected",takedownBy?,rejectReason?,submittedAt?,channel?,installs?}]}

Example

bash
curl -s "$HALO_URL/api/store/my-publications" \
  -H "Authorization: Bearer $HALO_TOKEN"

Find the installed app that a store slug would resolve to ​

POST /api/store/publish/find-app Assistant

  • Optional fields: type, author. appId is null when no locally installed app uniquely matches.

Request body

json
{
  "slug": "author/app-id"
}

Response

ts
{success:true,data:{appId: string|null}}

Example

bash
curl -sX POST "$HALO_URL/api/store/publish/find-app" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"slug":"author/app-id"}'

Preview the slug and version a publish of an app would produce ​

POST /api/store/publish/preview Assistant

  • Optional fields: author, name (override the store name before deriving the slug). storeVersion is null when the slug has never been published.

Request body

json
{
  "appId": "<appId — a uuid from GET /api/apps>"
}

Response

ts
{success:true,data:{slug,localVersion,storeVersion:string|null}}

Example

bash
curl -sX POST "$HALO_URL/api/store/publish/preview" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"appId":"<appId — a uuid from GET /api/apps>"}'

Paginated search over store listings (primary browse endpoint) ​

POST /api/store/query Assistant

  • All fields optional: search, type, category, page, pageSize, locale.

Request body

json
{
  "search": "weather",
  "page": 1,
  "pageSize": 20
}

Response

ts
{success:true,data:{items:[{slug,name,version,author,description,type,category,tags,...}],total?,hasMore,groups?:[{type,count,hasMore}],sources:[{registryId,status,error?}]}}

Example

bash
curl -sX POST "$HALO_URL/api/store/query" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"search":"weather","page":1,"pageSize":20}'

Force a full re-download of the store registry index ​

POST /api/store/refresh Assistant

Response

ts
{success:true,data:undefined}

Example

bash
curl -sX POST "$HALO_URL/api/store/refresh" \
  -H "Authorization: Bearer $HALO_TOKEN"

List configured store registry sources ​

GET /api/store/registries Assistant

  • adapterConfig is returned as stored and can hold an API key in plain text. Treat the response like a secret.

Response

ts
{success:true,data:[{id,name,url,enabled,isDefault?,sourceType?,adapterConfig?}]}

Example

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

Add a new registry source ​

POST /api/store/registries Assistant

  • Optional fields: sourceType (string) and adapterConfig (object, adapter-specific, for example an API key). The response returns adapterConfig as stored.
  • url must be http(s) and must not point to a loopback, private or link-local host. Fails if a source with the same URL already exists.

Request body

json
{
  "name": "My Registry",
  "url": "https://example.com/registry"
}

Response

ts
{success:true,data:{id,name,url,enabled,isDefault:false,sourceType?,adapterConfig?}} | {success:false,error}

Example

bash
curl -sX POST "$HALO_URL/api/store/registries" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"My Registry","url":"https://example.com/registry"}'

Remove a configured registry source ​

DELETE /api/store/registries/:registryId Reversible Assistant

  • Fails with an error (not thrown) when the target is the built-in/default registry — those cannot be removed. Can be added back with POST /api/store/registries.

Path parameters

NameDescription
registryIdStore source id, from GET /api/store/registries.

Response

ts
{success:true,data:undefined} or {success:false,error}

Example

bash
curl -sX DELETE "$HALO_URL/api/store/registries/<registryId>" \
  -H "Authorization: Bearer $HALO_TOKEN"

Update a registry source's adapter config (e.g. its API key) ​

PATCH /api/store/registries/:registryId/adapter-config Reversible Assistant

  • Body is a flat JSON object of adapter-specific fields — exact keys depend on the registry's sourceType.
  • Write-only: the response never echoes the config back, so this is safe for setting credentials. 400 if the body is missing or not a plain object.

Path parameters

NameDescription
registryIdStore source id, from GET /api/store/registries.

Request body

json
{
  "apiKey": "sk-..."
}

Response

ts
{success:true,data:undefined}

Example

bash
curl -sX PATCH "$HALO_URL/api/store/registries/<registryId>/adapter-config" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"apiKey":"sk-..."}'

Enable or disable a configured registry source ​

POST /api/store/registries/:registryId/toggle Reversible Assistant

Path parameters

NameDescription
registryIdStore source id, from GET /api/store/registries.

Request body

json
{
  "enabled": true
}

Response

ts
{success:true,data:undefined}

Errors

  • 400 if enabled is missing or not a boolean.

Example

bash
curl -sX POST "$HALO_URL/api/store/registries/<registryId>/toggle" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

Cheap conditional freshness check for the store index ​

POST /api/store/revalidate Assistant

  • changed:false means the index did not move (a 304-equivalent); the browse list does not need refetching.

Response

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

Example

bash
curl -sX POST "$HALO_URL/api/store/revalidate" \
  -H "Authorization: Bearer $HALO_TOKEN"

Take down one of the creator's own published apps ​

POST /api/store/unpublish Reversible Assistant

  • Can be republished later; already-installed/downloaded copies elsewhere are not affected either way.

Request body

json
{
  "slug": "author/app-id"
}

Response

ts
{success:true,data:null}

Example

bash
curl -sX POST "$HALO_URL/api/store/unpublish" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"slug":"author/app-id"}'

List available updates for installed apps ​

GET /api/store/updates Assistant

Response

ts
{success:true,data:[{appId,currentVersion,latestVersion,entry,strategy,severity}]}

Example

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

Apply an available update to an installed app ​

POST /api/store/updates/:appId/apply Reversible Assistant

  • mode is optional: "patch_minor"|"major"|"force", defaults to "force". No built-in downgrade path — reinstalling the old version is the way back.
  • A digital human keeps the user's version of every field that differs from the author's new version (prompt, run schedule, name, ...); kept lists those fields, empty when the update applied in full. editsKnown false means there was no earlier author version to compare with, so every difference was kept. The user can switch kept fields to the author's version in the app.

Path parameters

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

Request body

json
{
  "mode": "force"
}

Response

ts
{success:true,data:{appId,from,to,severity,kept,editsKnown}} or {success:false,error}

Example

bash
curl -sX POST "$HALO_URL/api/store/updates/<appId>/apply" \
  -H "Authorization: Bearer $HALO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"force"}'

Re-check all installed apps for available updates right now ​

POST /api/store/updates/check-now Reversible Assistant

  • Apps set to auto-update silently apply any patch/minor update found — this is not just a read.

Response

ts
{success:true,data:[{appId,currentVersion,latestVersion,entry,strategy,severity}]}

Example

bash
curl -sX POST "$HALO_URL/api/store/updates/check-now" \
  -H "Authorization: Bearer $HALO_TOKEN"

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