Skip to main content

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.

MethodPathPurpose
GET/api/copilotkit/statusReport 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.

MethodPathAuthPurpose
GET/api/chatwoot/statuspublicReport configuration
POST/api/chatwoot/sessionsession or guestIdCreate/restore a contact and conversation
GET/api/chatwoot/conversationssession or guestIdList the contact's conversations
POST/api/chatwoot/conversationssession or guestIdForce-create a new conversation
POST/api/chatwoot/messagessession or guestIdMirror a message into Chatwoot
GET/api/chatwoot/messagessession or guestIdPoll messages
POST/api/chatwoot/request-humansession or guestIdAsk for a human agent
POST/api/chatwoot/webhookpublicReceive 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:

PathChannelUsed by
/tide/networks/:userIdBase64/devicesA local network agent publishing discovered devices
/tide/client/:networkId?/tideThe IDE/editor talking to devices on a remote network
/workers/workersBuild 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.

StatusMeaning
200Reserved (or already yours)
400 device not availableSomeone else holds an unexpired reservation
404 device not foundThe 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.