Telemetry ingest API
The telemetry ingest API accepts telemetry_event records. Most customers do
not call this directly — Claresia’s Telemetry Pipeline
pulls from the LLM platform’s audit log automatically.
You do call this directly if:
- You are running Mode C BYOC and the customer-side redaction Lambda / Function App emits envelopes
- You have a custom LLM platform not yet covered by a built-in pull connector
- You are emitting synthetic test telemetry for load testing
Endpoint
Section titled “Endpoint”POST /api/v1/telemetry/eventsAuthorization: Bearer <jwt>X-Claresia-Tenant: daineseContent-Type: application/jsonSingle event
Section titled “Single event”POST /api/v1/telemetry/events
{ "skill_id": "gatespic.incident-postmortem", "skill_version": "1.4.2", "archetype_id": "firmware_engineer", "user_id": "user:marco.mazzolin@dainese.it", "llm_platform": "anthropic-claude-enterprise", "llm_model": "claude-sonnet-4.7", "ts": "2026-05-03T14:22:18.500000Z", "success": true, "latency_ms": 2317, "tokens_in": 4823, "tokens_out": 1421, "cost_usd_estimate": 0.041, "error_code": null}Response:
HTTP/1.1 201 Created{ "record_id": "01933a95-...", "provenance_hash": "h7Vw3K...", "provenance_cosign": "Mq4z8L..."}Batch events (recommended for throughput)
Section titled “Batch events (recommended for throughput)”POST /api/v1/telemetry/events:batch
{ "events": [ { "skill_id": "...", ... }, { "skill_id": "...", ... }, { "skill_id": "...", ... } ]}Response:
HTTP/1.1 201 Created{ "accepted": 3, "failed": 0, "records": [ { "record_id": "01933a95-...", "provenance_hash": "...", "provenance_cosign": "..." }, { "record_id": "01933a96-...", "provenance_hash": "...", "provenance_cosign": "..." }, { "record_id": "01933a97-...", "provenance_hash": "...", "provenance_cosign": "..." } ]}Max 1000 events per batch. Larger batches return 400 Bad Request.
Required fields
Section titled “Required fields”| Field | Type | Required | Notes |
|---|---|---|---|
skill_id | string | yes | Must reference a Skill IR registered in the tenant |
skill_version | string (semver) | yes | |
archetype_id | string | yes | Must exist in the cc-051 archetype bundle |
user_id | string | yes | Format user:<email> or user:<scim_external_id> |
llm_platform | enum | yes | anthropic-claude-enterprise, microsoft-copilot-m365, openai-chatgpt-enterprise, google-gemini-workspace, slack, custom:<name> |
llm_model | string | recommended | e.g., claude-sonnet-4.7, gpt-5, gemini-2.5-pro |
ts | RFC3339 | yes | Microsecond precision recommended |
success | boolean | yes | |
latency_ms | integer | yes | |
tokens_in | integer | yes | |
tokens_out | integer | yes | |
cost_usd_estimate | float | yes | Use 6-decimal precision |
error_code | string | null | yes if success=false |
Mode-specific behavior
Section titled “Mode-specific behavior”Mode A / B
Section titled “Mode A / B”The endpoint accepts the event, computes provenance, writes to the per-tenant ClickHouse cluster, and triggers downstream Command Center surface refresh.
Mode C
Section titled “Mode C”The customer-side redaction Lambda calls this endpoint with the envelope only
— no payload, no PII. Claresia accepts and stores the envelope; no
content-bearing fields are accepted in Mode C (they’re rejected with 400).
Idempotency
Section titled “Idempotency”Send Idempotency-Key: <unique-key> header:
POST /api/v1/telemetry/eventsIdempotency-Key: dainese:2026-05-03T14:22:18.5Z:user:marco:gatespic-incidentRepeat calls with the same key return the existing record (idempotent retry). Keys are scoped per tenant + 24h TTL.
Validation
Section titled “Validation”tokens_in + tokens_outmust equal the LLM platform’s reported total (within ±2% tolerance to allow for platform-specific rounding)cost_usd_estimateis estimated by the customer; Claresia computes its own estimate from(tokens_in × price_in + tokens_out × price_out)and stores both for reconciliationlatency_msmust be > 0 and < 600000 (10 minutes)
Sample customer-side connector (Python, Mode C)
Section titled “Sample customer-side connector (Python, Mode C)”import asyncioimport httpxfrom datetime import datetime, timezone
async def emit_envelope(claresia_endpoint: str, jwt: str, tenant: str, event: dict): async with httpx.AsyncClient() as client: # Strip payload-bearing fields (keep envelope only) envelope = { k: v for k, v in event.items() if k in {"skill_id", "skill_version", "archetype_id", "user_id", "llm_platform", "llm_model", "ts", "success", "latency_ms", "tokens_in", "tokens_out", "cost_usd_estimate", "error_code"} } idempotency_key = f"{tenant}:{envelope['ts']}:{envelope['user_id']}:{envelope['skill_id']}" r = await client.post( f"{claresia_endpoint}/api/v1/telemetry/events", json=envelope, headers={ "Authorization": f"Bearer {jwt}", "X-Claresia-Tenant": tenant, "Idempotency-Key": idempotency_key, }, ) r.raise_for_status() return r.json()Querying ingested telemetry
Section titled “Querying ingested telemetry”See Hub API — telemetry events are queryable as
record_type=telemetry_event records.
Errors
Section titled “Errors”| HTTP | Reason |
|---|---|
| 400 | Schema validation failed (see detail) |
| 401 | Bearer token invalid or expired |
| 403 | Service account lacks telemetry:write scope |
| 409 | Idempotency conflict (different payload, same key) |
| 413 | Batch too large (>1000 events) |
| 422 | Skill IR not registered for this tenant |
| 429 | Rate limit (see Retry-After) |
| 503 | Downstream Hub temporarily unavailable; retry with exponential backoff |