Skip to main content
GitHub

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

APIBase URLWith an API key during the beta
Gateway APIhttps://ingest.risicare.ai/v1Available. SDK span ingestion (you rarely call this directly) — see Gateway Endpoints
Scoreshttps://api.risicare.ai/v1/scoresAvailable, 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:

FieldTypeRequiredDefaultDescription
trace_idstringYes—Trace to annotate: 32 lowercase hex characters. Any other value returns 422
span_idstringNonullSpecific span (optional): 16 lowercase hex characters
namestringNo"default"Score name/label
scorefloatYes—Score value between 0.0 and 1.0. Out-of-range values return 422
commentstringNonullHuman-readable comment
sourcestringNo"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>" }
  ]
}
FieldDescription
acceptedNumber of spans that the gateway accepted
rejectedNumber of spans that the gateway rejected
errorsOne 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
ignoredPresent 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.

StatusCodeMeaning
400VALIDATION_ERROR, SERIALIZATION_ERROR, INVALID_TRACE_ID, INVALID_SPAN_IDThe request is not valid
401UNAUTHORIZEDThe Authorization: Bearer header is missing or malformed, or the key is not valid
413PAYLOAD_TOO_LARGEThe Content-Length of the request is over the size limit
413DECOMPRESSED_PAYLOAD_TOO_LARGEOn /v1/spans and /v1/spans/batch: the body is over the size limit after gzip decompression
415UNSUPPORTED_CONTENT_ENCODINGOn /v1/spans and /v1/spans/batch: the Content-Encoding is not gzip or identity
429RATE_LIMIT_EXCEEDEDThe request is over a rate limit. The response has a Retry-After header
503TOO_MANY_SPANS, TOO_MANY_ATTRIBUTESThe request is over a per-request cap. The gateway accepts none of its spans
503ENQUEUE_UNCONFIRMEDThe gateway could not confirm that the spans are in its queue
503BUFFER_FULL, REDIS_MEMORY_HIGHThe gateway is at capacity. The response has a Retry-After header
503QUEUE_NEAR_CAPACITYThe ingest queue of the project is at capacity
503INFLIGHT_BYTES_EXHAUSTED, QUEUE_DEPTH_UNKNOWN, ARCHIVE_DEGRADED_NO_HEADROOM, AUTH_UNAVAILABLE, STORAGE_ERRORA part of the gateway is at capacity or temporarily unavailable
500INTERNAL_ERRORThe gateway could not complete the request

Three answers do not have this body:

  • The OTLP route (/v1/otlp/v1/traces, and /v1/traces with an OTLP body or a Content-Encoding) answers a body that it cannot decode or parse with 400 or 413 and an OTLP body: the reason is in partialSuccess.errorMessage.
  • A request body over the size limit that has no Content-Length header gets a 413 without the error object.
  • A rate limit at the network edge can answer 429 without the error object and without a Retry-After header.

Gateway Limits

LimitValueAnswer when a request is over the limit
Spans in one request10,000503 TOO_MANY_SPANS
Attribute pairs in one request (the total over all its spans)24,000503 TOO_MANY_ATTRIBUTES
Request body10 MB, before and after gzip decompression413 (see Gateway Errors)
Request rateLimited per project and per client IP address429. 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 /health

Basic health check. It answers a fixed healthy status and checks nothing. No health route needs authentication.

Readiness Check

GET /health/ready

Returns 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/live

Returns 200 if the server process is running.

Ingest Readiness Check

GET /health/ingest

Returns 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": {}
  }
}
CodeHTTP StatusDescription
UNAUTHORIZED401Missing or invalid API key
AUTHORIZATION_FAILED403The key's role is not allowed
VALIDATION_ERROR422The request body is invalid
INVALID_CONTENT_LENGTH400The Content-Length header is not an integer
REQUEST_TOO_LARGE413The request body is over 10 MB
INTERNAL_ERROR500The 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.

Next Steps