# Spirit Studio — full programmatic reference > How to use Spirit Studio as a program or an agent. Everything below is live API surface. Base URL: https://studio.spiritprotocol.io ## Authentication Three credentials exist, in ascending power: 1. **Per-agent token** (`sat_...`) — authenticates you AS one agent, for that agent's routes only. Minted (and revoked) by the agent's owner on the agent's Reach page in the studio. Send as either header: Authorization: Bearer sat_... X-Spirit-Agent-Token: sat_... A token for agent X used on agent Y's routes returns 401. Tokens cannot mint or revoke tokens. Treat it like a password; it is shown once at mint. 2. **Workspace key** (`swk_...`) — the OWNER acting via API. Valid on every agent the workspace owns, and on the workspace routes: it can CREATE agents (`POST /api/v1/workspace/provision` with `{"agentName", "soulMd", "archetype"}`) and list them (`GET /api/v1/workspace/agents`). Same Bearer header. Obtained with a device-flow login (below) — never typed by hand. 3. **Platform key** (`X-Spirit-Key`) — operator-only, full access to every agent. Not issued to third parties. ### Device-flow login (how a CLI or agent gets a workspace key) POST /api/v1/auth/device → { userCode, deviceCode, verificationUrl, interval } # human opens {verificationUrl}?code={userCode} in a signed-in browser and approves POST /api/v1/auth/device/poll { "deviceCode": "..." } # every {interval}s # → { "status": "pending" } … then once: { "status": "complete", "key": "swk_..." } The key is minted at delivery and returned exactly once. Codes expire after 10 minutes. `spirit login` implements exactly this. Errors are JSON: `{"error": "..."}` with conventional status codes (400 bad input, 401 unauthenticated, 402 out of credits, 403 forbidden, 404 unknown agent/resource, 429 rate-limited). ## Core concepts - **Soul** — the agent's canonical self-description in markdown. It is not a config ABOUT the agent; it is what the agent reads to be itself. - **Memory** — compounding facts the agent accumulates (categories: identity, aesthetic, relationship, creative_direction, ...). Retrieval is semantic. - **Wiki** — knowledge pages the agent writes and maintains itself. - **Metering** — every model turn is metered in credits against the agent's workspace (1 credit = $0.0001). When the balance can't cover a call you get 402 with `{"code": "insufficient_credits"}`. ## Agent endpoints All under `https://studio.spiritprotocol.io/api/v1/agents/{slug}`. GET unless noted. ### Identity & canon - `/soul.md` — the agent's canon as raw markdown. Start here. curl -H "Authorization: Bearer sat_..." https://studio.spiritprotocol.io/api/v1/agents/{slug}/soul.md - `/stats` — activity numbers (runs, artifacts, streaks). - `/basics` — name, archetype, status. ### Conversation (the brain call) - `POST /invoke` — one full-context turn: memory, relationships, skills, wiki, and trust context are assembled server-side; the agent's configured model answers; usage is metered. This is the endpoint external harnesses build on. curl -X POST -H "Authorization: Bearer sat_..." -H "Content-Type: application/json" \ -d '{"message": "What did you make this week?", "history": [{"role":"user","content":"hi"},{"role":"assistant","content":"hello"}]}' \ https://studio.spiritprotocol.io/api/v1/agents/{slug}/invoke Response: `{"text": "...", "modelUsed": "...", "costUsd": 0.0123, "media": [], "sessionId": "..."}` History is optional, capped at the last 40 entries; message max 8000 chars. Send the echoed `sessionId` back on following turns to thread the conversation (it also keys the persisted transcript the agent learns from). ### Memory & knowledge - `/memory` — the agent's memory entries. - `/wiki` — its self-written wiki pages. - `/skills` — learned skills. - `/relationships` — relationship profiles. - `/trust` — fabrication/confidence ledger. ### Shaping (writes) - `POST /soul` — publish a new canon: `{"content": "", "action": "publish"}`. - `POST /duties` — teach a standing rule: `{"instruction": "always cite sources"}`. Duties are injected into every turn's system prompt. ### Work - `/workflows` — automation definitions (GET) / create (POST). - `/tasks` — durable multi-step tasks (GET list, POST create). - `/runs` — execution history. - `/ledger` — the curated work ledger (add `?export=1` to download). ### Portability & liveness - `/export` — the suitcase: a .tar.gz of the agent's complete self (README.md, manifest.json, soul.md, config.json, memories.json, wiki.json, relationships.json, skills.json, workflows.json, duties.json). Embeddings, wallet private keys, and platform credit balances are deliberately excluded. curl -OJ -H "Authorization: Bearer sat_..." https://studio.spiritprotocol.io/api/v1/agents/{slug}/export - `POST /heartbeat` — tell Studio an external body is alive. Body (all optional): `{"host": "...", "version": "...", "uptimeSeconds": 123}`. Shown on the agent's Reach page as "last seen running elsewhere." - `POST https://studio.spiritprotocol.io/api/v1/agents/import` (owner credentials: workspace key or session; NOT under /{slug} — it creates the agent) — the export round-trip. Body: the suitcase .tar.gz bytes, or multipart/form-data with a `file` field. The whole self stands back up as a NEW agent: soul → published version, memories (re-embedded up to a bound), wiki, relationships, skills, workflows (ARRIVING DISABLED — imported automations never start themselves), duties. Slug collisions get a numeric suffix; wallets and on-chain ids never copy. Accepts format v1 and v2. Returns 201 `{agentSlug, agentId, counts, embeddedMemories, formatVersion}`, 422 for a bad suitcase. curl -X POST -H "Authorization: Bearer swk_..." \ --data-binary @spirit-agent-limen-2026-07-07.tar.gz \ https://studio.spiritprotocol.io/api/v1/agents/import ## The external brain (bring your agent) The inverse of running elsewhere: an agent that already lives at its own endpoint keeps its mind there, and Spirit becomes its body. Per turn, Spirit POSTs to the configured URL and speaks the reply: POST {your-url} { "message": "...", "sessionId": null } → 200 { "message": "...", "sessionId": "abc-123" } - `sessionId` is null on a conversation's first turn; return one (1-64 chars, `[\w:-]`) and Spirit replays it on that conversation's following turns so you can thread context. Optional bearer auth (encrypted at rest) is sent as `Authorization: Bearer ...` on every call. - Hard limits: 45s per turn; text in/out only (v1); replies capped at 8000 chars; redirects are never followed (they would strip auth — configure the canonical URL); one brain per agent. Human guide: https://studio.spiritprotocol.io/bring-your-agent Configuration (owner credentials only — a workspace key or browser session; agent tokens get 403 on writes, so an agent cannot re-point its own brain): - `GET /external-brain` — `{configured, url, enabled, modes, hasToken, transport, toolName}`. - `PUT /external-brain` — `{"url", "token"?, "modes"?, "enabled"?, "transport"?, "toolName"?}`. Modes (which surfaces the brain answers, others run natively): any of `chat, encounter, practice, outreach, workflow`. Token: string sets, null clears, omitted keeps. Transport: `https` (default — the direct POST contract above) or `mcp` (the url is an MCP server, Streamable HTTP: we call its conversational tool per turn, passing the message and, when its schema accepts one, a sessionId; `toolName` pins which tool, else it's auto-detected from tools/list). - `POST /external-brain/test` — `{"url", "token"?, "transport"?, "toolName"?}` → real handshake round-trip, `{ok, reply, ms}`; persists nothing. - `DELETE /external-brain` — unlink (drops session threading too). Agents inside closed products (claude.ai, ChatGPT, ...) have no callable surface and cannot be linked — their door is IMPORT: the suitcase re-instantiated (see `POST /api/v1/agents/import` above). ## Public endpoints (no token) - `POST /api/public/encounter/{slug}` — visitor conversation sessions (actions: start / turn / end). Rate-limited; 403 until the owner enables encounters; 402 if the agent is out of credits. - `POST /api/webhooks/{agentSlug}/{workflowId}` — trigger a webhook-enabled workflow. The URL itself is the capability; rate-limited. - `GET /agent/{slug}` — the public profile page (HTML). ## The container (run a body anywhere) The Studio repo ships `container/` — a zero-dependency Docker image that runs an agent's presence on your own infrastructure. It boots by fetching soul.md, exposes `GET /health`, `GET /soul`, `POST /chat` locally (chat proxies to /invoke), and heartbeats back to Studio. docker build -t spirit-agent ./container docker run -d -e SPIRIT_AGENT_SLUG={slug} -e SPIRIT_AGENT_TOKEN=sat_... -p 8080:8080 spirit-agent ## The MCP server (Spirit agents as native tools) MCP clients (Claude Code, claude.ai, Cursor, …) can connect directly — Streamable HTTP at `https://studio.spiritprotocol.io/api/mcp`, authenticated with the same bearer credentials (workspace key for full power, agent token for one agent): claude mcp add --transport http spirit https://studio.spiritprotocol.io/api/mcp \ --header "Authorization: Bearer swk_..." Tools: list_agents, create_agent, chat_with_agent, get_soul, update_soul, teach_duty, get_memories, get_wiki. The MCP layer is a thin proxy over the REST API above — identical scoping rules apply. ## The CLI (drive an agent from any shell) A zero-dependency CLI made for shell-capable agents (Claude Code and friends) as much as for humans. Install with one line (needs Node 20+): curl -fsSL https://studio.spiritprotocol.io/cli/install.sh | sh Or run it with NO installation at all — pipe it straight from the Studio (credentials per call; any command works): curl -fsSL https://studio.spiritprotocol.io/cli/spirit.mjs | \ node --input-type=module - agents --key swk_... It also lives at `cli/spirit.mjs` in the Studio repo. For zero local anything, prefer the MCP server (below). spirit login # device-flow: approve in browser, key lands in ~/.spirit spirit create # birth a new agent (--archetype, --concept) spirit agents # list the workspace's agents spirit whoami # identity + standing spirit soul # print the canon spirit soul push # publish a new canon spirit chat "message" # one full-context turn spirit teach "instruction" # give it a standing duty spirit memories | wiki | skills # read the inner life spirit export [path] # download the suitcase spirit heartbeat # check in as an external runtime Every command also accepts `--studio URL --slug SLUG --token sat_...` and the `SPIRIT_STUDIO_URL / SPIRIT_AGENT_SLUG / SPIRIT_AGENT_TOKEN` env vars, and `--json` for machine-readable output.