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 neither GOOGLE_API_KEY nor GEMINI_API_KEY is set on the server. In that case every other path under /api/copilotkit returns:

{ "error": "No AI provider configured. Set GOOGLE_API_KEY or GEMINI_API_KEY environment variable." }

with status 503.

The catch-all route is the CopilotKit v2 runtime, not a hand-written REST API. The v2 client calls /info for agent discovery, /agent/:id/connect, /agent/:id/run, /agent/:id/stop, and /transcribe beneath the base path. Use the CopilotKit client library rather than calling these directly — the protocol (including streaming) is defined by CopilotKit.

The server exposes a single agent, default, running Gemini 2.5 Flash with a searchKnowledgeBase tool backed by a Pinecone index of the AppBlocks documentation. Project-editing tools are supplied by the frontend at runtime, so an external client will get conversation and documentation search but none of the project manipulation the web app offers.

note

This route is registered before body parsing so the runtime can read the raw request stream. Send requests exactly as the CopilotKit client does.

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 Socket.IO endpoints, not REST. The Express routes proxy WebSocket and HTTP polling traffic to an internal Socket.IO server with three namespaces:

PathNamespaceUsed 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 a session — the proxy rejects unauthenticated upgrades 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 REMOTE_DEVICE_TIMEOUT minutes (default 10) and are swept once a minute.

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

Socket.IO namespace 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 polls every second, picks up the oldest enqueued build job whose type matches some worker's capabilities, and assigns it to the worker with the lowest active jobs × average duration score. 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 namespace is only for worker processes.