HTTP API channel
Integrate external apps with Cradle through a local REST API — sync, async, webhook, and scoped authentication.
The HTTP API channel exposes Cradle as a local server for mobile apps, web
frontends, CLI tools, cron jobs, and automation platforms. It uses the same
triage → RAG → agent → operator flow as Telegram and the widget, so every
feature behaves identically across channels.
Default port: 31416. Widget uses 31415 by default.
Authentication
All endpoints under /api/v1/* except /api/v1/health require a key:
Authorization: Bearer <api-key>or
X-Api-Key: <api-key>Keys are scoped. See Authentication and scopes for the scope grammar and project-switching rules.
Scopes per endpoint
| Endpoint | Method | Scope |
|---|---|---|
/api/v1/health | GET | (none) |
/api/v1/messages | POST | messages:write |
/api/v1/messages/:chatId | GET | messages:read |
/api/v1/admin/agents | GET / POST | agents:read / agents:write |
/api/v1/admin/agents/:id/execute | POST | agents:execute |
/api/v1/admin/kb | GET | kb:read |
/api/v1/admin/models | GET | models:read |
/api/v1/admin/models/:catalogId/pull | GET | models:write |
/api/v1/admin/tickets | GET | tickets:read |
/api/v1/admin/keys | GET / POST | admin:* |
/api/v1/projects | * | admin:* |
Project header
Admin keys can switch projects per request:
curl -H "x-api-key: ck_live_…" \
-H "x-cradle-project: support" \
http://127.0.0.1:31416/api/v1/admin/agentsScoped keys must use the project they are bound to, or the server returns 403.
POST /api/v1/messages
Send a message into Cradle.
{
"text": "What is the warranty policy?",
"chatId": "client-42",
"userId": "user-123",
"meta": { "anything": "you-want" },
"mode": "sync",
"webhookUrl": "https://your-app.example.com/cradle-webhook",
"timeoutMs": 60000
}| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | Message text, 1–50 000 characters |
chatId | string | no | Conversation id; generated as UUID if omitted. Reuse for threading. |
userId | string | no | External user id, stored in meta. |
meta | object | no | Arbitrary data saved to tickets.channel_meta. |
mode | enum | no | sync (default), async, webhook |
webhookUrl | string | no | Callback URL. Forces webhook mode. |
timeoutMs | number | no | Max wait in ms for sync/webhook. Default from settings, max 120 000. |
sync mode
Long-poll until the agent replies or timeout. Response (200):
{
"ok": true,
"mode": "sync",
"chatId": "client-42",
"ticketId": "550e8400-e29b-41d4-a716-446655440000",
"replies": [
{ "type": "message", "text": "Warranty is 24 months...", "markdown": false },
{ "type": "attachment", "filename": "warranty.xlsx", "mime": "...", "base64": "..." }
]
}replies is an array. Markdown tables may be returned as XLSX attachments.
async mode
Server immediately returns 202. Replies accumulate in a buffer and are drained
via GET /api/v1/messages/:chatId.
{
"ok": true,
"mode": "async",
"chatId": "client-42",
"ticketId": "550e8400-...",
"drainUrl": "/api/v1/messages/client-42"
}webhook mode
Server returns 202 and later POSTs to webhookUrl with an HMAC-SHA256
signature:
X-Cradle-Signature: sha256=<hex>The secret defaults to the API key if webhookSecret is not configured in the
channel settings.
GET /api/v1/messages/:chatId
Drain accumulated replies for a chat.
| Param | Type | Description |
|---|---|---|
:chatId | string | Same id used in POST |
wait | number | 0 returns immediately; >0 long-polls up to 60 000 ms |
Response:
{
"ok": true,
"chatId": "client-42",
"replies": [ { "type": "message", "text": "..." } ]
}An empty replies array is not an error.
Rate limits
Default: 120 requests / minute / (IP + key).
Enabling the channel
In the desktop app: Integrations → HTTP API:
- Generate an API key (at least 16 characters for legacy migration, or use a
scoped
admin:*key from the CLI). - Choose
bindHost:127.0.0.1— local processes only.0.0.0.0— LAN access (open the firewall only to trusted hosts).
- Save and start.
On a headless server the API channel is enabled by default after
cradle server init.