Parlane

REST contract

MCP is the primary transport. This is the fallback for everything else — a vibe-coder can implement it in about 50 lines; reference servers in Node and Python live in examples/. Auth is a bearer token on every request.

GET /.well-known/parlane.json

Returns the manifest. 200 + application/json. See the manifest reference.

POST /chat → SSE stream

POST /chat
{
  "conversation_id": "conv_123",
  "message": "What's the reactor load?",
  "attachments": []
}

conversation_id is client-managed; your server may override it by returning a different id in the done event. Response: Content-Type: text/event-stream, one SSE event per line-group. The event types are the contract:

eventdatameaning
delta{ text }An incremental chunk of assistant text. Concatenate in order.
tool{ tool, status, label? }Tool-call activity indicator.
done{ conversation_id, message_id? }End of turn.
error{ code, message }Terminal error; the stream ends.
example stream
event: delta
data: {"text": "Reactor load is holding at "}

event: delta
data: {"text": "62%."}

event: tool
data: {"tool": "check_reactor", "status": "start", "label": "Checking reactor…"}

event: tool
data: {"tool": "check_reactor", "status": "end"}

event: done
data: {"conversation_id": "conv_123", "message_id": "msg_9"}

The stream must end with exactly one done or one error. Clients paint the first delta within 150 ms of receipt — that's the target for a responsive-feeling composer, not a hard guarantee your network will hit.

GET /ui/{dashboard_id}

Returns a UI document (see the component catalog). 200 + application/json. dashboard_id matches a manifest dashboards[].id.

POST /action → { ui? | ack? }

POST /action
{
  "action_id": "a1b2c3d4-0001",
  "tool": "set_environment",
  "dashboard_id": "systems",
  "params": {
    "source": "systems-dashboard",
    "scrubber": true,
    "targetTemp": 21.5
  }
}

Response — exactly one of ui / ack is meaningful:

{
  "ui": {
    "version": 1,
    "id": "systems",
    "root": { "type": "stack", "props": { "direction": "vertical" }, "children": [] }
  }
}
// or, when nothing needs to change on screen:
{ "ack": true }

If ui is present, the client replaces the current document entirely (diffed by id). Otherwise it applies the action envelope's refresh. A failed tool returns { error: { code?, message } }, surfaced non-fatally. There is no patch response — full-replace only.

POST /notify

Relay-side only. Your app-facing server never implements this directly — see relay self-hosting.

Auth

Bearer token on every request: Authorization: Bearer <token>. A server may print a QR encoding both its URL and a revocable, regenerable token so connecting is a single scan:

parlane://connect?url=https%3A%2F%2Fyour-server.example.com&token=sk_live_...

Attachments

v1 = images, text, and URLs, with a 10 MB cap per attachment. Arbitrary files are deferred, based on real demand rather than a speculative feature.

fieldtypemeaning
kind"image"|"text"|"url"Attachment type.
mimestringMIME type.
namestring?Original filename, when known.
datastring?Base64 payload for image/text. ≤ 10 MB decoded.
urlstring?For kind: "url", or a pre-uploaded blob URL.

Exactly one of data / url is present.

Next

Relay self-hosting →