Reference
HTTP ingest API
Use the raw HTTP ingest API when you can't use the Python SDK.
Endpoint
POST {base_url}/v1/traces/ingestDefault base_url is https://api.staso.ai.
Auth
X-API-Key: ak_live_...
Content-Type: application/jsonWorkspace-scoped keys require every trace in the batch to set workspace_slug.
Request body
{
"traces": [
{
"trace_id": "4b2f1e0a-7c36-4b9e-8c12-93a2b8d4e0f1",
"timestamp": "2026-04-15T12:00:00.000Z",
"environment": "prod",
"workspace_slug": "default",
"root_span_name": "agent-run",
"status": "ok",
"model": "claude-sonnet-4",
"input_tokens": 120,
"output_tokens": 345,
"total_tokens": 465,
"duration_ms": 1820.5,
"attributes": "{}",
"session_id": "conv-42",
"agent_id": "b0b2...",
"agent_name": "support-bot",
"user_id": "user-alice",
"spans": [
{
"span_id": "1f77...",
"parent_span_id": null,
"name": "support-bot",
"kind": "agent",
"status": "ok",
"timestamp": "2026-04-15T12:00:00.000Z",
"duration_ms": 1820.5,
"input": "{\"query\":\"where is my order?\"}",
"output": "{\"result\":\"...\"}",
"attributes": "{\"tags\":[\"beta\"]}",
"session_id": "conv-42",
"agent_id": "b0b2...",
"agent_name": "support-bot",
"user_id": "user-alice"
}
]
}
]
}Trace fields
| Field | Type | Default | Notes |
|---|---|---|---|
trace_id | string | null | — | Any UUID. Server generates one if omitted. |
timestamp | string (ISO 8601) | — | Required. Within ±24h of server clock. |
environment | string | "default" | |
workspace_slug | string | "" | Required for workspace-scoped keys. |
duration_ms | float | 0 | |
root_span_name | string | "" | Usually the agent name. |
status | string | "ok" | ok | error | timeout. |
model | string | "" | |
input_tokens / output_tokens / total_tokens | int | 0 | |
attributes | string | "{}" | Stringified JSON. |
session_id | string | "" | |
agent_id / agent_name / user_id | string | "" | |
spans | SpanIngest[] | [] | Ordered list. |
Span fields
| Field | Type | Default | Notes |
|---|---|---|---|
span_id | string | null | — | Generated if omitted. |
parent_span_id | string | null | null | null for root. |
timestamp | string (ISO 8601) | — | Required. ±24h of server clock. |
duration_ms | float | 0 | |
name | string | — | Required. |
kind | string | "llm" | llm | tool | chain | retriever | agent | custom. |
status | string | "ok" | ok | error | timeout. |
model | string | "" | |
input_tokens / output_tokens / total_tokens | int | 0 | |
input / output | string | "" | Stringified JSON. |
attributes | string | "{}" | Stringified JSON for span metadata. |
error_message | string | null | null | |
session_id / agent_id / agent_name / user_id | string | "" |
session_idvsconversation_id. The wire schema usessession_idto group traces into a conversation. Everywhere else — Python SDK, dashboard, docs — this is called a conversation. Sendsession_idon the wire; read about it asconversation_id.
Constraints
- 1–500 traces per request.
- Every
timestampwithin ±24h of server clock. kind:llm | tool | chain | retriever | agent | custom.status:ok | error | timeout.input,output,attributesare strings holding JSON, not nested objects.
Responses
| Status | Body | Meaning |
|---|---|---|
200 | {"accepted": N, "rejected": N, "errors": [...]} | Batch accepted. errors lists per-trace failures. |
400 | — | Malformed body or missing workspace_slug for a workspace-scoped key. |
402 | — | Payment required. |
403 | {"detail": "Organization has no active plan..."} | No active plan, or workspace not in key scope. |
429 | {"detail": "Rate limit exceeded ..."} | Honor Retry-After. |
Rate limits
Counted per organization across four windows:
| Window | Header on limit |
|---|---|
| Per minute | Retry-After: 60 |
| Per hour | — |
| Per day | — |
| Per month | Retry-After: 86400 |
Batched requests are atomic — if any window would overflow, the entire batch is rejected and no counter is incremented. See pricing for current limits.
curl
curl -X POST https://api.staso.ai/v1/traces/ingest \
-H "X-API-Key: $STASO_API_KEY" \
-H "Content-Type: application/json" \
-d @trace.json