curl --location --request POST 'https://dashboard.laburen.com/api/agents/<agentId>/query' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data-raw '{
"query": "Hello, I need help with my order",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "clxxxxxxxxxxxxxxxxx"
}'
const apiUrl = 'https://dashboard.laburen.com/api';
const apiKey = '<API_KEY>';
const agentId = '<agentId>';
const response = await fetch(`${apiUrl}/agents/${agentId}/query`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
query: 'Hello, I need help with my order',
conversationId: 'clxxxxxxxxxxxxxxxxx', // Optional: to continue conversation
visitorId: 'clxxxxxxxxxxxxxxxxx', // Optional: user identifier
// systemPrompt: 'You are a helpful assistant that only answers in English.', // Optional override
// modelName: 'gpt_5_6_luna', // Optional: run this request on another model
// reasoningEffort: 'high', // Optional: off | low | medium | high | max
}),
});
const data = await response.json();
console.log(data.answer);
// Save data.conversationId to continue the conversation
import requests
api_url = "https://dashboard.laburen.com/api"
api_key = "<API_KEY>"
agent_id = "<agentId>"
response = requests.post(
f"{api_url}/agents/{agent_id}/query",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
},
json={
"query": "Hello, I need help with my order",
"conversationId": "clxxxxxxxxxxxxxxxxx", # Optional: to continue conversation
"visitorId": "clxxxxxxxxxxxxxxxxx", # Optional: user identifier
# "systemPrompt": "You are a helpful assistant.", # Optional override
# "modelName": "gpt_5_6_luna", # Optional: run on another model
# "reasoningEffort": "high", # Optional: off | low | medium | high | max
},
)
data = response.json()
print(data["answer"])
# Save data["conversationId"] to continue the conversation
{
"answer": "Hello! I'd be happy to help you with your order. Could you please provide your order number so I can look it up?",
"usage": {
"completionTokens": 28,
"promptTokens": 1250,
"totalTokens": 1278,
"cost": 0.0064
},
"sources": [
{
"source": "FAQ.pdf",
"chunk": "To check your order status, please provide your order number..."
}
],
"approvals": [],
"messageId": "clxxxxxxxxxxxxxxxxx",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "clxxxxxxxxxxxxxxxxx",
"request_human": false,
"status": "UNRESOLVED"
}
Agentes
Agent - Query
Send messages to an AI agent and receive responses. Supports text queries, file attachments, real-time streaming, and conversation continuity.
POST
/
api
/
agents
/
{agentId}
/
query
curl --location --request POST 'https://dashboard.laburen.com/api/agents/<agentId>/query' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data-raw '{
"query": "Hello, I need help with my order",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "clxxxxxxxxxxxxxxxxx"
}'
const apiUrl = 'https://dashboard.laburen.com/api';
const apiKey = '<API_KEY>';
const agentId = '<agentId>';
const response = await fetch(`${apiUrl}/agents/${agentId}/query`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
query: 'Hello, I need help with my order',
conversationId: 'clxxxxxxxxxxxxxxxxx', // Optional: to continue conversation
visitorId: 'clxxxxxxxxxxxxxxxxx', // Optional: user identifier
// systemPrompt: 'You are a helpful assistant that only answers in English.', // Optional override
// modelName: 'gpt_5_6_luna', // Optional: run this request on another model
// reasoningEffort: 'high', // Optional: off | low | medium | high | max
}),
});
const data = await response.json();
console.log(data.answer);
// Save data.conversationId to continue the conversation
import requests
api_url = "https://dashboard.laburen.com/api"
api_key = "<API_KEY>"
agent_id = "<agentId>"
response = requests.post(
f"{api_url}/agents/{agent_id}/query",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
},
json={
"query": "Hello, I need help with my order",
"conversationId": "clxxxxxxxxxxxxxxxxx", # Optional: to continue conversation
"visitorId": "clxxxxxxxxxxxxxxxxx", # Optional: user identifier
# "systemPrompt": "You are a helpful assistant.", # Optional override
# "modelName": "gpt_5_6_luna", # Optional: run on another model
# "reasoningEffort": "high", # Optional: off | low | medium | high | max
},
)
data = response.json()
print(data["answer"])
# Save data["conversationId"] to continue the conversation
{
"answer": "Hello! I'd be happy to help you with your order. Could you please provide your order number so I can look it up?",
"usage": {
"completionTokens": 28,
"promptTokens": 1250,
"totalTokens": 1278,
"cost": 0.0064
},
"sources": [
{
"source": "FAQ.pdf",
"chunk": "To check your order status, please provide your order number..."
}
],
"approvals": [],
"messageId": "clxxxxxxxxxxxxxxxxx",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "clxxxxxxxxxxxxxxxxx",
"request_human": false,
"status": "UNRESOLVED"
}
This endpoint allows you to send messages to an AI agent and receive responses. It supports:
Webhook Headers:
Events:
To make the same agent answer as fast and cheap as possible, send the opposite:
If the model rejects the level, the response is a
Immediate Response:
Webhook Receiver Example (Node.js/Express):
- Simple text queries
- File attachments (documents, images, audio)
- Real-time response streaming
- Continuation of existing conversations
- Contact association (CRM)
- Per-request overrides of the agent’s prompt, model and reasoning level
Path
string
required
The ID of the agent you want to query (CUID format).
Body
Required
string
required
The user’s message or question.
Optional - Basic Configuration
boolean
default:"false"
If
true, responds with Server-Sent Events in real-time.string
ID to continue an existing conversation. Auto-generated if not provided.
string
Unique ID of the visitor/user. Auto-generated if not provided.
string
default:"api"
Source channel for the message. Valid values:
api, dashboard, website, form, whatsapp, telegram, slack, meta, crisp, zapier, mail, mercadolibre, agent_builder, chatwoot, crmchatsappai.string
Additional context for the AI (e.g., specific instructions).
string
Overrides the agent’s system prompt for this request only. The agent’s saved configuration in the database is not modified.Use this to test different prompt variants, run evaluations, or inject custom instructions without editing the agent from the dashboard.
An empty string
"" is treated as no override — the agent’s saved system prompt is used instead. If both systemPrompt and promptType: "raw" are provided, promptType: "raw" takes precedence.Optional - Model & Reasoning Overrides
string
Overrides the agent’s model for this request only. The agent’s saved configuration in the database is not modified.Use the identifier form used by the agent configuration, with underscores — for example
gpt_5_6_sol, gemini_3_8_flash, claude_5_sonnet, gpt_6_luna, glm_5_3.These identifiers are not the same as the provider-facing model names used by the Increment Agent Queries endpoint. Here it is
gpt_5_6_sol, there it is gpt-5.6-sol.string
Controls how much internal reasoning the model does before answering, for this request only. The agent’s saved reasoning level is not modified.Valid values:
The exceptions worth remembering: most Gemini models cannot turn reasoning off — The level is validated against the model that will actually run the request: the
off, low, medium, high, max. Always send one of these five — Laburen translates them to whatever each provider expects, so you never send provider-native values such as none or a thinking budget.Which of the five a model accepts depends on the model. Sending a level the model does not support returns 400.modelName | Accepted values | Default |
|---|---|---|
gpt_5_6_sol | off, low, medium, high | high |
gpt_5_6_terra | off, low, medium, high | high |
gpt_5_6_luna | off, low, medium, high | medium |
glm_5_2 | off, low, medium, high, max | high |
glm_5_3 | low, medium, high, max | medium |
glm_5_3_flash | low, medium, high, max | medium |
claude_5_sonnet | off, low, medium, high, max | medium |
claude_5_5_opus | low, medium, high, max | medium |
gpt_6_astra | low, medium, high, max | medium |
gpt_6_sol | off, low, medium, high, max | medium |
gpt_6_luna | off, low, medium, high, max | low |
gemini_3_pro_preview | low, medium, high | high |
gemini_3_1_pro_preview | low, medium, high | high |
gemini_3_flash | low, medium, high | high |
gemini_3_8_flash | low, medium, high | medium |
gemini_3_5_flash_lite | off, low, medium, high | off |
gemini_3_5_flash_lite is the only one that accepts off, and there off means Google’s lowest level rather than reasoning fully disabled. glm_5_3, glm_5_3_flash, claude_5_5_opus and gpt_6_astra cannot turn it off either: reasoning is mandatory on those models. The DeepSeek models (deepseek_r1, deepseek_v4_pro, deepseek_v4_flash) always reason at a fixed level, so they reject every value.On
gemini_3_8_flash, low produces little or no reasoning in practice, even on complex prompts. If you need the model to actually reason, start at medium.Any model not listed in the table has no configurable reasoning. Sending
reasoningEffort to one of those returns 400.modelName you send in this same body, or the agent’s saved model when you omit it.When you omit reasoningEffort, the agent’s saved level is used. If the agent has none saved, the model’s default from the table applies. The override works with conversational mode on or off.Optional - Webhook (Conversational Mode)
string
URL to receive the AI response asynchronously. Required when the agent has conversational mode enabled and
channel is api.The webhook URL must:- Be a valid URL format
- Use
http://orhttps://protocol - Be reachable (the server validates connectivity)
- Not point to localhost or private IPs in production (SSRF protection)
Optional - File Attachments
Optional - Contact (CRM)
object
Response (without streaming)
string
The agent’s response.
array
string
ID of the response message.
string
ID of the conversation (save this to continue the conversation).
string
ID of the visitor.
boolean
Whether the agent requested human intervention.
string
Conversation status (e.g.,
UNRESOLVED, RESOLVED).object
array
List of approvals for tool executions (if any).
Response (Conversational Mode with Webhook)
When the agent has conversational mode enabled and you provide awebhookUrl, the endpoint returns immediately with a queued status:
string
Always
"queued" for webhook mode.string
ID of the conversation.
string
ID of the visitor.
string
ID of the user’s input message.
string
Confirmation message indicating the request was queued.
string
The webhook URL where the response will be sent.
Webhook Payload
After processing (typically 8 seconds delay to batch multiple messages), a POST request is sent to yourwebhookUrl with the following payload:
{
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "visitor-abc123",
"status": "success",
"messages": [
{
"id": "msg_001",
"text": "First part of the response...",
"createdAt": "2024-01-15T10:30:00.000Z"
},
{
"id": "msg_002",
"text": "Second part of the response...",
"createdAt": "2024-01-15T10:30:00.000Z"
}
],
"agentResponse": {
"answer": "Complete response without splitting...",
"sources": [],
"usage": {
"completionTokens": 150,
"promptTokens": 500,
"totalTokens": 650,
"cost": 0.0065
},
"metadata": {}
},
"messageId": "msg_answer456"
}
Content-Type: application/jsonX-Laburen-Event: agent.response
In conversational mode, long responses are automatically split into multiple messages (maximum 3 for most channels, up to 10 for Instagram/Meta).
Streaming Response
Whenstreaming: true, the endpoint responds with Server-Sent Events:
Content-Type: text/event-stream
event: answer
data: Hello,
event: answer
data: how can
event: answer
data: I help you?
event: endpoint_response
data: {"messageId":"...","answer":"...","conversationId":"..."}
data: [DONE]
answer: Partial response text (concatenate to build the full answer)endpoint_response: Full response object (JSON) with all metadata
Error Responses
| Status Code | Type | Description |
|---|---|---|
| 400 | Bad Request | Invalid channel value. Returns list of valid channels. |
| 400 | Bad Request | Missing webhookUrl when agent has conversational mode enabled and channel is api. |
| 400 | Bad Request | Invalid webhookUrl (malformed URL, invalid protocol, unreachable server). |
| 400 | Bad Request | Unknown reasoningEffort value. Returns the list of valid levels. |
| 400 | Bad Request | reasoningEffort not supported by the model that will run the request. |
| 401 | UNAUTHORIZED | Invalid API Key or insufficient permissions |
| 404 | NOT_FOUND | Agent not found |
| 500 | Internal Error | Error processing the message |
curl --location --request POST 'https://dashboard.laburen.com/api/agents/<agentId>/query' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data-raw '{
"query": "Hello, I need help with my order",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "clxxxxxxxxxxxxxxxxx"
}'
const apiUrl = 'https://dashboard.laburen.com/api';
const apiKey = '<API_KEY>';
const agentId = '<agentId>';
const response = await fetch(`${apiUrl}/agents/${agentId}/query`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
query: 'Hello, I need help with my order',
conversationId: 'clxxxxxxxxxxxxxxxxx', // Optional: to continue conversation
visitorId: 'clxxxxxxxxxxxxxxxxx', // Optional: user identifier
// systemPrompt: 'You are a helpful assistant that only answers in English.', // Optional override
// modelName: 'gpt_5_6_luna', // Optional: run this request on another model
// reasoningEffort: 'high', // Optional: off | low | medium | high | max
}),
});
const data = await response.json();
console.log(data.answer);
// Save data.conversationId to continue the conversation
import requests
api_url = "https://dashboard.laburen.com/api"
api_key = "<API_KEY>"
agent_id = "<agentId>"
response = requests.post(
f"{api_url}/agents/{agent_id}/query",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
},
json={
"query": "Hello, I need help with my order",
"conversationId": "clxxxxxxxxxxxxxxxxx", # Optional: to continue conversation
"visitorId": "clxxxxxxxxxxxxxxxxx", # Optional: user identifier
# "systemPrompt": "You are a helpful assistant.", # Optional override
# "modelName": "gpt_5_6_luna", # Optional: run on another model
# "reasoningEffort": "high", # Optional: off | low | medium | high | max
},
)
data = response.json()
print(data["answer"])
# Save data["conversationId"] to continue the conversation
{
"answer": "Hello! I'd be happy to help you with your order. Could you please provide your order number so I can look it up?",
"usage": {
"completionTokens": 28,
"promptTokens": 1250,
"totalTokens": 1278,
"cost": 0.0064
},
"sources": [
{
"source": "FAQ.pdf",
"chunk": "To check your order status, please provide your order number..."
}
],
"approvals": [],
"messageId": "clxxxxxxxxxxxxxxxxx",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "clxxxxxxxxxxxxxxxxx",
"request_human": false,
"status": "UNRESOLVED"
}
Model and Reasoning Override Example
Run a single request on a different model and reasoning level without changing the agent. Useful for A/B testing prompts, evaluations, or spending more reasoning on a hard question while the agent stays cheap by default.cURL
curl --location --request POST 'https://dashboard.laburen.com/api/agents/<agentId>/query' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data-raw '{
"query": "Compare these two contracts and tell me which one is riskier",
"modelName": "gpt_5_6_sol",
"reasoningEffort": "high"
}'
cURL
curl --location --request POST 'https://dashboard.laburen.com/api/agents/<agentId>/query' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data-raw '{
"query": "What are your opening hours?",
"modelName": "gpt_5_6_sol",
"reasoningEffort": "off"
}'
400:
{
"error": "Model \"gemini_3_flash\" does not support reasoningEffort \"off\""
}
Neither override is persisted. The agent keeps the model and reasoning level saved from the dashboard, so the next request without these fields behaves exactly as before.
Streaming Example (JavaScript)
import {
EventStreamContentType,
fetchEventSource,
} from '@microsoft/fetch-event-source';
const apiUrl = 'https://dashboard.laburen.com/api';
const apiKey = '<API_KEY>';
const agentId = '<agentId>';
let answer = '';
let endpointResponse = '';
const ctrl = new AbortController();
await fetchEventSource(`${apiUrl}/agents/${agentId}/query`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
signal: ctrl.signal,
body: JSON.stringify({
streaming: true,
query: 'Hello, I need help',
conversationId: 'optional-conversation-id',
visitorId: 'optional-visitor-id',
}),
async onopen(response) {
if (response.status === 401) {
throw new Error('Unauthorized');
}
if (response.status === 402) {
throw new Error('Usage limit exceeded');
}
},
onmessage: (event) => {
if (event.data === '[DONE]') {
// End of stream
ctrl.abort();
// Parse the full response
const fullResponse = JSON.parse(endpointResponse);
console.log('Full response:', fullResponse);
} else if (event.data?.startsWith('[ERROR]')) {
console.error('Stream error:', event.data);
} else if (event.event === 'endpoint_response') {
endpointResponse += event.data;
} else if (event.event === 'answer') {
answer += event.data;
// Update UI with partial answer
console.log('Partial answer:', answer);
}
},
onerror: (error) => {
console.error('Connection error:', error);
},
});
Webhook Example (Conversational Mode)
When your agent has conversational mode enabled, usewebhookUrl to receive the AI response asynchronously:
cURL
curl --location --request POST 'https://dashboard.laburen.com/api/agents/<agentId>/query' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data-raw '{
"query": "Hello, I need help",
"channel": "api",
"webhookUrl": "https://your-server.com/webhook/laburen"
}'
{
"status": "queued",
"conversationId": "clxxxxxxxxxxxxxxxxx",
"visitorId": "visitor-abc123",
"inputMessageId": "msg_input123",
"message": "Your message has been queued. Response will be sent to the provided webhookUrl after processing.",
"webhookUrl": "https://your-server.com/webhook/laburen"
}
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/laburen', (req, res) => {
const event = req.headers['x-laburen-event'];
if (event === 'agent.response') {
const { conversationId, messages, agentResponse } = req.body;
console.log('Conversation:', conversationId);
console.log('Messages:', messages.length);
// Process each split message
messages.forEach((msg, i) => {
console.log(`Message ${i + 1}:`, msg.text);
});
// Full answer available in agentResponse
console.log('Full answer:', agentResponse.answer);
console.log('Usage:', agentResponse.usage);
}
res.status(200).send('OK');
});
app.listen(4000, () => {
console.log('Webhook server listening on port 4000');
});
Was this page helpful?

