BLLM REST API
The BLLM API lets you programmatically execute agents, manage conversations, and query knowledge bases. All endpoints are under /api/v1/ and require API key authentication.
Base URL:
https://your-domain.com/api/v1
Authentication
Every request must include an API key. Create and manage keys under Settings → Public API keys (REST integrations). LLM provider keys are separate. Each key has scoped permissions, optional IP allowlists, and custom rate limits.
Pass the key via either header:
Authorization: Bearer bllm_pk_a1b2c3d4e5f6... # or X-API-Key: bllm_pk_a1b2c3d4e5f6...
Key format
Keys are prefixed with bllm_pk_ followed by 32 hex characters. The raw key is shown once at creation — only a SHA-256 hash is stored server-side.
Scopes
Each key is granted one or more scopes that control what it can access:
| Scope | Grants |
|---|---|
| agents:read | List agents, get agent details |
| agents:execute | Execute / run an agent |
| conversations:read | List conversations, get details, list messages |
| conversations:write | Create conversations, send messages |
| knowledge:read | List knowledge bases, query a knowledge base |
Error Handling
All errors return a consistent JSON envelope:
{
"error": {
"code": "API_KEY_INVALID",
"message": "The provided API key is invalid.",
"status": 401
},
"meta": {
"requestId": "req_a1b2c3d4e5f6g7h8",
"timestamp": "2026-03-19T10:00:00.000Z"
}
}Error codes
| Code | Status | Meaning |
|---|---|---|
| API_KEY_REQUIRED | 401 | No API key header provided |
| API_KEY_INVALID | 401 | Key format is wrong or key not found |
| API_KEY_REVOKED | 401 | Key has been revoked |
| API_KEY_EXPIRED | 401 | Key has passed its expiration date |
| IP_NOT_ALLOWED | 403 | Request IP not in key's allowlist |
| INSUFFICIENT_SCOPE | 403 | Key lacks the required scope for this endpoint |
| AGENT_NOT_ACCESSIBLE | 403 | Agent is not in the key's allowed agent list |
| AGENT_NOT_PUBLISHED | 403 | Agent exists but is not published |
| RATE_LIMIT_EXCEEDED | 429 | Per-key rate limit exceeded |
| NOT_FOUND | 404 | Resource does not exist |
| VALIDATION_ERROR | 422 | Missing or invalid request body fields |
| INTERNAL_ERROR | 500 | Unexpected server error |
Rate Limits
Each API key has a configurable rate limit (default: 60 requests/minute). Every response includes rate limit headers:
| Header | Description |
|---|---|
| X-RateLimit-Limit | Maximum requests per window |
| X-RateLimit-Remaining | Requests remaining in current window |
| X-RateLimit-Reset | Unix timestamp when the window resets |
| Retry-After | Seconds to wait (only on 429 responses) |
Pagination
List endpoints use cursor-based pagination. Pass ?limit=20&cursor=clx... as query parameters. Responses include a pagination object:
{
"data": [...],
"pagination": {
"cursor": "clx...",
"hasMore": true,
"limit": 20
},
"meta": { ... }
}/api/v1/agentsReturns a paginated list of published agents accessible to this API key.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
| limit | integer | 20 | Max results (1-50) |
| cursor | string | — | Cursor from previous response |
Response
{
"data": [
{
"id": "clx...",
"name": "Support Agent",
"description": "Handles customer questions",
"published": true,
"createdAt": "2026-03-01T00:00:00.000Z",
"updatedAt": "2026-03-15T12:00:00.000Z"
}
],
"pagination": { "cursor": null, "hasMore": false, "limit": 20 },
"meta": { "requestId": "req_...", "timestamp": "..." }
}/api/v1/agents/:idReturns details for a single published agent.
Path parameters
| Param | Description |
|---|---|
| id | Agent ID |
Response
{
"data": {
"id": "clx...",
"name": "Support Agent",
"description": "Handles customer questions",
"published": true,
"createdAt": "2026-03-01T00:00:00.000Z",
"updatedAt": "2026-03-15T12:00:00.000Z"
},
"meta": { ... }
}/api/v1/agents/:id/executeExecute an agent. Creates a new conversation (or continues an existing one) and returns the agent's response events.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| userInput | string | Yes | The user's message |
| variables | object | No | Initial variables for the execution |
| conversationId | string | No | Continue an existing conversation |
| chatHistory | array | No | Previous messages for context |
| context | object | No | Conversation context (key-value pairs accessible to LLM nodes as {{variables}}) |
{
"userInput": "What is my order status?",
"conversationId": "clx...",
"context": {
"customer_name": "Alice",
"tier": "premium",
"order_id": "ORD-1234"
}
}The context object is stored on the conversation and injected into the execution variables. Each key becomes a {{variable}} in LLM prompts (e.g. {{customer_name}}), and the full JSON is available as {{__context}}. On subsequent calls with an existing conversationId, new context fields are shallow-merged with the stored context. If an LLM node has context enrichment enabled, it will auto-extract facts from the conversation and merge them back. The current context is always returned in the response.
Response
{
"data": {
"events": [
{ "type": "message", "content": "Let me look that up for you..." },
{ "type": "message", "content": "Your order #1234 shipped on March 15." },
{ "type": "done" }
],
"newState": { "order_id": "1234", "status": "shipped" },
"currentNodeId": null,
"conversationId": "clx...",
"context": {
"customer_name": "Alice",
"tier": "premium",
"order_id": "ORD-1234",
"topic": "order_status"
},
"success": true
},
"meta": { ... }
}Event types: message (agent text), wait_input (agent is waiting for user), error (execution error), done (flow finished successfully — omitted when the run ends with errors). handoff may appear for human handoff flows.
success: true when the run completed with a done event and no terminal errors; false on failure; omitted when the run is only paused on wait_input.
Webhooks: If a webhook URL is configured on your API key, the execution result is also delivered asynchronously via HTTP POST to that URL. For agents using batch input, the batch flush is automatically scheduled server-side and the result is delivered via a batch.flushed webhook event. See the Webhooks section for details.
/api/v1/conversationsList conversations for your agents. Scoped to the API key's allowed agents if configured.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
| limit | integer | 20 | Max results (1-50) |
| cursor | string | — | Cursor from previous response |
Response
{
"data": [
{
"id": "clx...",
"agentId": "clx...",
"agentName": "Support Agent",
"title": "API Conversation",
"status": "ACTIVE",
"createdAt": "...",
"updatedAt": "..."
}
],
"pagination": { ... },
"meta": { ... }
}/api/v1/conversationsCreate a new conversation for a published agent. Optionally attach context that LLM nodes can reference.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| agentId | string | Yes | ID of a published agent |
| title | string | No | Conversation title (default: "API Conversation") |
| context | object | No | Key-value context for the conversation (max 10KB) |
{
"agentId": "clx...",
"title": "Support Chat",
"context": {
"customer_name": "Alice",
"tier": "premium",
"account_id": "ACC-42"
}
}The context is stored on the conversation and automatically injected into LLM node variables. Each key becomes a {{variable}} (e.g. {{customer_name}}) and the full JSON is available as {{__context}}. Context can be updated on subsequent messages via the Send Message or Execute Agent endpoints.
Response (201)
{
"data": {
"id": "clx...",
"agentId": "clx...",
"title": "Support Chat",
"status": "ACTIVE",
"context": {
"customer_name": "Alice",
"tier": "premium",
"account_id": "ACC-42"
},
"createdAt": "..."
},
"meta": { ... }
}/api/v1/conversations/:idGet details for a single conversation.
Response
{
"data": {
"id": "clx...",
"agentId": "clx...",
"agentName": "Support Agent",
"title": "API Conversation",
"status": "ACTIVE",
"createdAt": "...",
"updatedAt": "..."
},
"meta": { ... }
}/api/v1/conversations/:id/messagesList messages in a conversation, ordered oldest-first.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
| limit | integer | 50 | Max results (1-100) |
| cursor | string | — | Cursor from previous response |
Response
{
"data": [
{ "id": "clx...", "role": "user", "content": "Hello", "createdAt": "..." },
{ "id": "clx...", "role": "assistant", "content": "Hi! How can I help?", "createdAt": "..." }
],
"pagination": { ... },
"meta": { ... }
}/api/v1/conversations/:id/messagesSend a message in an existing conversation. This triggers agent execution using the conversation's agent and returns the response events.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| content | string | Yes | The message text |
| context | object | No | Updated context fields (shallow-merged with existing context) |
{
"content": "What is my order status?",
"context": {
"mood": "frustrated",
"last_order": "ORD-5678"
}
}When context is provided, it is shallow-merged into the conversation's stored context before execution. The response includes the current context (which may also include fields auto-extracted by LLM context enrichment).
Response
Same format as the Execute Agent response — includes events, newState, context, and conversationId.
/api/v1/knowledgeList knowledge bases belonging to the API key's owner.
Response
{
"data": [
{
"id": "clx...",
"name": "Product Docs",
"description": "Internal documentation",
"documentCount": 12,
"createdAt": "...",
"updatedAt": "..."
}
],
"pagination": { ... },
"meta": { ... }
}/api/v1/knowledge/:id/queryRun a semantic search against a knowledge base. Returns the most relevant document chunks with similarity scores.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | The search query |
| limit | integer | No | Max results (default 5, max 20) |
{ "query": "How do I reset my password?", "limit": 3 }Response
{
"data": {
"query": "How do I reset my password?",
"results": [
{
"content": "To reset your password, go to Settings > Security...",
"similarity": 0.92,
"metadata": null
}
],
"total": 1
},
"meta": { ... }
}API Key Management
These endpoints are authenticated via your session (browser login), not API keys. Use them from the dashboard or your own admin tooling.
/api/keys/publicCreate a new API key. The raw key is returned once in the response.
{
"name": "Production Key",
"scopes": ["agents:read", "agents:execute", "conversations:write"],
"allowedAgentIds": ["clx..."],
"allowedIPs": ["203.0.113.0/24"],
"expiresAt": "2027-01-01T00:00:00Z",
"rateLimitPerMinute": 100
}/api/keys/publicList all your API keys. Returns prefix, scopes, status, and last used — never the raw key.
/api/keys/public/:idUpdate key name, scopes, allowed agents, allowed IPs, or rate limit.
/api/keys/public/:idRevoke a key. Sets status to revoked. Immediate effect.
/api/keys/public/:id/rotateRevoke the current key and create a new one with the same configuration. Returns the new raw key once.
/api/keys/public/:id/usagePaginated usage logs for a key. Supports ?from= and ?to= date filters.
Webhooks
Webhooks let your server receive real-time HTTP POST notifications when agent execution completes. Configure a webhook URL (and optional signing secret) on your API key via Settings → Public API keys or the key management API.
Configuration
Add a webhookUrl and optional webhookSecret when creating or updating an API key. When set, every execution triggered by that key will POST results to the configured URL.
// Create key with webhook
POST /api/keys/public
{
"name": "Production Key",
"scopes": ["agents:execute", "conversations:write"],
"webhookUrl": "https://your-server.com/webhook",
"webhookSecret": "whsec_your_secret_key"
}Event types
| Event | Description |
|---|---|
| execution.completed | Agent execution finished successfully |
| execution.error | Agent execution failed |
| batch.flushed | Batch input timer expired and batch was flushed automatically |
Payload format
{
"event": "execution.completed",
"conversationId": "clx...",
"agentId": "clx...",
"data": {
"events": [
{ "type": "message", "content": "Your order shipped on March 15." },
{ "type": "done" }
],
"newState": { "order_id": "1234" },
"currentNodeId": null,
"context": { "customer_name": "Alice" },
"success": true
},
"timestamp": "2026-06-03T14:30:00.000Z"
}Request headers
| Header | Description |
|---|---|
| Content-Type | application/json |
| X-BLLM-Event | The event type (e.g. execution.completed) |
| X-BLLM-Signature | HMAC-SHA256 signature (only when webhookSecret is set) |
| User-Agent | BLLM-Webhook/1.0 |
Signature verification
When a webhookSecret is configured, each webhook request includes an X-BLLM-Signature header containing sha256=<hex-digest>. Verify it by computing HMAC-SHA256 of the raw request body with your secret:
const crypto = require("crypto");
function verifySignature(secret, body, signature) {
const expected = "sha256=" +
crypto.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}Retry behavior
If the initial delivery fails (non-2xx response or network error), one retry is attempted after 3 seconds. The webhook delivery is fire-and-forget and does not block the API response.
Batch mode and server-side flush
When an agent uses batch input mode, the server automatically schedules a flush after the configured batch timeout. This means your integration does not need to implement its own timer or send a flushBatch request — the system handles it and delivers the result via a batch.flushed webhook event. If new messages arrive before the timeout, the timer is reset. You can still send flushBatch: true manually to trigger an early flush.