Traces: Custom API
The Custom API parser is the simplest. It works for any system that isn’t n8n or Flowise, whether it’s built with LangChain, your own code, or any other framework.
Minimal example
Section titled “Minimal example”{ "data": { "text": "The AI responded with this message." }}That’s it. Mibo extracts the response text from text and evaluates your semantic assertions against it.
Response text extraction
Section titled “Response text extraction”The parser looks for text in this order:
- The path configured in Response Parsing (set in your endpoint configuration)
- Common keys:
text,message,content,output,answer,response - The entire response as a string (fallback)
So any of these work:
{ "text": "Hello!" }{ "message": "Hello!" }{ "content": "Hello!" }{ "output": "Hello!" }{ "answer": "Hello!" }{ "response": "Hello!" }If your API returns a nested structure, set the response path in your platform configuration (e.g., data.choices.0.message.content) and send the full response:
{ "data": { "choices": [ { "message": { "content": "The actual response text" } } ] }}With tool calls
Section titled “With tool calls”If your AI system uses tools/functions, include them as tool_calls:
{ "data": { "text": "I found flights to Madrid starting at $299.", "tool_calls": [ { "name": "search_flights", "arguments": { "origin": "NYC", "destination": "Madrid", "date": "2026-04-01" } }, { "name": "format_results", "arguments": { "format": "summary", "limit": 5 } } ] }}Each tool call needs:
name(string): the function/tool namearguments(object): the input passed to the tool
With node-level detail
Section titled “With node-level detail”If your system has multiple steps or nodes (like a pipeline or workflow), you can add node-level structure so node_call assertions work. Use each node name as a key mapping to an object with an output field:
{ "data": { "Retrieve Documents": { "output": { "count": 12, "source": "knowledge-base" }, "status": "success" }, "AI Agent": { "output": { "text": "Based on the 12 documents retrieved..." }, "status": "success", "tools_called": [ { "name": "search_kb", "input": { "query": "pricing" }, "output": { "results": 12 } } ] } }}You can then assert on specific nodes and their output values:
{ "target": "node_call", "condition": "MUST_CALL", "expected_name": "Retrieve Documents", "expected_arguments": { "count": { "matcher": "gte", "value": 1 } }}The node names in your trace ("Retrieve Documents", "AI Agent") are the same names you reference in expected_name. Each node’s output becomes the data that expected_arguments checks against. You can also use expected_tool_calls on nodes that include a tools_called array.
Full example with curl
Section titled “Full example with curl”curl -X POST "https://api.mibo-ai.com/public/traces" \ -H "Content-Type: application/json" \ -H "x-api-key: mibo_your_api_key" \ -d '{ "data": { "text": "Your appointment has been scheduled for tomorrow at 9 AM.", "tool_calls": [ { "name": "create_booking", "arguments": { "date": "2026-03-09", "time": "09:00", "user_id": 12345 } } ] } }'Compatible assertions
Section titled “Compatible assertions”| Assertion type | Works? | What it checks |
|---|---|---|
| Semantic | Yes | Evaluates extracted text |
json_match | Yes | Checks fields in the data |
response_regex | Yes | Matches pattern against text |
json_schema | Yes | Validates data structure |
http_status | Only if _http_status is in data | |
response_time | Only if _metadata.duration_ms is in data | |
token_limit | Automatic if response includes a supported token format | |
node_call | Yes | Checks node entries in trace data (requires node-level structure) |
node_call + expected_arguments | Yes | Checks node output values (requires nodes with output keys) |
node_call + expected_tool_calls | Yes | Checks tool calls within a node (requires tools_called array) |