API reference
Submit telemetry events
Send batched cost telemetry from a Pylva SDK runtime with customer context, model usage, latency, and pricing-safe metadata.
The SDK sends batched telemetry events to this endpoint. Direct use is possible, but the SDK is recommended because it handles batching, retries, context propagation, and rule interactions.
Per-event semantic failures reject only the offending events and report them in
The response can also include
Authentication
Send your API key inX-Pylva-Key. Rate limit: 1000 requests/min per key, shared with the other telemetry endpoints.
curl -X POST https://api.pylva.com/api/v1/events \
-H "X-Pylva-Key: $PYLVA_API_KEY" \
-H "Content-Type: application/json" \
-d @batch.json
Request
The SDK sends this wire shape automatically. If you call the endpoint directly, use the full event shape below. Every event field is required - nullable is not the same as omittable. Send explicitnull for fields you do not use; a partial event is rejected with 400 VALIDATION_ERROR. schema_version is the literal string "1.6". The batch envelope takes 1-100 events.
{
"batch_id": "00000000-0000-4000-8000-000000000001",
"sdk_version": "1.0.0",
"events": [
{
"schema_version": "1.6",
"run_id": "00000000-0000-4000-8000-000000000002",
"parent_run_id": null,
"trace_id": "00000000-0000-4000-8000-000000000003",
"span_id": "00000000-0000-4000-8000-000000000004",
"parent_span_id": null,
"timestamp": "2026-06-11T10:00:00.000Z",
"customer_id": "cust_abc123",
"step_name": "evaluate",
"provider": "openai",
"model": "gpt-4o-mini",
"tokens_in": 1200,
"tokens_out": 320,
"latency_ms": 842,
"tool_name": null,
"status": "success",
"framework": "none",
"instrumentation_tier": "sdk_wrapper",
"cost_source": "auto",
"metric": null,
"metric_value": null,
"stream_aborted": false,
"abort_savings_usd": 0,
"sdk_version": "1.0.0",
"metadata": {
"token_count_source": "exact"
}
}
]
}
Event fields
The authoritative schema is api.pylva.com/openapi.json. Summary:| Field | Type | Notes |
|---|---|---|
schema_version | string | Literal "1.6". |
run_id, trace_id, span_id | uuid | span_id is the idempotency key. |
parent_run_id, parent_span_id | uuid or null | |
timestamp | ISO date-time | Within a 15-minute clock-skew allowance. |
customer_id | string | Max 255 chars, [A-Za-z0-9_-]. Opaque - never an email. |
step_name | string or null | Max 200 chars. |
provider, model | string or null | |
tokens_in, tokens_out, latency_ms | integer >= 0 | |
tool_name | string or null | Max 200 chars. |
status | enum | success, failure, retry, aborted. |
framework | enum | langgraph, crewai, mastra, openai-agents, pydantic-ai, none. |
instrumentation_tier | enum | sdk_wrapper (auto-captured) or reported (manual). |
cost_source | enum | auto or configured. |
metric, metric_value | string/number or null | Non-LLM usage metrics. |
stream_aborted | boolean | |
abort_savings_usd | number | |
sdk_version | string | |
metadata | object or null | Max 4KB. Optional. |
Response
{
"accepted": 1,
"rejected": 0
}
errors[] of a 200 response:
{
"accepted": 0,
"rejected": 1,
"errors": [
{ "index": 0, "message": "timestamp is beyond the clock-skew allowance" }
]
}
warnings entries of { "event_index": 0, "code": "needs_pricing_input" } (codes: needs_pricing_input, pending_pricing, customer_limit_reached) and budget_exceeded flags when a budget rule fired.
Notes
- Cost is calculated server-side.
- Do not send
cost_usd; Pylva prices the event after ingest. - Duplicate span IDs in a batch - or already ingested recently - are dropped silently and counted in neither
acceptednorrejected, so retrying a batch after a network failure is safe. - Events with unknown pricing can be accepted and surfaced as pricing setup tasks.
- Do not include prompts, completions, raw messages, emails, or API keys in event metadata.