API

Your box (the container running your workspace, on your laptop under stackbone dev or deployed in your cloud) serves one HTTP API, and the runtime produces every route from the files you author. Any OpenAI, Anthropic or AG-UI client can chat with an agent. Any HTTP client can start a workflow. Studio and the stackbone CLI operate the box through the same server. You write no HTTP server, and no request routes through Stackbone: your caller talks to your box.

Everything the box serves, read live from the box itself: one agent and three workflows.

What the box serves

Five layers of HTTP, all on the same server and the same port:

Layer Routes Who calls it
Agent chat POST /openai/v1/chat/completions, POST /anthropic/v1/messages, POST /agui/v1/agents/:name, plus GET …/v1/models on the first two Your own product or service, with the OpenAI SDK, the Anthropic SDK, an AG-UI client, or curl. The Studio Playground is one such client.
Workflow runtime POST /api/workflows/:name/start, POST /api/workflows/:name/chat, GET /api/workflows, GET /api/workflows/:name/schema, GET /api/discovery, the approval hooks Studio, stackbone workflows start, a timer the workflow declares, an inbound trigger, another workflow. Under stackbone dev, anything on your machine.
Health GET /live, GET /health, GET /api/health, GET /api/contract Your platform's probes, stackbone contract show, the control plane when it checks the box is reachable.
Operate /api/runs, /api/sessions, /api/approvals, /api/logs, /api/events, /api/catalog, /api/prompts, /api/config, /api/secrets, /api/guardrails, /api/evals, and more Studio and the CLI. Every screen and every stackbone <group> verb is a client of these routes. See The surface area.
MCP POST /mcp A coding agent or any MCP client: four tools that list, describe and call the operate routes under your identity, with a confirmation step before anything irreversible. See MCP.

Under stackbone dev the base URL is http://127.0.0.1:4242. A deployed box listens on 8080, or on PORT when your platform sets one, and you reach it at whatever address you gave it (see Going to production). The routes are the same in both.

Open that address in a browser under stackbone dev and the box prints its own route list:

The box's own front page under stackbone dev: the routes, with the chat wires and the workflow start among them.

You never see a URL of the form stackbone.ai/…/your-agent. The control plane holds your organization and the record of where each box lives, and the traffic goes to the box.

Every box also documents itself. /api/docs is a browsable reference of the routes that box is running right now, and /api/openapi.json is the document behind it. Neither takes a credential. See API reference.

Chat with an agent from any client

An agent speaks three standard wire formats. A client built for OpenAI Chat Completions, Anthropic Messages or AG-UI needs a base URL and a key and nothing else: the official SDKs, LibreChat, Open WebUI, the Vercel AI SDK, LangChain, curl. You pick the agent with the model field (OpenAI and Anthropic) or with the :name in the URL (AG-UI). GET /openai/v1/models lists the agent names, so a chat UI can fill its model picker from it.

curl http://127.0.0.1:4242/openai/v1/chat/completions \
  -H 'Authorization: Bearer stackbone-dev' \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "my-workspace",
    "messages": [{ "role": "user", "content": "In one sentence, what can you help me with?" }]
  }'
{
  "id": "chatcmpl-1f390752-a191-40a3-9e72-2be0afe04faa",
  "object": "chat.completion",
  "model": "my-workspace",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "I can assist you with a wide range of tasks, including research, problem-solving, writing, and general inquiries.",
      },
      "finish_reason": "stop",
    },
  ],
  "usage": { "prompt_tokens": 6393, "completion_tokens": 24, "total_tokens": 6417 },
}

Set "stream": true and you get the same server-sent events the vendor's own API streams. A tool call arrives as tool_calls (OpenAI) or a tool_use block (Anthropic). The Anthropic wire also carries the agent's thinking blocks; AG-UI streams every reasoning block, tool call and tool result as its own event.

A turn is stateless unless you say otherwise. By default you replay the full messages[] on every call, the same as calling the vendor directly, and each turn is recorded as its own run. Send an x-stackbone-session header with any stable string and the box keeps the conversation and its tool state on its side, so you send only the newest turn. AG-UI's threadId does the same and is always on. The protocol reference covers the second header, x-stackbone-conversation, which groups turns without storing state.

A tool that needs a person pauses the turn. When an agent tool marked with interruptOn fires, the turn stops and the decision lands in the Studio HITL Inbox. The next call against that session answers 409 with code: "approval_pending" until someone decides. Over AG-UI the pause is an interrupt on the stream, and your client can resolve it in the next request instead of going through Studio. See Tool approvals.

Every turn, whoever sent it, is a run: it shows up under Runs in Studio with the tools it called and the tokens it spent, and in stackbone runs list.

Start and follow a workflow

A workflow is a 'use workflow' function under workflows/. The box gives each one a start route, a detail and a schema, and Studio shows the route next to the entry with a Copy curl button that produces the call below.

A workflow entry: its start route, its step, and the guardrail set wired to it.

curl -X POST http://127.0.0.1:4242/api/workflows/refund/start \
  -H 'Content-Type: application/json' \
  -d '{ "orderId": "ord_42", "amount": 19.99 }'
{
  "workflowName": "refund",
  "status": "started",
  "runId": "aa4f50bf-29df-58dd-86c1-5ddbbf8d74d2",
  "worldRunId": "wrun_01M080NBDQD31ER017X2PV3241",
  "trigger": "POST /api/workflows/refund/start",
}

The call returns as soon as the run is enqueued. runId is the id you see in Studio and pass to stackbone runs get. The run itself is durable: each 'use step' runs once, the box writes down its result, and a run that pauses for hours (a sleep, an approval) resumes where it stopped after a restart.

A workflow marked serial answers differently. It takes one run at a time, so the box queues a start that arrives while an earlier run holds the lock: the receipt reads "status": "queued" and carries no runId yet. See Serial execution.

The box checks the input at the door. It enforces a workflow's declared input schema before anything runs, and a bad payload starts no run:

{
  "code": "workflow_input_invalid",
  "message": "Input does not match the schema for workflow \"refund\".",
  "details": {
    // `path` is the segments to the field. `[]` is the whole payload, and an
    // array index stays a number: `["items", 0, "name"]`.
    "issues": [
      { "path": ["amount"], "message": "Invalid input: expected number, received undefined" },
    ],
  },
}
Situation Status code
No workflow by that name (the body lists the real ones) 404 workflow_not_found
The input does not match the declared schema 400 workflow_input_invalid
A guardrail refused the payload (the body names the rule) 422 workflow_guardrail_blocked
The workflow failed to compile at boot 503 workflow_compile_failed
The workflow exists, but no workflow runtime is armed 503 workflow_runtime_not_armed

The same schema drives the Studio Playground. Pick a workflow and Studio builds the input form from it, with the input and output contract beside it. GET /api/workflows/:name/schema returns the two JSON Schemas, and stackbone workflows schema <name> prints them.

The same schema the start route enforces, as a form.

Streaming workflows answer on /chat. You start a workflow written to hold a conversation (its catalog mode is chat) with POST /api/workflows/:name/chat, and it answers a server-sent-event stream of the frames its steps write. See Workflow agents.

A paused workflow resumes over a hook. A workflow that calls requestApproval() parks until a decision is posted to POST /api/workflows/hooks/:token/resume. You do not build that round-trip: the HITL Inbox and stackbone hitl approve decide through POST /api/approvals/:id/decide, and the box posts the resume itself. The resume route is gated like every other workflow route, so the token in the URL addresses the parked hook and does not stand in for a credential.

Discovery routes, for a client that needs to know what is there before calling it:

Route Returns
GET /api/workflows The workflow catalog: name, start route, whether it declares a schema, its steps.
GET /api/workflows/:name One workflow's detail.
GET /api/workflows/:name/schema Its input and output JSON Schema.
GET /api/discovery The combined { agents, workflows } view of the workspace.
GET /api/recurring-jobs Every timer armed on the box, when it fires next, and how its last execution went.

The surface area

Studio and the CLI are HTTP clients of the box like any other. Every screen and every stackbone <group> verb reads or writes through the box's own routes, and the box enforces your role on each of them. The groups:

Group What you can do Studio CLI
Runs List, filter and open runs; read the trace step by step; retry or cancel; stream one run's logs. Runs stackbone runs
Sessions Group chat turns into conversations with a token total; open a turn's run. Sessions
Approvals List what paused for a person, read the payload and the audit trail, approve or reject. HITL Inbox stackbone hitl
Logs Tail everything the box prints, live, filtered by level, run or text. Logs stackbone logs tail
Catalog Every agent and workflow the box serves, with model, tools, schema, guardrails and triggers. Catalog stackbone workflows list
Prompts Read and edit the prompts your code loads by name; version and roll back. Prompts stackbone prompts
Dynamic config Change values your code reads at runtime, without a redeploy. Dynamic config stackbone config
Secrets Set, rotate and delete the keys your code reads. Values are written once and never listed. Secrets stackbone secrets
Guardrails Rules the runtime enforces on a turn: block, mask, or hold for approval. Guardrails
Evals Cases, suites and runs that score an agent or a workflow; the gate you put in CI. Cases, Suites, Runs stackbone eval <suite>
Recurring jobs Every timer on the box, its next fire and last outcome. Recurring jobs
Storage, DB, RAG Browse the box's object storage, query its Postgres read-only, manage RAG collections. Storage, DB Explorer, RAG stackbone storage, db, rag
Connections Connect an outside service once, grant it, test it, and subscribe to its events as triggers. Connections, Triggers
Model provider The credential and base URL the agents use to reach a model. Lives in the box, never in the control plane. Model provider
Events One server-sent-event stream of run, step, session and approval lifecycle, so a screen updates without polling. Every live screen

Reach these through Studio and the CLI

The routes behind this table are the box's own. They are what Studio and the stackbone CLI speak, and both ship in step with the box, but they are not published as a contract for your own code to call yet. The three layers above them (chat, workflows, health) are.

Who can call what

The box checks a credential on every request. Which one depends on the route:

Routes Credential
Agent chat (/openai, /anthropic, /agui) A workspace API key: Authorization: Bearer <key> or x-api-key: <key>, whichever your SDK sends. Mint one in Studio under Settings → API keys. A missing, unknown, revoked or expired key answers 401 in the wire's own error shape, and a key that was not granted the agent it asked for answers 403. See Security and auth.
Workflow runtime and the operate routes The five-minute identity token the control plane mints for your Studio session or your signed-in CLI, naming your organization, the box and your role. The box verifies it against the control plane's public keys and enforces the role per action (runs:read, hitl:decide, secrets:rotate, evals:run, …). The control plane itself signs its own calls to the box with the box's signing secret.
Approval hooks (/api/workflows/hooks/:token/resume) The same identity token. The token in the URL names the parked hook; it is not a credential, so a caller still has to prove itself. See Approval hooks and inbound triggers.
Health, the handshake and the reference None. /live, /health, /api/health, /api/contract, /api/docs and /api/openapi.json answer without a credential. A probe never needs one, and you can read the reference before you hold a token.
MCP (/mcp) None under stackbone dev; the operator token on a self-hosted box; your Stackbone sign-in on a deployed box, as a five-minute token valid only at /mcp. Your role applies on every call. See MCP.

A box from a self-host deployment answers the operate routes differently: it checks one static bearer, STUDIO_STANDALONE_TOKEN, and arms no identity gate at all. The two are exclusive, and the box's boot log names which one it chose. See A self-host box takes one operator token.

stackbone dev arms none of this. The local box holds no signing secret and verifies no token, so every route answers, and the chat wires take any non-empty bearer (stackbone-dev in the examples above). The tunnel it opens so that Studio can reach it is a random address that disappears when the process stops. Treat it as private while it lives.

Calls from a browser go through CORS. By default the box answers preflights for https://app.stackbone.ai, https://chat.stackbone.ai and any http://localhost:* origin. A frontend of your own on another origin goes on the allowlist, in stackbone.config.json under studio.corsOrigins or in the STACKBONE_CORS_ALLOW_ORIGINS variable. That variable replaces the default list rather than adding to it, so put the defaults you still want back in the value. An origin that is not on the list gets no Access-Control-Allow-Origin header and the browser refuses the response itself; there is no 403 to find. See Configuration and, for a deployed pair of containers, Self-host behind a proxy.

Health, liveness and the handshake

The box splits "the process is up" from "everything it depends on works", so your platform does not recycle the container because one subsystem is slow:

Route Behaviour
GET /live Instant 200 { "status": "ok" }. No probes. Point your platform's liveness check here.
GET /health Runs every subsystem probe in parallel. 200 { "status": "ok", "checks": { … } }, or 503 { "status": "degraded", "checks": { … } } naming the subsystem that failed and why. checks covers what your workspace depends on, such as database and redis.
GET /api/health The cheap read that names which host answered: "host": "dev" under stackbone dev, "host": "image" on a deployed box, plus the agent's slug. No probes.
GET /api/contract The Stackbone Agent Protocol handshake: the protocol version the box speaks, the build it runs, and the capabilities your code can rely on (database.postgres_direct, storage.s3, queues.jobs, secrets.read_write, …).
GET /api/services The Postgres, object-storage and agent endpoints the box injected into your agent, read back, so you can point psql or an S3 client at the same stack. The storage secret key is never in the answer. This one takes a credential.

A local box and a deployed one both serve all five. Your platform's probes read /live and /health; the rest are for you.

// GET /api/contract
{
  "version": 15,
  "minSupported": 1,
  "capabilities": [
    "database.postgres_direct",
    "rag.basic",
    "queues.jobs",
    "secrets.read_write",
    "config.read_write",
    "approval.fire_and_forget",
    "storage.s3",
    "ai.openrouter",
    "prompts.basic",
    "browser.provider",
    "activity.read",
  ],
  "build": { "name": "stackbone-cli", "version": "0.4.0-alpha.0" },
}

stackbone contract show prints the same answer, and stackbone contract validate checks your project against it before you build an image. This handshake is what the SDK's ambient stackbone client (stackbone.database, .storage, .secrets, …) reads to know what the box offers; it is not one of the routes you drive yourself.

Read more

  • Agent runtime protocol: the full contract, request and response shapes for all three chat wires, session headers, tool approvals over AG-UI, and the environment the runtime injects.
  • API reference: the browsable reference your own box serves at /api/docs, generated from the routes it is running.
  • Security and auth: how a caller proves itself to the box, and where every credential sits.
  • MCP: the operate routes as four tools for a coding agent, under your identity.
  • Governance: the Studio screens that sit on top of the operate routes.
  • Building workflows: declaring an input schema and triggering a run from code, a timer or an event.
  • Agent-runtime CLI commands: runs, hitl, logs, secrets, config, prompts and contract from a terminal.
BUILT WITH ❤️ FROM CANADA AND SPAIN