REST API
REST API reference for the ingest gateway and the scores route.
Reference for the Risicare REST API.
What an API key can do over HTTP during the beta
During the beta an API key can do two things over HTTP: send spans to the ingest gateway, and record a score. The Management API is not available with an API key during the beta. For everything else, use the dashboard.
Base URLs
| API | Base URL | With an API key during the beta |
|---|---|---|
| Gateway API | https://ingest.risicare.ai/v1 | Available. SDK span ingestion (you rarely call this directly) — see Gateway Endpoints |
| Scores | https://api.risicare.ai/v1/scores | Available, POST only — see Scores |
| Management API | — | Not available. Use the dashboard |
https://app.risicare.ai is the dashboard. It accepts a browser sign-in only: a request with an API key to https://app.risicare.ai/api/v1/... gets 401 (UNAUTHORIZED, "Authentication required").
The sections below keep the names of the Management API areas. Each section says what you can do today.
Authentication
Send the API key in the Authorization header:
# Canonical (used by the SDKs)
curl -H "Authorization: Bearer rsk-..."The ingest gateway reads only the Authorization header. A request to a
Gateway endpoint that carries the key only in X-API-Key gets
401 (Missing Authorization header). The value must use the Bearer scheme.
The scores route also accepts the key in X-API-Key:
# Alternative on the scores route (for legacy / non-RFC-7235 clients)
curl -H "X-API-Key: rsk-..."Header precedence on the scores route: Authorization is consulted first. If an Authorization header is present at all, X-API-Key is ignored — even when the Authorization value is malformed (missing the Bearer prefix). A malformed Authorization header therefore returns 401 even if a valid X-API-Key is also present. Send only one of the two headers; for new integrations prefer Authorization: Bearer rsk-....
A key acts with the role that its creator holds in the organization.
Key management is dashboard-only
No route creates, lists or revokes API keys for a request that carries an API key. Use Settings → API Keys in the dashboard. See API Keys below.
Traces
Not available with an API key during the beta. Read traces in the dashboard: the Traces page lists them, and a trace page shows the span tree. You can export the list as CSV or JSON (at most 100 traces).
No route deletes a trace. Deleting a trace is done by the Risicare team on request during the beta — see Data Management.
Sessions
Not available with an API key during the beta. Read sessions on the Sessions page of the dashboard.
No route deletes a session. Deleting a session is done by the Risicare team on request during the beta — see Data Management.
Agents
Not available with an API key during the beta. Read agents on the Agents page of the dashboard.
Diagnoses
Held for the beta. A diagnosis cannot be requested, and there is no way to list or read diagnoses with an API key. The dashboard does not show diagnoses while diagnosis is held. See Diagnose.
Fixes
Held for the beta. No new fix is generated while diagnosis is held, and fix deployment is gated off. There is no way to list, create, change or promote a fix with an API key, and the dashboard has no control for it. See Heal.
The SDK fix runtime reads GET /v1/fixes/active on the API host. That route is not reachable with an API key during the beta, and the fix runtime is off by default. See Fix Runtime.
Deployments
Gated off in this release. A deployment cannot be created, listed or rolled back with an API key, and the dashboard has no deployment pages. See Deploy.
Projects
Not available with an API key during the beta. In the dashboard you can rename a project on its Settings page. A customer cannot change the other project settings during the beta — see Projects.
API Keys
No route manages API keys for a request that carries an API key. Create, list and revoke keys in the dashboard under Settings → API Keys. Owners and admins can create and revoke keys.
Evaluations
Held for the beta. An evaluation cannot be started, and there is no way to list or read evaluations with an API key. The dashboard's Evaluations page shows only a notice that evaluations are not part of this release. See Evaluations.
Datasets
Not available with an API key during the beta. Create, edit and delete datasets on the Datasets page of the dashboard.
Alerts
Not available with an API key during the beta. The Alerts page of the dashboard lists alert rules and their history, and it can pause or delete a rule. It has no control that creates a rule, and the Management API is not available with an API key during the beta.
Alert evaluation is turned off during the beta: no rule fires, and no alert.triggered event is sent.
Scores
Create Score
POST /v1/scores on https://api.risicare.ai is the one API route that accepts an API key during the beta. The SDKs' score() call sends this request.
POST https://api.risicare.ai/v1/scores
Content-Type: application/json
Authorization: Bearer rsk-...
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"name": "accuracy",
"score": 0.95,
"comment": "Response matched expected output",
"source": "api"
}Request body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
trace_id | string | Yes | — | Trace to annotate: 32 lowercase hex characters. Any other value returns 422 |
span_id | string | No | null | Specific span (optional): 16 lowercase hex characters |
name | string | No | "default" | Score name/label |
score | float | Yes | — | Score value between 0.0 and 1.0. Out-of-range values return 422 |
comment | string | No | null | Human-readable comment |
source | string | No | "api" | One of api, sdk, ui, eval |
Returns 201 with the created score object. Requires the member role (or higher) — a viewer-role key receives 403.
A limit per client IP address applies to this route at the network edge. A request over it gets 429 without a Retry-After header and without the error object.
List Scores
Not available with an API key during the beta. The dashboard shows the scores of a trace on the trace page.
Webhooks
Not available with an API key during the beta. The Webhooks page of the dashboard lists webhooks. It has no control that creates, changes or deletes a webhook, and the Management API is not available with an API key during the beta. Webhook delivery is turned off during the beta.
The receiver helpers are in the SDKs: Python verify_webhook_signature (risicare>=0.1.12) and JavaScript verifyWebhookSignature (risicare@0.4.0).
Agent Topology
Not available with an API key during the beta. The Agents page of the dashboard shows the agent topology.
Data Management
No route erases data for a request that carries an API key. During the beta, erasure is handled by the Risicare team on request: this covers a data subject's data, a trace and a session. See Data Management.
Gateway Endpoints
SDK-facing endpoints for span ingestion. The base URL is https://ingest.risicare.ai. The gateway serves exactly these ingestion routes: /v1/spans, /v1/spans/batch, /v1/traces and /v1/otlp/v1/traces.
The gateway reads the API key only from Authorization: Bearer rsk-... — see Authentication. The responses, error codes and limits of these routes are under Gateway Responses.
Field names in these request bodies are camelCase. Each span needs a traceId (32 hex characters), a spanId (16 hex characters), a name and a startTime (ISO 8601). parentSpanId, kind, endTime, status, attributes and the other span fields are optional.
Spans
POST /v1/spans
Content-Type: application/json
Authorization: Bearer rsk-...
{
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "llm.call",
"startTime": "2024-01-15T10:00:00Z",
"endTime": "2024-01-15T10:00:01Z",
"attributes": {...}
}
]
}The body is always a spans array, even for a single span. Both published SDKs send spans to this endpoint.
Sending the same span again is safe: a second send with the same content does not make a second span. Do not send a span again to correct it. For about 20 minutes after the first send, a second send with different content is refused after the gateway has answered 200. After that time, a changed span replaces the first one when the two start times are on the same calendar day in storage. When the start times are on different days, the two are stored as two spans.
Batch Spans
POST /v1/spans/batch
Content-Type: application/x-ndjson
Authorization: Bearer rsk-...
{"traceId": "4bf92f3577b34da6a3ce929d0e0e4736", "spanId": "00f067aa0ba902b7", "name": "llm.call", "startTime": "2024-01-15T10:00:00Z"}
{"traceId": "4bf92f3577b34da6a3ce929d0e0e4736", "spanId": "b7ad6b7169203331", "name": "tool.call", "startTime": "2024-01-15T10:00:01Z"}The body is newline-delimited JSON: one span object per line, with no wrapping array.
Trace Ingestion
POST /v1/traces
Content-Type: application/json
Authorization: Bearer rsk-...
{
"traces": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spans": [...]
}
]
}Each trace holds its spans and may carry a sessionId. This endpoint also accepts an OTLP/HTTP JSON body (a top-level resourceSpans). Neither published SDK uses it.
OTLP Ingestion
The OTLP endpoint accepts OTLP/JSON only. The gateway parses the body with serde_json — binary protobuf (application/x-protobuf) is not supported. Configure your OTel exporter for the JSON protocol (e.g. OTEL_EXPORTER_OTLP_PROTOCOL=http/json).
POST /v1/otlp/v1/traces
Content-Type: application/json
Authorization: Bearer rsk-...
{
"resourceSpans": [
{
"resource": {
"attributes": [
{"key": "service.name", "value": {"stringValue": "my-agent"}}
]
},
"scopeSpans": [
{
"spans": [
{
"traceId": "5b8efff798038103d269b633813fc60c",
"spanId": "eee19b7ec3c1b174",
"name": "llm.call",
"startTimeUnixNano": "1544712660000000000",
"endTimeUnixNano": "1544712661000000000"
}
]
}
]
}
]
}Gzip request bodies are supported via Content-Encoding: gzip (the decompressed payload must still be OTLP/JSON). The double /v1/ is intentional — gateway prefix + OTLP protocol path.
Gateway Responses
POST /v1/spans and POST /v1/spans/batch answer 200 with this body:
{
"accepted": 2,
"rejected": 1,
"errors": [
{ "index": 2, "error": "<reason>" }
]
}| Field | Description |
|---|---|
accepted | Number of spans that the gateway accepted |
rejected | Number of spans that the gateway rejected |
errors | One entry for each rejected span: index and error (the reason). On /v1/spans, index is the position of the span in the spans array. On /v1/spans/batch, index can differ from the line number of the span when the request has blank lines or lines that do not parse. Absent when no span is rejected |
ignored | Present only when the request has fields that the gateway does not model. The gateway discards those fields. count is the number of distinct field paths, and fields lists up to 10 of them |
With the native traces body, POST /v1/traces answers 200 with tracesAccepted, spansAccepted and spansRejected, and with errors (traceId and spanErrors) when spans are rejected. POST /v1/otlp/v1/traces answers with an OTLP body: partialSuccess (rejectedSpans, errorMessage) reports rejected spans. POST /v1/traces answers the same way when the request has an OTLP body or a Content-Encoding.
A 200 means that the accepted spans are in the gateway's queue. It does not mean that they are stored. The gateway sends the 200 after its queue confirms the spans. If the queue does not confirm them in time, the gateway answers 503 ENQUEUE_UNCONFIRMED. Storage comes after the queue, so a 200 does not prove that a span is stored or visible in the dashboard.
A 200 can report rejected spans. A request in which the gateway rejects some spans, or all of them, still answers 200. Read rejected and errors.
Both published SDKs read this body. For them, a 2xx response without accepted and rejected counts is a failure — see Production & Failure Modes.
Gateway Errors
An error response from the gateway has the body below. There are three exceptions, listed after the table.
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"retry_after": 1
}
}retry_after (seconds) is present only on RATE_LIMIT_EXCEEDED, BUFFER_FULL and REDIS_MEMORY_HIGH. Those responses also carry a Retry-After header with the same value.
| Status | Code | Meaning |
|---|---|---|
400 | VALIDATION_ERROR, SERIALIZATION_ERROR, INVALID_TRACE_ID, INVALID_SPAN_ID | The request is not valid |
401 | UNAUTHORIZED | The Authorization: Bearer header is missing or malformed, or the key is not valid |
413 | PAYLOAD_TOO_LARGE | The Content-Length of the request is over the size limit |
413 | DECOMPRESSED_PAYLOAD_TOO_LARGE | On /v1/spans and /v1/spans/batch: the body is over the size limit after gzip decompression |
415 | UNSUPPORTED_CONTENT_ENCODING | On /v1/spans and /v1/spans/batch: the Content-Encoding is not gzip or identity |
429 | RATE_LIMIT_EXCEEDED | The request is over a rate limit. The response has a Retry-After header |
503 | TOO_MANY_SPANS, TOO_MANY_ATTRIBUTES | The request is over a per-request cap. The gateway accepts none of its spans |
503 | ENQUEUE_UNCONFIRMED | The gateway could not confirm that the spans are in its queue |
503 | BUFFER_FULL, REDIS_MEMORY_HIGH | The gateway is at capacity. The response has a Retry-After header |
503 | QUEUE_NEAR_CAPACITY | The ingest queue of the project is at capacity |
503 | INFLIGHT_BYTES_EXHAUSTED, QUEUE_DEPTH_UNKNOWN, ARCHIVE_DEGRADED_NO_HEADROOM, AUTH_UNAVAILABLE, STORAGE_ERROR | A part of the gateway is at capacity or temporarily unavailable |
500 | INTERNAL_ERROR | The gateway could not complete the request |
Three answers do not have this body:
- The OTLP route (
/v1/otlp/v1/traces, and/v1/traceswith an OTLP body or aContent-Encoding) answers a body that it cannot decode or parse with400or413and an OTLP body: the reason is inpartialSuccess.errorMessage. - A request body over the size limit that has no
Content-Lengthheader gets a413without theerrorobject. - A rate limit at the network edge can answer
429without theerrorobject and without aRetry-Afterheader.
Gateway Limits
| Limit | Value | Answer when a request is over the limit |
|---|---|---|
| Spans in one request | 10,000 | 503 TOO_MANY_SPANS |
| Attribute pairs in one request (the total over all its spans) | 24,000 | 503 TOO_MANY_ATTRIBUTES |
| Request body | 10 MB, before and after gzip decompression | 413 (see Gateway Errors) |
| Request rate | Limited per project and per client IP address | 429. The gateway's own limit answers RATE_LIMIT_EXCEEDED with Retry-After |
A request over the span cap or the attribute cap gets 503, not 413. The same request gets the same answer each time, so send fewer spans in each request.
The gateway accepts a gzip request body (Content-Encoding: gzip) on /v1/spans, /v1/spans/batch and /v1/otlp/v1/traces, and on /v1/traces for an OTLP body.
Health Check
GET /healthBasic health check. It answers a fixed healthy status and checks nothing. No health route needs authentication.
Readiness Check
GET /health/readyReturns 200 only when the gateway's dependencies are reachable: Redis, and ClickHouse and PostgreSQL when they are configured. Otherwise it returns 503.
Liveness Check
GET /health/liveReturns 200 if the server process is running.
Ingest Readiness Check
GET /health/ingestReturns 200 when the gateway can reach Redis, and 503 when it cannot.
Error Responses
The ingest gateway has its own error body and its own codes — see Gateway Errors.
The scores route answers an error with this body:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "...",
"details": {}
}
}| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid API key |
AUTHORIZATION_FAILED | 403 | The key's role is not allowed |
VALIDATION_ERROR | 422 | The request body is invalid |
INVALID_CONTENT_LENGTH | 400 | The Content-Length header is not an integer |
REQUEST_TOO_LARGE | 413 | The request body is over 10 MB |
INTERNAL_ERROR | 500 | The API could not complete the request |
A limit at the network edge can answer 429 without this body — see Scores.
A request with an API key to https://app.risicare.ai/api/v1/... gets 401 with the code UNAUTHORIZED and the message "Authentication required", whatever the route.
Rate Limits
The ingest gateway limits requests per project and per client IP address. A request over the gateway's own limit gets 429 RATE_LIMIT_EXCEEDED with a Retry-After header; a limit at the network edge can answer 429 without that header. See Gateway Limits. The scores route has a limit per client IP address at the network edge.