{ "openapi": "3.1.0", "info": { "title": "Foglamp Ingest API", "version": "v1", "description": "Write path for Foglamp. Receives batches of traces from the SDK, prices each span, and persists them to ClickHouse." }, "servers": [ { "url": "http://localhost:4000", "description": "Self-hosted (default)" }, { "url": "https://ingest.foglamp.dev", "description": "Hosted" } ], "security": [ { "bearerAuth": [] }, { "apiKeyAuth": [] } ], "paths": { "/ingest": { "post": { "summary": "Ingest a batch of traces", "description": "Validates the payload, resolves the API key, prices each span, and buffers the rows for a flush to ClickHouse. Fire-and-forget: a 202 means the batch was accepted, not yet durably written.", "operationId": "ingestTraces", "tags": ["Ingest"], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/IngestPayload" } } } }, "responses": { "202": { "description": "Batch accepted into the write buffer.", "content": { "application/json": { "schema": { "type": "object", "required": ["accepted"], "properties": { "accepted": { "type": "integer", "description": "Number of span rows accepted.", "example": 3 } } } } } }, "400": { "description": "Invalid JSON body, or payload failed schema validation.", "content": { "application/json": { "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string", "example": "invalid payload" }, "issues": { "type": "array", "description": "Validation issues, present when the payload failed schema validation.", "items": { "type": "object", "additionalProperties": true } } } } } } }, "401": { "description": "Missing, invalid, or revoked API key.", "content": { "application/json": { "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string", "example": "invalid or revoked API key" } } } } } }, "413": { "description": "Request body exceeds INGEST_MAX_BODY_BYTES (default 10 MiB). Rejected before the body is parsed.", "content": { "application/json": { "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string", "example": "payload too large" } } } } } }, "429": { "description": "Too Many Requests — returned in two distinct cases. (1) Per-key rate limit: includes a `Retry-After` header; back off and retry after it. (2) Plan quota exceeded: a billing condition with no `Retry-After`; retrying won't succeed until the plan is upgraded or the quota resets.", "headers": { "Retry-After": { "description": "Seconds to wait before retrying. Present only on the rate-limit variant, not the quota variant.", "required": false, "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" } } }, "examples": { "rateLimit": { "summary": "Per-key rate limit (carries Retry-After)", "value": { "error": "rate limit exceeded" } }, "quota": { "summary": "Plan quota exceeded (no Retry-After)", "value": { "error": "Monthly span quota exceeded — upgrade your plan to keep ingesting." } } } } } }, "503": { "description": "Server is shutting down and is no longer accepting new batches (graceful drain in progress). Retry against another replica or after restart.", "content": { "application/json": { "schema": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string", "example": "shutting down" } } } } } } } } }, "/health": { "get": { "summary": "Health check", "description": "Liveness probe. Returns the current size of the in-memory write buffer.", "operationId": "health", "tags": ["Ingest"], "security": [], "responses": { "200": { "description": "Service is up.", "content": { "application/json": { "schema": { "type": "object", "required": ["status", "buffered"], "properties": { "status": { "type": "string", "example": "ok" }, "buffered": { "type": "integer", "description": "Rows currently held in the write buffer.", "example": 0 } } } } } } } } } }, "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Send your API key as `Authorization: Bearer fl_…`." }, "apiKeyAuth": { "type": "apiKey", "in": "header", "name": "x-api-key", "description": "Send your API key in the `x-api-key` header." } }, "schemas": { "IngestPayload": { "type": "object", "description": "A versioned batch of traces.", "required": ["version", "traces"], "additionalProperties": false, "properties": { "version": { "type": "string", "const": "v1", "description": "Wire contract version.", "example": "v1" }, "traces": { "type": "array", "minItems": 1, "maxItems": 1000, "items": { "$ref": "#/components/schemas/Trace" } } } }, "Trace": { "type": "object", "description": "One top-level model call (e.g. a single generateText/streamText) and its spans. Validation rules (rejected with 400 otherwise): (1) at least one of `traceName` or `agentName` must be present; (2) `workflowName` and `workflowRunId` must be provided together (both or neither).", "required": ["traceId", "spans"], "additionalProperties": false, "properties": { "traceId": { "type": "string", "minLength": 1, "maxLength": 128, "example": "0192f1c0-1a2b-7c3d-8e4f-1a2b3c4d5e6f" }, "traceName": { "type": "string", "maxLength": 256, "example": "classify-email" }, "agentName": { "type": "string", "maxLength": 256, "example": "summarizer" }, "workflowName": { "type": "string", "maxLength": 256, "example": "deploy-digest" }, "workflowRunId": { "type": "string", "maxLength": 128, "example": "run_2024_05_24" }, "sessionId": { "type": "string", "maxLength": 128, "example": "session_abc123" }, "customer": { "type": "object", "description": "The end-customer this call serves, for per-customer cost attribution. Only `id` is required; `name`/`imageUrl` are display-only.", "required": ["id"], "additionalProperties": false, "properties": { "id": { "type": "string", "minLength": 1, "maxLength": 128, "example": "cus_acme" }, "name": { "type": "string", "maxLength": 256, "example": "Acme Inc" }, "imageUrl": { "type": "string", "format": "uri", "maxLength": 2048, "example": "https://logo.clearbit.com/acme.com" } } }, "metadata": { "$ref": "#/components/schemas/Metadata" }, "spans": { "type": "array", "minItems": 1, "maxItems": 2000, "items": { "$ref": "#/components/schemas/Span" } } } }, "Span": { "type": "object", "description": "A unit of work inside a trace. endTime must be >= startTime.", "required": ["spanId", "spanType", "name", "startTime", "endTime"], "additionalProperties": false, "properties": { "spanId": { "type": "string", "minLength": 1, "maxLength": 128, "example": "span_1" }, "parentSpanId": { "type": "string", "minLength": 1, "maxLength": 128, "example": "span_0" }, "spanType": { "type": "string", "enum": ["agent", "llm", "tool", "embedding", "other"], "example": "llm" }, "name": { "type": "string", "maxLength": 512, "example": "generateText" }, "startTime": { "type": "integer", "minimum": 0, "description": "Epoch milliseconds.", "example": 1716566400000 }, "endTime": { "type": "integer", "minimum": 0, "description": "Epoch milliseconds; must be >= startTime.", "example": 1716566401200 }, "status": { "type": "string", "enum": ["ok", "error"], "default": "ok", "example": "ok" }, "errorMessage": { "type": "string", "maxLength": 8192 }, "provider": { "type": "string", "maxLength": 128, "example": "openai" }, "modelId": { "type": "string", "maxLength": 256, "example": "gpt-4o" }, "usage": { "$ref": "#/components/schemas/Usage" }, "ttftMs": { "type": "number", "minimum": 0, "description": "Time to first token, milliseconds. May be fractional.", "example": 320 }, "chunkOffsets": { "type": "array", "maxItems": 200, "items": { "type": "integer", "minimum": 0 }, "description": "Streaming LLM spans only: ms from step start for each sampled output chunk. Parallel to chunkTokens." }, "chunkTokens": { "type": "array", "maxItems": 200, "items": { "type": "integer", "minimum": 0 }, "description": "Streaming LLM spans only: cumulative output tokens at each chunkOffsets sample." }, "reasoningOffsets": { "type": "array", "maxItems": 200, "items": { "type": "integer", "minimum": 0 }, "description": "Streaming reasoning models only: ms from step start for each sampled reasoning chunk. Parallel to reasoningChunkTokens." }, "reasoningChunkTokens": { "type": "array", "maxItems": 200, "items": { "type": "integer", "minimum": 0 }, "description": "Streaming reasoning models only: cumulative reasoning tokens at each reasoningOffsets sample." }, "reasoningDurationMs": { "type": "integer", "minimum": 0, "description": "Total wall-clock ms spent inside reasoning blocks for this step." }, "input": { "type": "string", "maxLength": 1000000, "description": "Prompt/input text. Omitted when recordInputs is disabled." }, "output": { "type": "string", "maxLength": 1000000, "description": "Completion/output text. Omitted when recordOutputs is disabled." }, "toolCatalog": { "type": "string", "maxLength": 1000000, "description": "JSON catalog of tools offered for the call (name → {description, JSON-Schema params}). Stamped on llm and agent spans only." }, "modelCallMs": { "type": "integer", "minimum": 0, "description": "Pure model-call wall-clock for the step (ms), excluding client-side tool execution. v7 only; absent on v4–v6 wrap and non-model spans." }, "systemFingerprint": { "type": "string", "maxLength": 256, "description": "Provider model-build fingerprint (e.g. OpenAI system_fingerprint)." }, "safetyMetadata": { "type": "string", "maxLength": 16384, "description": "JSON blob of provider safety ratings, as reported. Absent when the provider reports none." }, "sources": { "type": "string", "maxLength": 1000000, "description": "JSON array of RAG/grounding citations (StepResult.sources). Recorded only when output capture is on." }, "rateLimit": { "type": "object", "additionalProperties": false, "description": "Rate-limit headroom, normalized cross-provider from response headers (OpenAI x-ratelimit-*, Anthropic anthropic-ratelimit-*). Any subset may be present.", "properties": { "requestsLimit": { "type": "integer", "minimum": 0 }, "requestsRemaining": { "type": "integer", "minimum": 0 }, "requestsResetMs": { "type": "integer", "minimum": 0 }, "tokensLimit": { "type": "integer", "minimum": 0 }, "tokensRemaining": { "type": "integer", "minimum": 0 }, "tokensResetMs": { "type": "integer", "minimum": 0 } } }, "metadata": { "$ref": "#/components/schemas/Metadata" } } }, "Usage": { "type": "object", "description": "Token and request counts. Each dimension is priced independently at ingest.", "additionalProperties": false, "properties": { "inputTokens": { "type": "integer", "minimum": 0, "example": 1200 }, "outputTokens": { "type": "integer", "minimum": 0, "example": 350 }, "totalTokens": { "type": "integer", "minimum": 0, "example": 1550 }, "reasoningTokens": { "type": "integer", "minimum": 0 }, "cachedInputTokens": { "type": "integer", "minimum": 0 }, "cacheWriteInputTokens": { "type": "integer", "minimum": 0 }, "imageCount": { "type": "integer", "minimum": 0 }, "webSearchCount": { "type": "integer", "minimum": 0 }, "requestCount": { "type": "integer", "minimum": 0 } } }, "Metadata": { "type": "object", "description": "Free-form string-to-string labels. At most 64 entries; keys up to 128 chars, values up to 1024 chars.", "maxProperties": 64, "propertyNames": { "maxLength": 128 }, "additionalProperties": { "type": "string", "maxLength": 1024 }, "example": { "environment": "production", "region": "us-east-1" } } } } }