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

Four 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/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.

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. The traffic goes to the box.

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, its result is written down, and a run that pauses for hours (a sleep, an approval) resumes where it stopped after a restart.

The input is checked at the door. A workflow that declares an input schema gets it enforced before anything runs, and a bad payload starts no run:

{
  "code": "workflow_input_invalid",
  "message": "Input does not match the schema for workflow \"refund\".",
  "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 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. A workflow written to hold a conversation (its catalog mode is chat) is started with POST /api/workflows/:name/chat and 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 post it for you, and the token is the only credential the route asks for.

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 bearer credential: Authorization: Bearer <key> or x-api-key: <key>, whichever your SDK sends. A missing one answers 401 in the wire's own error shape. Today the box checks that a credential is present, not what it is: per-workspace API keys are not there yet, so keep a deployed box off the public internet or behind your own gateway until they ship. 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 token in the URL. It is random and unguessable, and it is the capability.
Health and the handshake None. /live, /health and /api/contract answer without a credential, so a probe never needs one.

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. The box answers preflights for https://app.stackbone.ai and any http://localhost:* origin by default. 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. See Configuration.

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 lighter answer stackbone dev serves instead of the two above.
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/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",
  ],
  "build": { "name": "stackbone-cli", "version": "0.3.3" },
}

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.
  • Security and auth: how a caller proves itself to the box, and where every credential sits.
  • 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