Tracing and analytics
When an answer is wrong, see exactly why
Every request leaves a trace: what was retrieved, what the model was asked, which tools it called with which arguments, what came back, how long each step took and what it cost. Find any of them from the id in your own logs.
200 free credits when you sign up. No card needed.
Tracing
The Tracing page lists every request from every channel, newest first, with search and auto-refresh. Traces are kept for 90 days.
| Status and time | OK or the error code, and when it ran |
|---|---|
| Trace id | Also in the stream's meta and done events and the X-Trace-Id header |
| Agent and model | Which agent answered, on which model |
| Duration | End to end |
| Tokens and cost | Input and output tokens and the estimated model cost |
| Rounds and tools | How many model rounds, which tools were called |
| Feedback | Thumbs up or down from the widget |
What a trace holds
Traces are OpenTelemetry spans using the GenAI semantic conventions, so the attributes are the standard ones. Model inputs and outputs and each tool call's input and output are recorded as span events, with personal details redacted. A chat request records these spans.
chat.request | The whole request, with agent, model, status and duration |
|---|---|
auth.validate_user | Checking the API key |
agent.fetch | Loading the agent's configuration (cached) |
preflight.rate_limit | The per-agent requests-per-minute check |
preflight.credits | Spending the reply's credit |
mcp.discovery | Loading the agent's MCP tools |
rag.prefetch | Hybrid search and rerank over the agent's knowledge |
conversation.start_or_load | Creating or loading the conversation |
agentic.round[n] | Each round of the model deciding, calling tools and answering |
llm.chat | The provider call, with model, tokens and time to first token |
tool.execute | One MCP tool call, with its timing and outcome |
curl -N https://api.sentientone.ai/v1/chat/stream \ -H "Content-Type: application/json" \ -H "X-Api-Key: $SENTIENTONE_API_KEY" \ -H "X-Agent-Id: $AGENT_ID" \ -d '{"message": "Where is order 10482?"}'const res = await fetch("https://api.sentientone.ai/v1/chat/stream", { method: "POST", headers: { "Content-Type": "application/json", "X-Api-Key": process.env.SENTIENTONE_API_KEY, "X-Agent-Id": process.env.AGENT_ID, }, body: JSON.stringify({ message: "Where is order 10482?" }),})if (!res.ok) { const { error } = await res.json() // { code, message, request_id } throw new Error(`${error.code}: ${error.message} (ref ${error.request_id})`)}const reader = res.body.getReader()const decoder = new TextDecoder()let buffer = ""while (true) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value, { stream: true }) const events = buffer.split("\n\n") buffer = events.pop() for (const e of events) { if (!e.startsWith("data: ")) continue const event = JSON.parse(e.slice(6)) if (event.type === "delta") process.stdout.write(event.content) if (event.type === "error") throw new Error(event.code) }}import json, osimport httpxheaders = { "X-Api-Key": os.environ["SENTIENTONE_API_KEY"], "X-Agent-Id": os.environ["AGENT_ID"],}body = {"message": "Where is order 10482?"}with httpx.stream("POST", "https://api.sentientone.ai/v1/chat/stream", headers=headers, json=body, timeout=130) as res: res.raise_for_status() for line in res.iter_lines(): if not line.startswith("data: "): continue event = json.loads(line[6:]) if event["type"] == "delta": print(event["content"], end="", flush=True) elif event["type"] == "done": conversation_id = event["conversation_id"] # send it back to continuedata: {"type": "meta", "conversation_id": "9b1c…", "trace_id": "4f2a…"}data: {"type": "sources", "sources": [{"index": 1, "title": "Shipping policy", "url": "https://…", "score": 0.82}]}data: {"type": "tool_call", "id": "call_1", "name": "get_order", "input": {"order_id": "10482"}}data: {"type": "tool_result", "id": "call_1", "name": "get_order", "output": "…"}data: {"type": "delta", "content": "Order 10482 left our warehouse"}data: {"type": "delta", "content": " on Tuesday and…"}data: {"type": "done", "conversation_id": "9b1c…", "trace_id": "4f2a…", "usage": {"input_tokens": 1830, "output_tokens": 64, "total_tokens": 1894}}- chat.request: The whole request
- auth.validate_user: Your secret key
- agent.fetch: Agent config, cached
- preflight.rate_limit: Per-agent requests per minute
- preflight.credits: 1 credit reserved
- rag.prefetch: Hybrid search and rerank
- agentic.round[1]: The model decides to call a tool
- llm.chat: Provider call
- tool.execute: get_order on your MCP server
- agentic.round[2]: The answer streams back
- llm.chat: Tokens stream as delta events
Span names as Tracing records them. Bar lengths here are illustrative.
One id, end to end
Send X-Request-Id with your call (or let the API make one). The same id is on every log line, every span and every error report for that request, and it's passed on to your model provider, your MCP servers and your workflow webhooks.
- Error responses and error events include it as request_id, so a failure in your logs points straight at the trace.
- Workflow runs carry it through each step.
Analytics
The Analytics page charts every agent, or one at a time.
| Requests and errors | Over time, per agent |
|---|---|
| Response time | And time to first token, the number users feel |
| Tools | Which tools are called, how often |
| Estimated cost | Model spend from tokens and each agent's price per million tokens |
| Credits left | Your balance and how fast it's going |
Per reply, in the Playground
While you build, the Playground shows each reply's latency, tokens, tool calls and sources beside it, so you rarely need to open a trace to see why an answer came out the way it did.
What isn't recorded
Traces are yours, in your account. Our own product analytics and error reports carry ids, counts, lengths and timings only: never message text, prompts, replies, tool arguments or keys.
Trace your first request in minutes
Sign up and you get 200 free credits, no card needed. Create an agent, add your knowledge, try it in the Playground, then call it from your code or put it on your website.