Skip to main content

Command Palette

Search for a command to run...

BDK

HTTP API

Bot Development Kit (BDK) hosts expose one public HTTP surface. In the default multi-agent layout, each agent uses /<slug>/v1/*; --mode single serves the same routes at /v1/*. Routes use the agent's HTTP auth chain, and session-owned resources return 403 to another principal.

Unless a section says otherwise, the default auth policy is localDevStrict(). --bearer-token replaces it with bearer auth, and --allow-anonymous replaces it with anonymous access. Built-in JSON routes use { ok: false, error: "<code>", message? } for errors, and MCP uses JSON-RPC. Custom channel routes answer hosted callers with an empty 202; see Call custom channel routes.

Host routes

These routes live at the host root in multi-agent mode. The index routes exist only when the playground is enabled.

RouteContract
GET /HTML index of mounted agents; no auth
GET /v1/agentsJSON index of mounted agents; no auth
GET /docs, GET /docs/*Documentation site in either layout; no auth

--no-playground removes the two index routes. --no-docs removes the documentation site.

Start a session

POST /v1/session opens a durable conversation.

curl -X POST http://127.0.0.1:3000/<slug>/v1/session \  -H 'content-type: application/json' \  -d '{"message":"What can you do?"}'# {"ok":true,"sessionId":"ses_…","continuationToken":"http:…",#  "playgroundUrl":"…?sessionId=ses_…","traceUrl":"…/v1/session/ses_…/events"}

Once accepted, the response returns sessionId for inspection and continuationToken for follow-ups; follow the stream for progress.

Body fieldContract
messageRequired user message
titleDisplay title
dryRunRun read tools and stub write tools
asOfISO-8601 instant with a timezone; sets ctx.now() and rejects omitted, relative, or later declared tool time arguments. Invalid values return 400
workspaceFilesRelative files added to the session workspace
purposeUse "eval" to mark regression traffic

Send a follow-up

POST /v1/session/:sessionId continues an existing conversation.

curl -X POST http://127.0.0.1:3000/<slug>/v1/session/ses_… \  -H 'content-type: application/json' \  -d '{"continuationToken":"http:…","message":"Make it shorter."}'

The route accepts any chat session, including one created by a custom channel. Each accepted follow-up rotates the continuation token and returns the replacement. A message sent to a busy session interrupts the active turn before starting.

The route returns 409 for a stale token or task session, and 403 when the caller doesn't own the session.

Stream or replay session events

GET /v1/session/:sessionId/stream is the live NDJSON feed.

curl -N 'http://127.0.0.1:3000/<slug>/v1/session/ses_…/stream?startIndex=0'

The route replays one event per line from startIndex, then follows new events. The default 0 replays the full stream. To reconnect without duplicates, pass the last index you received plus one.

GET /v1/session/:sessionId/events returns a one-shot NDJSON dump. Pass ?format=json for { sessionId, events, playgroundUrl }. See Stream events for the event vocabulary.

Manage sessions

RouteContract
POST /v1/session/:sessionId/stopInterrupt the active turn without sending another message
GET /v1/sessionsList sessions owned by the caller
GET /v1/session/:sessionId/costReturn per-turn token usage and estimated cost

On loopback under serve --dev, the session list includes every principal so webhook and schedule sessions appear in the playground. The cost route returns 404 for an unknown session.

Call a tool without a model turn

POST /v1/tools/:toolName runs a server tool with no model turn.

curl -X POST http://127.0.0.1:3000/<slug>/v1/tools/inspect_pr \  -H 'content-type: application/json' \  -d '{"input":{"prUrl":"https://github.com/acme/checkout/pull/42"}}'# {"ok":true,"toolName":"inspect_pr","callId":"tool_inspect_pr_…",#  "isError":false,"result":{…},"durationMs":12}
Body fieldContract
inputTool input; defaults to {}
sessionIdBind the call to an existing session
continuationTokenBind the call by its wire continuation token; mutually exclusive with sessionId

Omit both identifiers for an unbound call. Agent-execution tools return 400, and an unknown name returns 404 with the available names. See Tools for session binding, busy-session rules, and error codes.

Discovery routes

These read-only routes describe the running agent.

RouteContract
GET /v1/infoReturn the discovered model, tools, skills, connections, subagents, channels, schedules, hooks, and diagnostics
GET /v1/toolsReturn live server tools and advertised MCP tools as { name, title?, source? }; bind session-specific listings with session or continuationToken
GET /v1/tools/:nameReturn one tool's description, execution mode, effect rules, schemas, and source
GET /v1/logs?after=NReturn recent server logs and the next polling cursor

An unknown tool name returns 404 with available names. If an MCP connection can't list its tools, the catalog skips that connection and includes it in connectionErrors.

List and download artifacts

RouteContract
GET /v1/artifactsReturn { artifacts }, newest-updated first; filter with kind, sessionId, and a positive limit
GET /v1/artifacts/:id/contentDownload an artifact's file or blob as an attachment; return 404 when no content exists

The list follows the same ownership rules as GET /v1/sessions. See Artifacts for tagging and record fields.

Call custom channel routes

Authored routes mount under /v1/channels/<id> with their declared methods and paths, and each channel's auth chain applies. A hosted caller receives 202 Accepted with an empty body, never the handler's response. See Route responses.

MCP endpoint

Both MCP routes use stateless streamable HTTP. Send protocol requests with POST; GET and DELETE return 405.

RouteTools and auth
/v1/mcpask, check, and call_tool when server tools exist; uses the session API auth chain
/v1/mcp/toolsDeterministic server tools only; uses the CLI-level loopback, bearer, or anonymous auth chain

See MCP connections to connect one agent to another.

Playground eval routes

The playground Evals tab and remote eval commands use these routes:

RouteContract
GET /v1/dev/evalsList discovered cases and eval config
GET /v1/dev/evals/runsList recent run snapshots, newest first
POST /v1/dev/evals/runsStart { filterIds?, tags?, timeoutMs?, verbose? }; return 202, 404 for no match, or 409 while another run is active
GET /v1/dev/evals/runs/:runIdReturn progress and the final snapshot
POST /v1/dev/evals/runs/:runId/cancelCancel an active run; return 404 when unknown or 409 when no longer running

Poll the run route until its status is completed, failed, or cancelled. Entries within filterIds and tags use OR semantics; when both fields are present, a case must match each group.

Dev-mode routes

These routes exist only under serve --dev.

RouteContract
POST /v1/dev/schedules/:scheduleIdDispatch a schedule by hand, exactly once. Returns {scheduleId, sessionIds}
GET /v1/dev/remindersList reminders
POST /v1/dev/reminders/:reminderIdFire a reminder by hand

Schedules and reminders never fire automatically in dev mode. These routes run them manually.

Playground assets

GET /playground and GET /playground/assets/:file serve the playground. --no-playground removes both routes.

Status codes

Built-in routes use these common status codes.

CodeMeaning
400Invalid body, query, tool input, or request shape
401No auth policy admitted the request
403The caller is authenticated but doesn't own the resource
404A named resource doesn't exist, or an eval selection matches no cases
405The route doesn't accept this method
409The resource state rejects the request, such as a stale token or busy write
202The request was accepted for asynchronous work
500A write-effect tool can't create its session workspace (workspace_unavailable)