Integrations
Endpoints that back optional platform integrations: the AI assistant, support
chat, documentation proxying, remote device access and build workers. Each
integration depends on server configuration and reports 503 when it is not
configured.
AI assistant (Cody)
Base path: /api/copilotkit.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/copilotkit/status | Report whether the assistant is available |
| ALL | /api/copilotkit/* | CopilotKit v2 (AG-UI) runtime endpoint |
GET /api/copilotkit/status
{ "available": true }
available is false when no AI provider is configured on the server. In that
case every other path under /api/copilotkit returns 503 with an error
message saying so.
The catch-all route is an agent runtime, not a hand-written REST API. The client
calls /info for agent discovery, /agent/:id/connect, /agent/:id/run,
/agent/:id/stop, and /transcribe beneath the base path. Use the matching
CopilotKit client library rather than calling these directly — the protocol,
including streaming, is defined by it.
The server exposes a single agent, default, with a knowledge-base search tool
over the AppBlocks documentation. Project-editing tools are supplied by the web
app at runtime, so an external client will get conversation and documentation
search but none of the project manipulation the web app offers.
Support chat (Chatwoot)
Base path: /api/chatwoot. These endpoints bridge the in-app assistant to a
Chatwoot support inbox, so a conversation with the AI can be handed over to a
human agent.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/chatwoot/status | public | Report configuration |
| POST | /api/chatwoot/session | session or guestId | Create/restore a contact and conversation |
| GET | /api/chatwoot/conversations | session or guestId | List the contact's conversations |
| POST | /api/chatwoot/conversations | session or guestId | Force-create a new conversation |
| POST | /api/chatwoot/messages | session or guestId | Mirror a message into Chatwoot |
| GET | /api/chatwoot/messages | session or guestId | Poll messages |
| POST | /api/chatwoot/request-human | session or guestId | Ask for a human agent |
| POST | /api/chatwoot/webhook | public | Receive Chatwoot events |
Actors and ownership
Every call resolves an actor: either the signed-in user or an anonymous guest
identified by a client-generated guestId. The actor determines ownerKey, the
contact identifier used with Chatwoot. ownerKey must be sent on most calls and
is checked against the resolved actor — a mismatch returns
403 {"error":"ownerKey does not match session owner"}, and an unresolvable
actor returns 401 {"error":"Login or guestId is required"}.
When Chatwoot is not configured, all of these return
503 {"error":"Chatwoot is not configured"}.
Status
GET /api/chatwoot/status
{
"available": true,
"botApi": true,
"agentApi": true,
"assistantMirrorApi": "bot",
"anonymousAllowed": true
}
assistantMirrorApi reports which credential set is used to mirror assistant
replies (bot, agent or none).
Session
POST /api/chatwoot/session
{
"projectId": "664a1f2b8c9d0e0011223301",
"projectName": "Boiler control",
"guestId": "guest-8f2a…",
"conversationId": 4711
}
Creates or restores the Chatwoot contact and conversation:
{
"contactIdentifier": "user-8a3f…",
"conversationId": 4711,
"pubsubToken": "…",
"ownerKey": "user-8a3f…",
"anonymous": false
}
Anonymous actors without a conversationId get a response with null
identifiers — no contact is created until they actually send something.
Conversations
GET /api/chatwoot/conversations?ownerKey=…&contactIdentifier=…&guestId=…
POST /api/chatwoot/conversations
GET returns { "conversations": [ … ] }, newest first, or an empty array when
the caller has no contact yet. POST takes
{ guestId, ownerKey, projectId, projectName } and always creates a new
conversation, returning { contactIdentifier, conversationId }.
Messages
POST /api/chatwoot/messages
GET /api/chatwoot/messages?contactIdentifier=…&conversationId=…&afterId=0&ownerKey=…
POST mirrors one message:
{
"contactIdentifier": "user-8a3f…",
"conversationId": 4711,
"ownerKey": "user-8a3f…",
"role": "user",
"content": "My device is offline",
"guestId": "guest-8f2a…",
"projectId": "664a1f2b…",
"projectName": "Boiler control"
}
content, role and ownerKey are required (400 otherwise). role must be
user (delivered as an incoming contact message) or assistant (delivered as a
bot message). Mirroring an assistant message may be skipped when no bot/agent
credentials are configured, in which case the response carries
{"message":null,"skipped":true,"reason":"…"}.
GET requires contactIdentifier, conversationId and ownerKey, and returns
{ "messages": [ … ] } — contact, bot and human agent messages, excluding private
notes, with only messages newer than afterId. Poll it to display agent replies.
Request a human
POST /api/chatwoot/request-human
{
"conversationId": 4711,
"contactIdentifier": "user-8a3f…",
"ownerKey": "user-8a3f…",
"guestId": "guest-8f2a…",
"projectId": "664a1f2b…",
"projectName": "Boiler control"
}
Reopens the conversation for human agents, clears the bot assignment and posts a
private note announcing the request. Returns
{ success, contactIdentifier, conversationId }. Requires Chatwoot's agent API
credentials, otherwise 503.
Webhook
POST /api/chatwoot/webhook
Public receiver for Chatwoot events. Always answers
200 {"received": true}. Agent replies are currently delivered to clients by
polling GET /api/chatwoot/messages, so no processing happens here.
Tibbit documentation proxy
GET /api/tibbits/docs/:model
Public. Fetches the documentation page for a Tibbit from docs.tibbo.com and
returns it as text/html. Underscores in :model are converted to hyphens, so
/api/tibbits/docs/00_3 proxies https://docs.tibbo.com/tibbit_00-3.
Upstream failures are relayed with the upstream status code (or 502) and an
empty body. The request times out after 10 seconds. This exists so the editor can
show Tibbit datasheets inline without hitting cross-origin restrictions.
Remote device access (TIDE)
Base paths: /tide/networks, /tide/client, /workers.
These are realtime socket endpoints, not REST. They proxy WebSocket and HTTP-polling traffic to an internal realtime server with three channels:
| Path | Channel | Used by |
|---|---|---|
/tide/networks/:userIdBase64 | /devices | A local network agent publishing discovered devices |
/tide/client/:networkId? | /tide | The IDE/editor talking to devices on a remote network |
/workers | /workers | Build workers taking jobs |
A network agent connects to /tide/networks/<base64 user id> (or the literal
public) and registers under that user. Clients connect to /tide/client, which
requires authentication — unauthenticated connections are rejected with
401 unauthorized — and receive a NETWORKS_UPDATED event listing the networks
available to them (their own plus any public ones).
Two plain HTTP routes accompany the socket traffic:
GET /tide/client/list
Returns the caller's networks, plus public networks:
[
{ "name": "Office LAN", "networkType": "private", "id": "aBcD1234" },
{ "name": "Demo lab", "networkType": "public", "id": "eFgH5678" }
]
GET /tide/client/remotedevices/:address
POST /tide/client/remotedevices/:address/settarget
DELETE /tide/client/remotedevices/:address/settarget
:address is a device MAC as seen by the network agent.
GET describes a discovered device:
{
"mac": "0.36.53.1.2.3",
"address": "0.36.53.1.2.3",
"lastUpdated": 1755412800000,
"userId": "8a3f…",
"nextAvailability": 1755413400000
}
userId is returned only when you hold the reservation. nextAvailability is
the epoch milliseconds at which the current reservation expires.
POST …/settarget reserves the device for exclusive access — required before
uploading or debugging over a shared network. A user may hold one reservation
at a time; taking a new one releases the previous. Reservations last for a
server-configured period (10 minutes by default) and are released automatically
once they expire.
| Status | Meaning |
|---|---|
200 | Reserved (or already yours) |
400 device not available | Someone else holds an unexpired reservation |
404 device not found | The address has not been discovered |
DELETE …/settarget releases your reservation; releasing a device you do not hold
returns 400 device not reserved.
Build workers
/workers
Realtime channel used by build runners. A worker connects, sends an update
event with its key and capabilities (for example build:tios,
build:zephyr), and then receives job events. Workers report progress and
results back on the job event, and current load on load.
The dispatcher picks up the oldest enqueued
build job whose type matches some worker's
capabilities and assigns it to the least loaded compatible worker. Jobs whose
type no worker can handle are failed immediately with
No compatible worker found. A worker connecting with an unknown key is
disconnected.
Submit and poll build jobs through /api/tasks; this
channel is only for worker processes.