Skip to content

Sending Traces

Mibo accepts traces on a single endpoint, POST /public/traces, with two supported request shapes. Both produce the same canonical trace internally — pick whichever fits your stack.

Both paths are first-class. Neither is a workaround for the other.

You’re on… Use
A service emitting OTel via the Anthropic / OpenAI / Traceloop / OpenLLMetry SDKs OTLP
n8n self-hosted ≥ 2.19 with N8N_OTEL_TRACING_ENABLED=true OTLP
n8n Cloud (no host-level OTel) Your API — see Send traces from n8n for all options
Flowise self-hosted with native OTel enabled OTLP
Flowise Cloud (no host-level OTel) Your API via the Flowise template
Anything else with an HTTP endpoint — LangChain agents, your own code, scripts, curl Your API

If you can choose OTLP, it usually wins — your existing exporter is already batched, retried, and instrumented. Your API exists so users on managed hosts (n8n Cloud, Flowise Cloud, anything where you can’t touch host env vars) get the same first-class trace ingestion.

  • Endpoint: POST https://api.mibo-ai.com/public/traces
  • Auth: x-api-key header. See API Keys & Trace Routing.
  • Identity: x-request-id HTTP header. Present → upserts the trace with that id. Absent → server-generated UUID, always creates a new trace.
  • Agent resolution: if the API key is scoped to a single agent, that agent is used. Otherwise the request must carry platformId (top level) or metadata.mibo.platform_id (Your API) / mibo.platform_id resource attribute (OTLP).
  • Canonical span model: spans with span_id, parent_span_id, name, attributes, optional timing and status. span.name is always the user-facing display label of the step.
OTLP Your API
Body shape resourceSpans[].scopeSpans[].spans[] (OTel envelope) { spans: [...] } (flat)
Attributes encoding OTel key/value list ({ key, value: { stringValue: "..." } }) Plain JSON object
Batching Multi-POST: server merges by traceId/span_id One POST per trace; create-or-replace by externalId
Best for Long-lived processes, SDK-instrumented services Synchronous flows, short scripts, managed-host integrations

The runner reads the same attributes on both paths — gen_ai.response.text, gen_ai.usage.input_tokens, gen_ai.tool.name, http.response.status_code, and the rest of the GenAI / HTTP semconv keys. Your assertions don’t change.

Whichever path you send on, these are the attributes assertions look up. Emit what you have; missing data surfaces as missing instrumentation in the dashboard rather than silent pass/fail.

Attribute What it powers
gen_ai.response.text Semantic assertions, response_regex, default text extraction
gen_ai.output.messages Same as above when present (preferred over response.text)
gen_ai.usage.input_tokens / output_tokens / reasoning.output_tokens token_limit
gen_ai.tool.name + gen_ai.tool.call.arguments tool_call (the span becomes a child tool call of its parent)
http.response.status_code http_status
Span name node_call (case-insensitive substring match)
Root span start/end_time_unix_nano response_time.max_ms

See Assertion Reference for the full mapping.