| name | minds-api |
| description | Use to act on OTHER Minds workspaces on the user's behalf -- list them, create a fresh one, SSH into one, read or export its backups, start/stop/destroy/recover it, or change its settings and service sharing. Reached through the latchkey gateway's minds-api-proxy; most routes need a per-workspace permission grant. For bringing another workspace's content into this one, use the migrate-workspace skill, which drives these routes as part of a much larger flow. |
| compatibility | Requires latchkey (the standard agent gateway) and curl; ssh/ssh-keygen for the SSH capability. mngr (vendored) for handing tasks to another workspace's agent. |
| metadata | {"author":"imbue"} |
Minds API
Minds exposes a small HTTP API that lets an agent in one workspace act on other
workspaces through the hub: list them, read detail/version/backups, create new
ones, destroy/start/stop them, export backups, establish SSH access, update
settings, and run health/restart recovery.
You never hold a token. Every call goes through the latchkey gateway's
minds-api-proxy on the reserved gateway-self host latchkey-self.invalid;
the gateway injects the central Minds API key and forwards to the desktop
client. So always use latchkey curl (not plain curl), and address the
proxy like this:
latchkey curl http://latchkey-self.invalid/minds-api-proxy/api/schema
latchkey curl http://latchkey-self.invalid/minds-api-proxy/api/v1/workspaces
Discover the API first
GET /api/schema is the authoritative, always-current description of every
route you can reach and every request/response type. Read it before assuming a
route or field shape -- this skill lists the highlights, but the schema is the
source of truth (and only lists routes actually reachable through the gateway):
latchkey curl http://latchkey-self.invalid/minds-api-proxy/api/schema | jq '.paths | keys'
latchkey curl http://latchkey-self.invalid/minds-api-proxy/api/schema \
| jq '.paths["/api/v1/workspaces/{agent_id}/ssh"]'
A workspace is addressed by its agent id (the agent_id field in the
listing below). Your own workspace's id is usually $MNGR_AGENT_ID; confirm it
appears in GET /api/v1/workspaces if you need to self-reference.
Getting access for a specific workspace (latchkey permissions)
Only two endpoints are allowed by default: the schema above, and GET /api/v1/app/version (the newest workspace-template ref the running Minds app
supports -- what update-self caps itself against). Every
/api/v1/workspaces/...
call is gated by the minds-workspaces detent scope, with one permission per
verb. The targeted verbs are granted per workspace, so you ask for access
to one specific workspace at a time.
| Verb permission | Covers | Targeted? |
|---|
minds-workspaces-read | list, detail, version, backups (read) | no (all workspaces) |
minds-workspaces-create | create a new workspace | no |
minds-workspaces-ssh | establish SSH access | yes |
minds-workspaces-backups-export | export a backup snapshot | yes |
minds-workspaces-destroy | destroy a workspace | yes |
minds-workspaces-lifecycle | start / stop the host | yes |
minds-workspaces-update | update settings (color, account) | yes |
minds-workspaces-recover | health check + restart | yes |
minds-workspaces-sharing | view/change service sharing | yes |
When a call comes back rejected (a "not permitted by the user" message / 403),
file a permission request and wait for the user to approve it. Minds uses a
dedicated type: "workspace" request (distinct from the predefined-service
requests in the latchkey skill). It goes in a tool call of its own, with
nothing else in it and its output untouched -- see "File exactly one permission
request per tool call" in the latchkey skill for why:
latchkey curl -XPOST http://latchkey-self.invalid/permission-requests \
-H 'Content-Type: application/json' \
-d '{
"agent_id": "'"$MNGR_AGENT_ID"'",
"type": "workspace",
"payload": {
"permissions": ["minds-workspaces-ssh", "minds-workspaces-backups-export"],
"target_workspace_id": "<TARGET_WORKSPACE_AGENT_ID>"
},
"rationale": "I need SSH + backup-export access to <name> so I can migrate its content into a fresh workspace."
}'
payload.permissions is the list of verb names from the table above.
payload.target_workspace_id is the specific workspace the targeted verbs
act on. Omit it / set null for the non-targeted verbs (read, create), or
to request a verb across all workspaces.
- After posting, wait for a system message telling you whether the user
approved or denied (same as the
latchkey skill's permission flow). Re-run
your call once approved.
Tip: request exactly the verbs the task needs, with a clear rationale -- the
user sees these as checkboxes per workspace.
Core capabilities
Paths below are relative to http://latchkey-self.invalid/minds-api-proxy.
Bodies are JSON; send -H 'Content-Type: application/json' (the API validates
the body only when that header is set).
List / inspect workspaces (minds-workspaces-read)
latchkey curl .../api/v1/workspaces | jq '.workspaces[] | {agent_id, name, host_state, provider_name}'
latchkey curl .../api/v1/workspaces/<id>
latchkey curl .../api/v1/workspaces/<id>/version
The listing includes destroyed-but-still-backed-up workspaces, so you can find
an old workspace even after its host is gone.
Create a new workspace (minds-workspaces-create)
POST /api/v1/workspaces returns 202 with an operation handle; the new
workspace's agent_id appears once mngr create finishes, so you poll the
typed operation route until it's done:
OP=$(latchkey curl -XPOST .../api/v1/workspaces \
-H 'Content-Type: application/json' \
-d '{"git_url": "<template-repo-url>"}' | jq -r .operation_id)
latchkey curl .../api/v1/workspaces/operations/create/$OP | jq '{status, is_done, agent_id, error}'
latchkey curl -N .../api/v1/workspaces/operations/create/$OP/logs
git_url is required (typically the template repo a fresh mind is built from).
Many optional fields exist (host_name, branch, launch_mode, ai_provider,
account_id, region, backup_*) -- see CreateWorkspaceRequest in the
schema. A 400 with {error, field} means a field-level problem; a 422
{"errors":[{field,message}]} means a structurally invalid body.
Backups: always create with backups unconfigured. Leave every backup_*
field unset (the default is backup_provider=CONFIGURE_LATER). Configuring
backups can involve the user's storage credentials (backup_api_key_env),
which are secrets you must NEVER ask the user for and never send through this
API -- the user enables backups themselves from the minds desktop app
afterwards. (The user's master password is likewise never yours to handle;
newer minds versions use it only inside the desktop app to protect
cross-device sync, and it does not appear in this API at all.)
SSH into another workspace (minds-workspaces-ssh)
You generate a keypair locally; only the public key leaves you, and the grant is
time-limited. Pass your own workspace id as requester_workspace_id (the hub
needs it to dedupe your grant and, for a local target, to broker a tunnel back
into your container).
ssh-keygen -t ed25519 -N '' -f /tmp/mind_key
CONN=$(latchkey curl -XPOST .../api/v1/workspaces/<TARGET_ID>/ssh \
-H 'Content-Type: application/json' \
-d '{"public_key": "'"$(cat /tmp/mind_key.pub)"'", "requester_workspace_id": "'"$MNGR_AGENT_ID"'"}')
echo "$CONN" | jq
ssh -i /tmp/mind_key -p "$(echo "$CONN" | jq -r .port)" \
"$(echo "$CONN" | jq -r .user)@$(echo "$CONN" | jq -r .host)"
- For a remote target you get its real address. For a local
(Docker/Lima) target you get
host="127.0.0.1" and a port that is reachable
from inside your own workspace (the hub reverse-tunnels into your container),
so run the ssh from this workspace.
- The grant expires at
expires_at; re-request to refresh (it won't stack).
502 usually means the target (or your own container) is offline/unreachable;
404 means the workspace id is unknown.
Read / export backups (minds-workspaces-read, minds-workspaces-backups-export)
latchkey curl .../api/v1/workspaces/<id>/backups \
| jq '{is_backing_up, snapshots: [.snapshots[] | {short_id, time, total_size_bytes}]}'
latchkey curl -o /tmp/restore.zip \
-XPOST .../api/v1/workspaces/<id>/backups/<snapshot_id>/export
Recover / lifecycle (minds-workspaces-recover, -lifecycle, -destroy)
latchkey curl .../api/v1/workspaces/<id>/health | jq
latchkey curl -XPOST .../api/v1/workspaces/<id>/restart -H 'Content-Type: application/json' -d '{"scope":"services"}'
latchkey curl -XPOST .../api/v1/workspaces/<id>/start
latchkey curl -XPOST .../api/v1/workspaces/<id>/destroy
Restart and destroy return operation handles; poll
.../api/v1/workspaces/operations/restart/<id> and
.../api/v1/workspaces/operations/destroy/<id> (each with /logs) the same way
as create.
Headline workflow: migrate an old workspace into a fresh one
Moving a user's content out of an old, outdated, or broken workspace into a clean
new one is the migrate-workspace skill's job, not this one's. Do not
improvise a migration out of the routes above: the real flow also has to resolve
what the user actually authored (by diffing the source against its own template
base), map paths across the tree reorganization, re-register apps and scheduled
jobs, recreate every past chat with its history, and re-audit the credential and
permission call sites. This skill only supplies the plumbing it uses.
Which side you are on decides what you do:
- In the NEW workspace (the user asks you to bring their old stuff over) --
load
migrate-workspace and follow it. It uses minds-workspaces-read to find
the source, -lifecycle to start it, -ssh for the live session it reads over,
and -destroy only if the user asks for that at the very end.
- In the OLD workspace (the user says they want to move to a new one) --
migrate-workspace's own escape hatch covers this in two steps: create the
fresh workspace here (POST /api/v1/workspaces with the template git_url,
polling operations/create/<op> until DONE, every backup_* field left
unset), then tell the user to open it and ask its agent to migrate. Copy nothing
yourself.
If the old workspace cannot be started at all, the live-session flow does not
apply. Export its newest snapshot (GET .../<OLD>/backups, then
POST .../<OLD>/backups/<snapshot_id>/export -o /tmp/old.zip) and work through
the contents with the user by hand.
Throughout: request only the per-workspace permissions each step needs, with a
rationale that names the workspace and the goal, and wait for approval before
retrying a gated call.
Notes
- Always prefer
latchkey curl over curl; the gateway is what injects auth and
routes the proxy. A connection error (curl exit 7) usually means the user's
computer is offline.
- The schema endpoint is the contract. If a field or route here ever disagrees
with
GET /api/schema, trust the schema.
- Don't expose the proxy/gateway mechanics to the user unless they ask -- talk in
terms of "your workspaces".
- For general latchkey usage and the predefined-service permission flow, see the
latchkey skill; this skill only adds the Minds-specific type: "workspace"
permission request and the workspace routes.