Skip to content

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:

HTTP
Authorization: Bearer <jwt>
HTTP
X-API-Key: <key>

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:

Bash
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"
  }'

Add the fields that make attribution useful (tokens, cost, and the identity or service responsible):

Bash
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:

JSON
{
  "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.

Prefer not to touch application code? Use the drop-in proxy