- Status: Reference
- Version: current
- Canonical for: public HTTP endpoints and response examples
- Audience: public
All endpoints are served by evidra-api (default :8080).
Authenticated endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <api-key>
Keys are issued via POST /v1/keys (see below) or set statically with the EVIDRA_API_KEY environment variable.
Liveness probe. Returns 200 OK with JSON body:
{"status":"ok"}Readiness probe. Returns 200 OK with JSON body {"status":"ok"} when the database connection is healthy, and 503 when it is not.
Returns the Ed25519 public key (PEM-encoded) when signing is configured.
Issue a new API key. Gated by an invite secret, not by standard Bearer auth.
Headers:
| Header | Required | Description |
|---|---|---|
X-Invite-Secret |
Yes | Must match the server's EVIDRA_INVITE_SECRET value |
Request body:
{ "label": "my-ci-pipeline" }label— optional, max 128 characters.
Response (201 Created):
{
"key": "ev1_abc123...",
"prefix": "ev1_abc1",
"tenant_id": "tnt_...",
"created_at": "2025-01-15T10:30:00Z"
}Rate limit: 3 keys per hour per IP.
Errors:
400— invalid JSON or label too long403— missing or invalid invite secret429— rate limit exceeded501— key management not available503— invite secret not configured on server
All ingestion endpoints require Bearer auth.
Forward a single evidence entry (raw JSON).
Request body: Any valid JSON evidence entry.
Response:
{ "receipt_id": "01JD...", "status": "accepted" }Ingest multiple entries in one request.
Request body:
{ "entries": [ { ... }, { ... } ] }Response:
{ "accepted": 5, "errors": [] }Typed external lifecycle ingest for prescribe entries. Use this when an external adapter wants Evidra to build and sign the final prescribe entry from normalized request fields instead of forwarding a raw entry blob.
The request carries:
contract_version- actor and correlation fields
- request taxonomy:
flavor,evidence.kind,source.system - either
canonical_actionorsmart_target - optional top-level
prescription_idandartifact_digest - optional
payload_overridewhen the caller already has a shaped prescribe payload body
Response (200 OK or 202 Accepted):
{
"entry_id": "01JD...",
"prescription_id": "presc_...",
"effective_risk": "medium",
"duplicate": false
}Typed external lifecycle ingest for report entries. Use this when an external adapter wants Evidra to resolve a prescription, validate the lifecycle terminal state, and build the canonical report entry server-side.
The request carries:
contract_version- actor and correlation fields
- request taxonomy:
flavor,evidence.kind,source.system prescription_idverdictexit_codefor non-declined typed reports ordecision_contextfor declined typed reports- optional top-level
artifact_digest - optional
payload_overridefor already-shaped report payload bodies
Response (200 OK or 202 Accepted):
{
"entry_id": "01JD...",
"effective_risk": "medium",
"duplicate": false
}Ingest SARIF findings as evidence entries.
All query endpoints require Bearer auth.
List evidence entries with pagination and optional filters.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 100 |
Page size (max 1000) |
offset |
integer | 0 |
Number of entries to skip |
type |
string | — | Filter by entry type (prescribe, report, finding, etc.) |
period |
string | — | Time window (7d, 30d, 90d) |
session_id |
string | — | Filter by session ID |
Response:
{
"entries": [
{
"id": "01JD1A2B3C",
"type": "prescribe",
"tool": "kubectl",
"operation": "apply",
"scope": "namespace",
"risk_level": "medium",
"actor": "alice",
"created_at": "2025-01-15T10:30:00Z"
},
{
"id": "01JD1A2B3D",
"type": "report",
"tool": "kubectl",
"operation": "apply",
"scope": "namespace",
"risk_level": "medium",
"actor": "alice",
"verdict": "success",
"exit_code": 0,
"created_at": "2025-01-15T10:30:05Z"
}
],
"total": 47,
"limit": 20,
"offset": 0
}Compatibility note: this query surface still returns a flat risk_level field.
For prescribe entries, it is derived from the stored effective_risk when present,
with fallback to legacy payloads.
Pagination example:
# First page (20 entries)
curl -H "Authorization: Bearer $KEY" \
"http://localhost:8080/v1/evidence/entries?limit=20&offset=0"
# Second page
curl -H "Authorization: Bearer $KEY" \
"http://localhost:8080/v1/evidence/entries?limit=20&offset=20"
# Filter by type and period
curl -H "Authorization: Bearer $KEY" \
"http://localhost:8080/v1/evidence/entries?type=prescribe&period=7d&limit=50"Retrieve a single entry by ID.
Response: Same shape as a single entry in the list response above.
All analytics endpoints require Bearer auth.
Compute a reliability scorecard from stored evidence.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
period |
string | 30d |
Time window (7d, 24h, 30d, 90d) |
actor |
string | — | Filter by actor ID |
tool |
string | — | Filter by tool name |
scope |
string | — | Filter by scope |
session_id |
string | — | Filter by session |
min_operations |
integer | — | Minimum operation count threshold |
Response:
{
"score": 96.5,
"band": "good",
"basis": "sufficient",
"confidence": "high",
"total_entries": 47,
"signal_summary": {
"protocol_violation": { "detected": false, "weight": 0.30, "count": 0 },
"artifact_drift": { "detected": true, "weight": 0.25, "count": 2 },
"retry_loop": { "detected": true, "weight": 0.15, "count": 1 },
"thrashing": { "detected": false, "weight": 0.10, "count": 0 },
"blast_radius": { "detected": false, "weight": 0.10, "count": 0 },
"risk_escalation": { "detected": false, "weight": 0.10, "count": 0 },
"new_scope": { "detected": true, "weight": 0.05, "count": 3 },
"repair_loop": { "detected": false, "weight": -0.05, "count": 0 }
},
"period": "30d",
"scoring_version": "v1.1.0",
"generated_at": "2025-01-15T10:30:00Z"
}Signal-level breakdown of detected behavioral patterns.
Query parameters: Same as /v1/evidence/scorecard.
ArgoCD webhook receiver. Requires:
Authorization: Bearer <EVIDRA_WEBHOOK_SECRET_ARGOCD>X-Evidra-API-Key: <tenant-api-key>
Maps sync_started / sync_completed events to prescribe/report entries.
This route remains supported, but it is not the full Argo CD product story. In self-hosted mode, controller-observed reconciliation is the primary GitOps path; webhook mode is the adjacent push path.
Generic webhook receiver. Requires:
Authorization: Bearer <EVIDRA_WEBHOOK_SECRET_GENERIC>X-Evidra-API-Key: <tenant-api-key>
Maps operation_started / operation_completed events.
Contract:
operation_idis required on both start and completion events and is the stable lifecycle identity used to correlate prescribe/report entries.idempotency_keyremains required onoperation_completed, but only for duplicate suppression.
All benchmark endpoints require Bearer auth.
Submit a benchmark run.
List benchmark runs.
Compare actor reliability across benchmark runs.
Validate a Bearer token. GET returns 200 with tenant metadata if valid, 401 if not. HEAD returns the same auth decision without a body. Both are useful as forward-auth targets for reverse proxies.
All errors return JSON:
{ "error": "human-readable message" }Common status codes:
400— bad request (invalid params or body)401— missing or invalid auth token403— forbidden (invalid invite secret)404— resource not found429— rate limit exceeded500— internal server error