--- title: "Dashboard HTTP API" description: "HTTP operations, parameters, responses, and authentication for the Dashboard HTTP interface." kind: "Reference (informative)" audience: "HTTP client authors" --- Source: [OpenAPI description](../implementations/web/openapi.json). HTTP interface of the @cotal-ai/web reference implementation. This description covers read operations, readiness metadata and the legacy channel deletion route; it does not establish a separate stable product or Cloud management API. Start the dashboard using the web service preset described in the operator guide. The server defaults to 127.0.0.1:7799; use the host and port configured for your dashboard. Open the owner-private launch URL once to exchange its k query token for the cotal_web_session cookie. Keep the session on the same host. With the default bind, the permitted browser origins are [http://127.0.0.1](http://127.0.0.1):\, [http://localhost](http://localhost):\ and [http://cotal.localhost](http://cotal.localhost):\. A custom host uses its own HTTP origin. A cross-origin request is refused before session checks. The readiness nonce only opens /api/meta. Read operations use GET and take no request body. Some read handlers also accept other bodyless methods; this description does not promise a 405 response for them. The native web service is read-only and always refuses channel deletion with 403. Legacy direct dashboard instances can enable deletion; /api/meta reports channelDeleteSupported. Description version 1 identifies this document, not the wire or npm package version. ## Reading the contract The [interactive reference](https://docs.cotal.ai/api/dashboard/) and [downloadable OpenAPI specification](https://docs.cotal.ai/dashboard.openapi.json) describe the same operations. Schema references in the sections below resolve inside the downloadable specification. See [API reference](api-reference.mdx) for the other integration interfaces. ## List the roster [#list-roster] ```http GET /api/roster ``` Return the dashboard observer's current presence roster. Rows can include offline peers; freshness is reported separately by the presence-view SSE event. ### Parameters No operation parameters. ### Responses #### HTTP 200 The current snapshot or history page. Content type: `application/json`. ```json { "type": "array", "items": { "$ref": "#/components/schemas/RosterEntry" } } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Read channel membership [#read-membership] ```http GET /api/membership ``` Return broker-sourced live and durable channel membership. asOf is absent when no membership heartbeat has been written. Use this for display, not authorization. ### Parameters No operation parameters. ### Responses #### HTTP 200 The current snapshot or history page. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Membership" } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 503 The membership snapshot could not be read. A missing snapshot is not an empty successful one. Content type: `application/json`. ```json { "$ref": "#/components/schemas/MembershipError" } ``` ## List chat channels [#list-channels] ```http GET /api/channels ``` List chat channels with message counts and effective replay and delivery policies. Event channels are excluded. description and replayWindow are absent when unset. ### Parameters No operation parameters. ### Responses #### HTTP 200 The current snapshot or history page. Content type: `application/json`. ```json { "type": "array", "items": { "$ref": "#/components/schemas/Channel" } } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Read recent activity [#read-activity] ```http GET /api/activity ``` Merge the newest chat messages selected by stream sequence with recent direct messages, order by message timestamp and retain the newest limit. partial, missing and reasons identify sources that failed or exceeded the shared 8000 ms deadline. A partial page is still HTTP 200. Failure to obtain the channel list is HTTP 500. ### Parameters | Name | Location | Required | Schema | | ----- | -------- | -------- | ------------------------------------------------------------------------- | | limit | query | no | `{"type":"integer","minimum":0,"maximum":9007199254740991,"default":200}` | **limit:** Maximum requested messages. Absent or empty uses 200. Accepts decimal digits naming a safe integer, including 0; negative, fractional, whitespace-padded and unsafe values are refused. ### Responses #### HTTP 200 The current snapshot or history page. Content type: `application/json`. ```json { "$ref": "#/components/schemas/ActivityPage" } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The query or percent-encoded channel name is invalid. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Read direct-message history [#read-dms] ```http GET /api/dms ``` Return recent direct messages visible to the dashboard observer. A failed or late read returns HTTP 503 instead of a partial array. ### Parameters | Name | Location | Required | Schema | | ----- | -------- | -------- | ------------------------------------------------------------------------- | | limit | query | no | `{"type":"integer","minimum":0,"maximum":9007199254740991,"default":500}` | **limit:** Maximum requested messages. Absent or empty uses 500. Accepts decimal digits naming a safe integer, including 0; negative, fractional, whitespace-padded and unsafe values are refused. ### Responses #### HTTP 200 The current snapshot or history page. Content type: `application/json`. ```json { "type": "array", "items": { "$ref": "#/components/schemas/Message" } } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The query or percent-encoded channel name is invalid. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 503 The history read failed or did not finish within 8000 ms. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Read channel history [#read-channel-history] ```http GET /api/channels/{channel}/history ``` Percent-decode the channel once and validate it using the core channel grammar. Invalid names are refused without rewriting them to another channel. A failed or late read returns HTTP 503. ### Parameters | Name | Location | Required | Schema | | ------- | -------- | -------- | ------------------------------------------------------------------------- | | channel | path | yes | `{"type":"string","minLength":1,"maxLength":4096}` | | limit | query | no | `{"type":"integer","minimum":0,"maximum":9007199254740991,"default":200}` | **channel:** Channel name or pattern accepted by the core channel grammar. Encode it as one URL path component. **limit:** Maximum requested messages. Absent or empty uses 200. Accepts decimal digits naming a safe integer, including 0; negative, fractional, whitespace-padded and unsafe values are refused. ### Responses #### HTTP 200 The current snapshot or history page. Content type: `application/json`. ```json { "type": "array", "items": { "$ref": "#/components/schemas/Message" } } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The query or percent-encoded channel name is invalid. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 503 The history read failed or did not finish within 8000 ms. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Watch live dashboard events [#watch-feed] ```http GET /feed ``` Open a Server-Sent Events stream. Each frame has event: \ and data: \, followed by a blank line. The initial frames contain roster and presence-view, then membership or membership-read-failed. Later frames carry roster, presence-view, membership, membership-read-failed and message updates. The stream provides no event IDs or replay cursor; reconnect and read history for backfill. An unhandled request failure after headers have been sent closes the stream. Membership read failures are sent as events and leave the stream open. The schemas under x-sse-events describe decoded data payloads. | Event | JSON data | | ------------------------ | ------------------------------------------------------------------------------------ | | `roster` | Array of presence records, with optional dashboard progress. | | `presence-view` | `state` and `fresh`, plus `staleSince` for a stale observer. | | `membership` | Optional `asOf` and an array of `members` with live and durable channels. | | `membership-read-failed` | An object with a `reason` string. | | `message` | `mode` and `msg`, with subject-derived `senderId` and chat `channel` when available. | Decoded payload schemas are also included under `x-sse-events` in the [downloadable specification](https://docs.cotal.ai/dashboard.openapi.json). The message event forwards publisher JSON without wire-message validation. Treat msg.from as advisory; senderId is derived from the broker subject. A malformed publisher payload can still appear in the feed. ### Parameters No operation parameters. ### Responses #### HTTP 200 An open UTF-8 SSE stream. Authentication and origin checks run before streaming begins. `Cache-Control`: \{"type":"string","const":"no-cache"} Content type: `text/event-stream`. ```json { "type": "string" } ``` Example: ```text event: roster data: [] event: presence-view data: {"state":"current","fresh":true} ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ### Event payloads Each named event carries the following JSON data schema. ```json { "roster": { "type": "array", "items": { "$ref": "#/components/schemas/RosterEntry" } }, "presence-view": { "$ref": "#/components/schemas/PresenceView" }, "membership": { "$ref": "#/components/schemas/Membership" }, "membership-read-failed": { "type": "object", "properties": { "reason": { "type": "string" } }, "required": [ "reason" ] }, "message": { "$ref": "#/components/schemas/LiveMessage" }, "hello": { "description": "The first event of a feed: its id, which every watch request names. Sent before the roster so a browser can watch an agent at once.", "type": "object", "properties": { "feed": { "type": "string" } }, "required": [ "feed" ], "additionalProperties": false }, "agent-frame": { "description": "A frame an agent published on its own event channel, sent only to the feeds watching that agent. `id` is the agent's roster id, the subject's verified publisher.", "type": "object", "properties": { "id": { "type": "string" }, "msg": { "description": "The frame as the agent published it, unvalidated publisher JSON forwarded by the dashboard tap; consumers validate it before treating it as a wire message." } }, "required": [ "id", "msg" ], "additionalProperties": false }, "overflow": { "description": "The feed fell more than the server's buffer ceiling behind, so the server closed it; the browser reconnects to resume.", "type": "object", "properties": { "reason": { "type": "string" } }, "required": [ "reason" ], "additionalProperties": false } } ``` ## Read dashboard metadata [#read-dashboard-meta] ```http GET /api/meta ``` Return the space, process ID and whether channel deletion is supported. An authenticated browser session, one-use launch token or the private readiness nonce can access this route. The readiness nonce is accepted only here and does not mint a session. Query strings are accepted. Other bodyless methods also reach this read handler; no 405 behavior is promised. ### Parameters No operation parameters. ### Responses #### HTTP 200 Dashboard metadata. Content type: `application/json`. ```json { "$ref": "#/components/schemas/DashboardMeta" } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Delete a legacy dashboard channel [#delete-dashboard-channel] ```http POST /api/channel/delete ``` Legacy direct dashboard operation. The native lifecycle web service always returns 403 before reading the body because it has no channel-purger authority. Check channelDeleteSupported in /api/meta. A legacy instance deletes one concrete canonical channel and its content using its configured purger authority. Wildcards, aliases and missing channels are refused. The body limit is 8192 bytes. JSON is parsed without a Content-Type check. Other methods fall through to 404. ### Parameters No operation parameters. ### Request body The request uses the schema below. Required: yes. Content type: `application/json`. ```json { "type": "object", "properties": { "channel": { "type": "string", "minLength": 1 } }, "required": [ "channel" ] } ``` Example: ```json { "channel": "general" } ``` ### Responses #### HTTP 200 Channel content was purged and its configuration removed as reported. Content type: `application/json`. ```json { "$ref": "#/components/schemas/ChannelDeleted" } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 Missing or non-canonical channel, malformed JSON, or deletion refusal. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 A browser origin was refused, or the native service is configured read-only. A read-only refusal includes code PERMISSION_DENIED. Content type: `application/json`. ```json { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string", "enum": [ "PERMISSION_DENIED" ] } }, "required": [ "error" ] } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Watch an agent's event frames on a feed [#watch-agent] ```http POST /api/agents/{agent}/watch ``` Subscribe one open feed to the agent's event-plane frames, which then arrive on that feed as agent-frame events. No body: a watch is a row in this process's map, which the feed's close removes. It changes no mesh state. Watching again is still 204. Other methods fall through to 404. ### Parameters | Name | Location | Required | Schema | | ----- | -------- | -------- | -------------------------------------------------- | | agent | path | yes | `{"type":"string","minLength":3,"maxLength":4096}` | | feed | query | yes | `{"type":"string","minLength":1}` | **agent:** The agent's id, \.\ with each part in \[A-Za-z0-9\_], percent-encoded as one URL path component. Any other text is refused with 400. **feed:** The id the feed's hello event named. A missing id, or one that is not an open feed of this process, is refused with 400. ### Responses #### HTTP 204 Recorded. No body. #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The agent id or the feed id was refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Stop watching an agent's event frames on a feed [#unwatch-agent] ```http POST /api/agents/{agent}/unwatch ``` Remove the feed's watch of the agent. No body, no mesh state. Unwatching an agent the feed does not watch is still 204. Other methods fall through to 404. ### Parameters | Name | Location | Required | Schema | | ----- | -------- | -------- | -------------------------------------------------- | | agent | path | yes | `{"type":"string","minLength":3,"maxLength":4096}` | | feed | query | yes | `{"type":"string","minLength":1}` | **agent:** The agent's id, \.\ with each part in \[A-Za-z0-9\_], percent-encoded as one URL path component. Any other text is refused with 400. **feed:** The id the feed's hello event named. A missing id, or one that is not an open feed of this process, is refused with 400. ### Responses #### HTTP 204 Recorded. No body. #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The agent id or the feed id was refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Read an agent's recent event frames [#read-agent-events] ```http GET /api/agents/{agent}/events ``` Read the newest frames on the agent's event channel, bounded like its siblings: a read that does not finish within the aggregation deadline, or that fails, returns HTTP 503 saying which. A read the broker refuses is answered 200 with the refusal and, when known, the grant that would allow it. Other methods are refused with 405 and an Allow header naming GET. ### Parameters | Name | Location | Required | Schema | | ----- | -------- | -------- | ------------------------------------------------------------------------- | | agent | path | yes | `{"type":"string","minLength":3,"maxLength":4096}` | | limit | query | no | `{"type":"integer","minimum":0,"maximum":9007199254740991,"default":200}` | **agent:** The agent's id, \.\ with each part in \[A-Za-z0-9\_], percent-encoded as one URL path component. Any other text is refused with 400. **limit:** Maximum requested frames. Absent or empty uses 200. Accepts decimal digits naming a safe integer, including 0; negative, fractional, whitespace-padded and unsafe values are refused. ### Responses #### HTTP 200 The page, or the broker's refusal of the read. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AgentEventsPage" } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The agent id or the limit was refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 Origin is checked before session authentication. An origin outside this listener's allowed origins is refused. Content type: `application/json`. ```json { "$ref": "#/components/schemas/OriginError" } ``` Example: ```json { "error": "cross-origin" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 503 The history read failed or did not finish within 8000 ms. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Post as the operator [#send-as-operator] ```http POST /api/send ``` Post one message as the operator the process holds: \{kind: "msg", channel, text} to one concrete channel, or \{kind: "dm", to, text} to one roster member. The body is read before any refusal, so the 8192-byte cap bounds what a refused post costs, and every check runs before the poster starts, so a refused post never puts the operator on the roster. A wildcard or alias channel, an id not on the roster, an empty text, another kind, or a body that is not a JSON object is refused with 400. A read-only service, an expired operator credential and a cross-origin request answer 403 with the reason. A publish the broker refuses answers 502 with the refusal. Other methods fall through to 404. ### Parameters No operation parameters. ### Request body The request uses the schema below. Required: yes. Content type: `application/json`. ```json { "type": "object", "properties": { "kind": { "type": "string", "enum": [ "msg", "dm" ] }, "channel": { "type": "string", "minLength": 1, "description": "The channel, for kind msg. One concrete channel; no wildcard." }, "to": { "type": "string", "minLength": 1, "description": "The roster member's agent id, for kind dm." }, "text": { "type": "string", "minLength": 1, "description": "The message. Trimmed; an empty text is refused." } }, "required": [ "kind", "text" ] } ``` Example: channel ```json { "kind": "msg", "channel": "general", "text": "Ship it." } ``` Example: dm ```json { "kind": "dm", "to": "local.UACCAXW2KQKIQGMNYJFHWTWZSKA3J2TF7EFCABLCXS5UIZATMT345EWS", "text": "How long until the smoke is green?" } ``` ### Responses #### HTTP 200 The post went out. Content type: `application/json`. ```json { "$ref": "#/components/schemas/SendAnswer" } ``` #### HTTP 302 A valid one-use k query token is exchanged for a dashboard session. The response redirects to the same path without its query. A valid existing cookie takes precedence over k. `Location`: The requested path without the query string. `Set-Cookie`: cotal_web_session=\; Path=/; HttpOnly; SameSite=Strict. The default local listener uses HTTP. #### HTTP 400 The target, the text, the kind or the body was refused; the error names which. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 401 No valid session, an invalid launch token, or a spent launch token. The readiness credential does not authorize these routes. Content type: `application/json`. ```json { "$ref": "#/components/schemas/AuthenticationError" } ``` Example: ```json { "error": "unauthenticated" } ``` #### HTTP 403 A browser origin was refused (cross-origin), the service is read-only (post is off in /api/meta), or the operator credential expired. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 413 These read routes accept no request body. An announced body is refused before it is read; the connection is closed. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 500 The request failed. The body names the failure; a failed read does not terminate the dashboard. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 502 The broker refused the publish; the error carries the refusal as it came. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` #### HTTP 503 The dashboard is shutting down, so it no longer posts. Content type: `application/json`. ```json { "$ref": "#/components/schemas/Error" } ``` ## Schemas These schemas describe the HTTP request and response wrappers. The downloadable specification includes the referenced definitions. ### Message ```json { "$ref": "#/components/schemas/WireCotalMessage" } ``` ### RosterEntry ```json { "allOf": [ { "$ref": "#/components/schemas/WirePresence" }, { "type": "object", "properties": { "progress": { "type": "object", "properties": { "kind": { "const": "unknown", "type": "string" } }, "required": [ "kind" ] } } } ], "description": "Presence plus an optional progress signal. Working rows currently report progress.kind as unknown because the dashboard has no session-store observer." } ``` ### Membership ```json { "type": "object", "properties": { "asOf": { "type": "number" }, "members": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "live": { "type": "array", "items": { "type": "string" } }, "durable": { "type": "array", "items": { "type": "string" } }, "observedAt": { "type": "number" } }, "required": [ "id", "live", "durable", "observedAt" ] } } }, "required": [ "members" ] } ``` ### Channel ```json { "type": "object", "properties": { "channel": { "type": "string" }, "messages": { "type": "integer", "minimum": 0 }, "description": { "type": "string" }, "replay": { "type": "boolean" }, "replayWindow": { "type": "string" }, "deliveryClass": { "type": "string" } }, "required": [ "channel", "messages", "replay", "deliveryClass" ] } ``` ### ActivityEntry ```json { "oneOf": [ { "type": "object", "properties": { "mode": { "const": "chat", "type": "string" }, "channel": { "type": "string" }, "msg": { "$ref": "#/components/schemas/Message" } }, "required": [ "mode", "channel", "msg" ] }, { "type": "object", "properties": { "mode": { "const": "unicast", "type": "string" }, "msg": { "$ref": "#/components/schemas/Message" } }, "required": [ "mode", "msg" ] } ] } ``` ### ActivityPage ```json { "type": "object", "properties": { "entries": { "type": "array", "items": { "$ref": "#/components/schemas/ActivityEntry" } }, "partial": { "type": "boolean" }, "read": { "type": "integer", "minimum": 0, "maximum": 2 }, "of": { "type": "integer", "const": 2 }, "missing": { "type": "array", "items": { "type": "string", "enum": [ "chat", "direct messages" ] } }, "reasons": { "type": "object", "additionalProperties": { "type": "string" } }, "deadlineMs": { "type": "integer", "const": 8000 } }, "required": [ "entries", "partial", "read", "of", "missing", "reasons", "deadlineMs" ] } ``` ### Error ```json { "type": "object", "properties": { "error": { "type": "string" } }, "required": [ "error" ] } ``` ### AuthenticationError ```json { "type": "object", "properties": { "error": { "type": "string", "enum": [ "unauthenticated", "launch-token-already-used" ] } }, "required": [ "error" ] } ``` ### OriginError ```json { "type": "object", "properties": { "error": { "type": "string", "const": "cross-origin" } }, "required": [ "error" ] } ``` ### MembershipError ```json { "type": "object", "properties": { "error": { "type": "string", "const": "membership-read-failed" }, "reason": { "type": "string" } }, "required": [ "error", "reason" ] } ``` ### PresenceView ```json { "oneOf": [ { "type": "object", "properties": { "state": { "const": "current", "type": "string" }, "fresh": { "const": true, "type": "boolean" } }, "required": [ "state", "fresh" ] }, { "type": "object", "properties": { "state": { "const": "unpopulated", "type": "string" }, "fresh": { "const": false, "type": "boolean" } }, "required": [ "state", "fresh" ] }, { "type": "object", "properties": { "state": { "const": "stale", "type": "string" }, "fresh": { "const": false, "type": "boolean" }, "staleSince": { "type": "number" } }, "required": [ "state", "fresh", "staleSince" ] } ] } ``` ### LiveMessage ```json { "type": "object", "properties": { "mode": { "type": "string", "enum": [ "chat", "unicast", "anycast" ] }, "senderId": { "type": "string" }, "channel": { "type": "string" }, "msg": { "description": "Unvalidated publisher JSON forwarded by the dashboard tap. Well-formed wire messages use the Message schema in this description. Consumers must validate the payload before treating it as a wire message; msg.from is advisory and senderId comes from the broker subject." } }, "required": [ "mode", "msg" ] } ``` ### DashboardMeta ```json { "type": "object", "properties": { "space": { "type": "string" }, "pid": { "type": "integer" }, "channelDeleteSupported": { "type": "boolean" }, "post": { "description": "Whether this dashboard can post as the operator. `participant` posts as one operator for the life of the process; `off` says why posting is unavailable.", "oneOf": [ { "type": "object", "properties": { "mode": { "type": "string", "enum": [ "participant" ] } }, "required": [ "mode" ], "additionalProperties": false }, { "type": "object", "properties": { "mode": { "type": "string", "enum": [ "off" ] }, "reason": { "type": "string" } }, "required": [ "mode", "reason" ], "additionalProperties": false } ] }, "operatorId": { "type": "string", "description": "The operator's own roster id once the dashboard has posted, so the browser can tell its own messages apart." } }, "required": [ "space", "pid", "channelDeleteSupported", "post" ], "additionalProperties": false } ``` ### ChannelDeleted ```json { "type": "object", "properties": { "ok": { "const": true }, "channel": { "type": "string" }, "purged": { "type": "integer", "minimum": 0 }, "removed": { "type": "boolean" } }, "required": [ "ok", "channel", "purged", "removed" ], "additionalProperties": false } ``` ### AgentEventsPage ```json { "description": "The newest frames on the agent's event channel, oldest first, or the broker's refusal of that read.", "oneOf": [ { "type": "object", "properties": { "frames": { "type": "array", "items": { "$ref": "#/components/schemas/Message" } }, "more": { "type": "boolean", "description": "Whether older frames exist beyond this page." } }, "required": [ "frames", "more" ], "additionalProperties": false }, { "type": "object", "properties": { "refused": { "type": "string", "description": "The broker's refusal, as it came." }, "grant": { "type": "string", "description": "The command that grants this dashboard the read, when one is known." } }, "required": [ "refused" ], "additionalProperties": false } ] } ``` ### SendAnswer ```json { "type": "object", "description": "A post that went out. seq is a DM's stored sequence, the broker's proof that it kept the message; a channel post has none.", "properties": { "ok": { "type": "boolean", "enum": [ true ] }, "id": { "type": "string", "description": "The message id." }, "operatorId": { "type": "string", "description": "The operator's agent id, the same id /api/meta reports." }, "seq": { "type": "integer", "minimum": 1 } }, "required": [ "ok", "id", "operatorId" ], "additionalProperties": false } ```