Ingestion API¶
Emit AI inference events to Venturi with a single HTTP POST. Use this when your
code or pipeline already has a point that observes each AI call.
Base URL
Examples use the placeholder https://<your-venturi-instance>. On the
SaaS tier, substitute https://api.venturi.systems; self-hosted,
substitute your own instance host. See
Deployment modes. All paths below are
relative to that base.
Authentication¶
Send either header:
Venturi issues the credential during tenant provisioning. Health endpoints are the only unauthenticated routes.
POST /api/v1/invocations¶
Record one AI inference as an InvocationEvent. Returns 202 Accepted: the
event is queued for the attribution pipeline.
Minimal request¶
These fields are required by the controlled schema:
curl -X POST https://<your-venturi-instance>/api/v1/invocations \
-H "Content-Type: application/json" \
-H "X-API-Key: $VENTURI_API_KEY" \
-H "Idempotency-Key: 1d10d1ba-0407-4f3a-a5ee-1a00f40a6bf2" \
-d '{
"event_id": "8f3c2ac0-4dd7-49b3-b004-4f7b7d9c73b0",
"ingestion_layer": "application",
"ingestion_timestamp": "2026-05-29T18:30:00Z",
"provider": "openai",
"requested_model": "gpt-4o"
}'
Recommended request¶
Add the fields that make attribution useful (tokens, cost, and the identity or service responsible):
curl -X POST https://<your-venturi-instance>/api/v1/invocations \
-H "Content-Type: application/json" \
-H "X-API-Key: $VENTURI_API_KEY" \
-H "Idempotency-Key: 9e77162e-a528-4bd9-93ac-4ac4e3655897" \
-d '{
"event_id": "8f3c2ac0-4dd7-49b3-b004-4f7b7d9c73b0",
"ingestion_layer": "application",
"ingestion_timestamp": "2026-05-29T18:30:00Z",
"provider": "openai",
"requested_model": "gpt-4o",
"input_tokens": 1200,
"output_tokens": 350,
"estimated_cost_usd": "0.0123",
"latency_ms": 840,
"attributed_identity": "alice@venturi.systems",
"attributed_service": "checkout-api",
"trace_id": "4c77b7f064ab4d9bb68f163a9f0b31ce"
}'
Never send message content
The schema has no field for prompt or completion text, by design. Send metadata only: tokens, model, cost, identity, latency. Venturi’s pipeline does not store content.
Key fields¶
| Field | Required | Notes |
|---|---|---|
event_id |
✅ | Unique per event; used for dedup. |
ingestion_layer |
✅ | Capture layer, e.g. application, gateway, proxy. |
ingestion_timestamp |
✅ | RFC 3339 / ISO 8601 UTC. |
provider |
✅ | openai, anthropic, bedrock, … |
requested_model |
✅ | The model you asked for. |
input_tokens / output_tokens |
Optional | Drives token attribution. |
estimated_cost_usd |
Optional | Decimal string to avoid float drift. |
attributed_identity / attributed_service |
Optional | Who/what made the call. |
trace_id |
Optional | Correlate multi-step agent workflows. |
The full field set (cost breakdown, energy/carbon, Kubernetes and SPIFFE
identity, workflow correlation) is defined in the controlled schema:
the versioned event schema (schemas/invocationevent/v1.schema.json in the platform repository, shared during onboarding).
attribution_confidence, where present, is capped at 0.95; Venturi never
claims certainty.
Idempotency¶
Every mutating request carries a client-generated UUID or ULID in
Idempotency-Key. Venturi keeps the tenant-and-route-scoped key-to-result mapping
for 24 hours. Replaying the same key with the same body returns the original
status and result with Idempotency-Replayed: true; replaying it with a different
body returns 422 Unprocessable Entity. event_id independently deduplicates the
normalized invocation, so keep both values stable when retrying the same event.
POST /api/v1/events¶
For generic observability events that aren’t a single model invocation. Same auth,
also returns 202 Accepted.
Responses¶
| Status | Meaning |
|---|---|
202 Accepted |
Event queued. |
401 Unauthorized |
Missing/invalid credential. |
422 Unprocessable Entity |
Payload failed schema validation: check required fields and estimated_cost_usd is a string. |
429 Too Many Requests |
Rate limit exceeded. Honor Retry-After and retry with jitter while preserving both identifiers. |
All error responses use Content-Type: application/problem+json per RFC 9457.
For example:
{
"type": "https://docs.venturi.systems/problems/rate-limited",
"title": "Too Many Requests",
"status": 429,
"detail": "The tenant ingestion rate limit was exceeded.",
"instance": "/api/v1/invocations",
"trace_id": "4c77b7f064ab4d9bb68f163a9f0b31ce",
"tenant_id": "tenant_01J4Z8PNH3X5ZT2V5K4R9EC1QF",
"error_code": "RATE_LIMITED",
"docs_url": "https://docs.venturi.systems/ingestion/api/"
}
A 429 also carries Retry-After, RateLimit-Limit,
RateLimit-Remaining, and RateLimit-Reset headers.
Health endpoints¶
GET /healthz is an unauthenticated liveness probe. A 200 confirms that the
process is running; it does not prove dependencies or ingestion readiness.
GET /readyz is intended for deployment health checks and reports whether the
required dependencies are ready. Neither endpoint returns tenant data.
Verifying it worked¶
After a successful 202, the event appears in the Venturi dashboard’s
Attribution / Recent view within ~30 seconds. See
Verify & confirm.