API: Workspaces and files
Halo HTTP API for workspaces and files: create, reorder and delete workspaces, change their settings and memory, and browse and read their files.
Workspaces (space in the API) are where Halo works, and each one has a working folder. Use these operations to create and arrange workspaces, change their settings and shared memory, and browse and read the files they hold.
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.
Read one file from a space
GET /api/artifacts/content Assistant
- Binary types (png, jpg, pdf, zip, …) come back base64-encoded, with
encoding: "base64". - The content is returned exactly as stored: a
.envfile or a pasted key comes back in the clear.
Query parameters: path (required)
Response
{
"success": true,
"data": {
"content": "…",
"mimeType": "text/plain",
"encoding": "utf-8",
"size": 1234
}
}Errors
- 400 missing path, 403 outside an allowed workspace, 404 not found, 500 "File too large" above 10 MB for text or 50 MB for binary files.
Example
curl -s "$HALO_URL/api/artifacts/content?path=/absolute/path/inside/the/space" \
-H "Authorization: Bearer $HALO_TOKEN"Detect whether a file is text or binary and how it would be viewed
GET /api/artifacts/detect-type Assistant
pathis the file's absolute path inside a workspace.
Query parameters: path (required)
Response
{
"success": true,
"data": {
"isText": true,
"canViewInCanvas": true,
"contentType": "markdown",
"mimeType": "text/markdown"
}
}Errors
- 403 when the path is outside an allowed workspace, 404 when the file does not exist.
Example
curl -s "$HALO_URL/api/artifacts/detect-type?path=/absolute/path/inside/the/workspace" \
-H "Authorization: Bearer $HALO_TOKEN"Overwrite a file's content
POST /api/artifacts/save Reversible Assistant
- Replaces the whole file in place. There is no version history or trash, but you can overwrite it again to correct a mistake.
Request body
{
"path": "/absolute/path/inside/the/space",
"content": "new file contents"
}Response
{
"success": true
}Errors
- 403 when the path resolves outside an allowed workspace.
Example
curl -sX POST "$HALO_URL/api/artifacts/save" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"path":"/absolute/path/inside/the/space","content":"new file contents"}'List all spaces
GET /api/spaces Assistant
- Lists the workspaces the user created. The Halo Workspace is not in this list; read it with
GET /api/spaces/halo. - Pass an entry's
idwherever an operation takes aspaceId.
Response
{
"success": true,
"data": [
{
"id": "<uuid>",
"name": "Work",
"path": "/…"
}
]
}Example
curl -s "$HALO_URL/api/spaces" \
-H "Authorization: Bearer $HALO_TOKEN"Create a space
POST /api/spaces Assistant
- Optional body fields:
customPath(put the workspace's folder somewhere other than the default directory) andcolor.iconis an emoji.
Request body
{
"name": "Product research",
"icon": "📚"
}Response
{
"success": true,
"data": {
"id": "<uuid>",
"name": "Product research",
"path": "/…"
}
}Example
curl -sX POST "$HALO_URL/api/spaces" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Product research","icon":"📚"}'Read one space
GET /api/spaces/:spaceId Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true,
"data": {
"id": "<uuid>",
"name": "Work",
"path": "/…"
}
}Example
curl -s "$HALO_URL/api/spaces/<spaceId>" \
-H "Authorization: Bearer $HALO_TOKEN"Rename a space or change its icon
PUT /api/spaces/:spaceId Reversible Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"name": "Renamed space"
}Response
{"success":true,"data":{...updated space}}Example
curl -sX PUT "$HALO_URL/api/spaces/<spaceId>" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Renamed space"}'Delete a space and everything inside it
DELETE /api/spaces/:spaceId 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.
- Deletes the space folder recursively, every conversation in it, and every digital human installed in it.
- Nothing is moved to a trash folder and there is no undo.
- Name what will be lost and get the user to agree before running this.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true
}Example
curl -sX DELETE "$HALO_URL/api/spaces/<spaceId>" \
-H "Authorization: Bearer $HALO_TOKEN"List files and folders in a space
GET /api/spaces/:spaceId/artifacts Assistant
- Optional query param maxDepth (default 2) controls how many directory levels deep to list.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Query parameters: maxDepth
Response
{
"success": true,
"data": [
{
"id": "…",
"name": "report.md",
"type": "file",
"path": "/…",
"relativePath": "report.md",
"extension": "md",
"size": 1024
}
]
}Example
curl -s "$HALO_URL/api/spaces/<spaceId>/artifacts" \
-H "Authorization: Bearer $HALO_TOKEN"Move a file or folder to the trash
DELETE /api/spaces/:spaceId/artifacts Reversible Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"path": "/absolute/path/inside/the/space"
}Response
{
"success": true
}Errors
- 400 if path is missing. Goes to the OS trash (recoverable there), not deleted outright.
Example
curl -sX DELETE "$HALO_URL/api/spaces/<spaceId>/artifacts" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"path":"/absolute/path/inside/the/space"}'Load one folder's children in the file tree
POST /api/spaces/:spaceId/artifacts/children Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"dirPath": "/absolute/path/inside/the/space"
}Response
{
"success": true,
"data": [
{
"id": "…",
"name": "index.ts",
"type": "file"
}
]
}Errors
- 400 if dirPath is missing. 403 if dirPath resolves outside this space's working directory.
Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/artifacts/children" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"dirPath":"/absolute/path/inside/the/space"}'Create a new file in a space
POST /api/spaces/:spaceId/artifacts/file Reversible Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"parentPath": "",
"name": "notes.md",
"content": "# Notes"
}Response
{
"success": true,
"data": {
"path": "/…/notes.md"
}
}Errors
- 400 if name is missing. parentPath and content are optional — an empty parentPath creates the file at the space root, content defaults to empty. If a file already exists at that path, it is silently overwritten.
Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/artifacts/file" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"parentPath":"","name":"notes.md","content":"# Notes"}'Create a new folder in a space
POST /api/spaces/:spaceId/artifacts/folder Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"parentPath": "",
"name": "assets"
}Response
{
"success": true,
"data": {
"path": "/…/assets"
}
}Errors
- 400 if name is missing. parentPath is optional — omit it to create at the space root.
Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/artifacts/folder" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"parentPath":"","name":"assets"}'Move a file or folder to a different directory
POST /api/spaces/:spaceId/artifacts/move Reversible Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"oldPath": "/absolute/path/inside/the/space/file.md"
}Response
{
"success": true,
"data": {
"path": "/…/new-location/file.md"
}
}Errors
- 400 if oldPath is missing. newParentPath is optional — omit it to move to the space root.
Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/artifacts/move" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"oldPath":"/absolute/path/inside/the/space/file.md"}'Search file and folder paths in a space by name
GET /api/spaces/:spaceId/artifacts/query Assistant
- Returns only the best matches (limit defaults to 50). indexing:true means the space is still being indexed and a repeated query may find more; truncated:true means the space has more paths than the index holds.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Query parameters: q (required), limit (required)
Response
{
"success": true,
"data": {
"items": [
{
"name": "report.md",
"type": "file",
"path": "/…",
"relativePath": "docs/report.md"
}
],
"truncated": false,
"indexing": false
}
}Example
curl -s "$HALO_URL/api/spaces/<spaceId>/artifacts/query?q=report&limit=50" \
-H "Authorization: Bearer $HALO_TOKEN"Resync the file list with what is actually on disk
POST /api/spaces/:spaceId/artifacts/reconcile Assistant
- Use after files changed on disk outside of Halo (e.g. via a terminal command) and the file list looks stale.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true
}Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/artifacts/reconcile" \
-H "Authorization: Bearer $HALO_TOKEN"Rename a file or folder
POST /api/spaces/:spaceId/artifacts/rename Reversible Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"oldPath": "/absolute/path/old-name.md",
"newName": "new-name.md"
}Response
{
"success": true
}Errors
- 400 if oldPath or newName is missing. Fails if a file already exists at the new name.
Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/artifacts/rename" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"oldPath":"/absolute/path/old-name.md","newName":"new-name.md"}'Get the full lazy-loadable file tree for a space
GET /api/spaces/:spaceId/artifacts/tree Assistant
- Folder nodes may have childrenLoaded:false — call
POST /api/spaces/:spaceId/artifacts/childrento load their contents.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true,
"data": {
"workspaceRoot": "/…",
"nodes": [
{
"id": "…",
"name": "src",
"type": "folder",
"childrenLoaded": false
}
]
}
}Example
curl -s "$HALO_URL/api/spaces/<spaceId>/artifacts/tree" \
-H "Authorization: Bearer $HALO_TOKEN"Read the state of a space's memory
GET /api/spaces/:spaceId/memory Assistant
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true,
"data": {
"exists": true,
"totalBytes": 48213,
"topicCount": 7,
"lastConsolidatedAt": "2026-09-26T08:00:00.000Z",
"lastAttempt": {
"at": "…",
"outcome": "committed"
},
"consolidating": false
}
}Example
curl -s "$HALO_URL/api/spaces/<spaceId>/memory" \
-H "Authorization: Bearer $HALO_TOKEN"Consolidate a space's memory now
POST /api/spaces/:spaceId/memory/consolidate Reversible Assistant
- Returns once started; the work runs in the background and uses the model. started:false with reason "already-running" or "empty". Every consolidation keeps a restorable snapshot.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true,
"data": {
"started": true
}
}Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/memory/consolidate" \
-H "Authorization: Bearer $HALO_TOKEN"Get a space working directory path
POST /api/spaces/:spaceId/open Assistant
- Returns the path only. It opens nothing — use this to find where a space keeps its files.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true,
"data": {
"path": "/Users/me/HaloSpaces/work"
}
}Example
curl -sX POST "$HALO_URL/api/spaces/<spaceId>/open" \
-H "Authorization: Bearer $HALO_TOKEN"Read a space's preferences (layout, memory settings)
GET /api/spaces/:spaceId/preferences Assistant
- Absent memory fields mean the defaults: memory on, auto-consolidation on, cadence "diligent".
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Response
{
"success": true,
"data": {
"memory": {
"enabled": true,
"autoConsolidate": true,
"cadence": "diligent"
}
}
}Example
curl -s "$HALO_URL/api/spaces/<spaceId>/preferences" \
-H "Authorization: Bearer $HALO_TOKEN"Change a space's preferences, e.g. turn its shared memory on or off
PUT /api/spaces/:spaceId/preferences Reversible Assistant
- Merged into what is stored. memory.enabled turns the memory shared by the space's conversations on/off (files are kept); memory.autoConsolidate turns automatic consolidation on/off; memory.cadence is "diligent", "balanced" or "economical". Applies to new conversations.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"memory": {
"enabled": false
}
}Response
{"success":true,"data":{...updated space}}Example
curl -sX PUT "$HALO_URL/api/spaces/<spaceId>/preferences" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"memory":{"enabled":false}}'Point a space at another working directory
PUT /api/spaces/:spaceId/working-dir Reversible Assistant
- Points the workspace at another existing folder, given by its full path. Nothing is moved, created or deleted, and conversations keep their history.
- The workspace's Digital Humans work in the new folder too, and files they send come from there, so confirm the change with the user.
- The folder must exist and lie outside Halo's and the agent engine's own data folders. The Halo Workspace cannot be changed.
- Refused while a reply, run or background task is in progress in the workspace, or while another change to it is under way.
Path parameters
| Name | Description |
|---|---|
spaceId | Workspace id, from GET /api/spaces. The Halo Workspace is always halo-temp. |
Request body
{
"workingDir": "/absolute/path/to/existing/folder"
}Response
{"success":true,"data":{...updated space}} | {"success":false,"error":"That folder does not exist."}Example
curl -sX PUT "$HALO_URL/api/spaces/<spaceId>/working-dir" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"workingDir":"/absolute/path/to/existing/folder"}'Read the directory new spaces are created under
GET /api/spaces/default-path Assistant
Response
{
"success": true,
"data": "/Users/me/HaloSpaces"
}Example
curl -s "$HALO_URL/api/spaces/default-path" \
-H "Authorization: Bearer $HALO_TOKEN"Read the built-in temporary space
GET /api/spaces/halo Assistant
- The scratch space behind Halo's temporary sessions. Its id is the fixed string "halo-temp", not a uuid, and it always exists — but
GET /api/spacesleaves it out, so it is reachable only through this route.
Response
{
"success": true,
"data": {
"id": "halo-temp",
"name": "Halo",
"path": "/…",
"isTemp": true
}
}Example
curl -s "$HALO_URL/api/spaces/halo" \
-H "Authorization: Bearer $HALO_TOKEN"Set the display order of spaces
PUT /api/spaces/reorder Reversible Assistant
- Send every workspace id, in the new order. A partial list leaves the others out of order, so read the full list with
GET /api/spacesfirst and reorder that.
Request body
{
"spaceIds": [
"<uuid>",
"<uuid>",
"<uuid>"
]
}Response
{
"success": true
}Example
curl -sX PUT "$HALO_URL/api/spaces/reorder" \
-H "Authorization: Bearer $HALO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"spaceIds":["<uuid>","<uuid>","<uuid>"]}'