Skip to content

Ingest API

Push Evidence Events from any system with a tenant API key. The Evidence SDK is the client for this contract — exporters dogfood it rather than posting HTTP themselves.

The canonical envelope is native JSON only (not CloudEvents, not OTLP). Schema, starter catalog, and validated examples: Evidence Event v1.

Endpoint

POST https://api.evitrus.dev/ingest
Authorization: Bearer evt_live_…
Content-Type: application/json

Accepts a single event object or an array. Response:

json
{ "accepted": 1 }

Same id + same sealed body retries with { "accepted": 1, "duplicate": true }. Same id + a different body is 409. Omit id only when a retry is not required (ingest assigns ev_ + hex). Oversize bodies are 413 (never truncated). Ignore-rule matches are 422 {"ignored":true} — do not retry.

Authentication

ModeHeader
API key (exporters / CI)Authorization: Bearer evt_live_…
Session (product UI tools)Authorization: Bearer <session token>

The tenant is always derived from the credential — never from the body.

Event shape

Minimal useful payload (flat aliases are fine; identities is the join plane):

json
{
  "id": "sdk_payments-api_a1b2c3d4e5f6_artifact",
  "timestamp": "2026-07-18T14:22:00Z",
  "source": "sdk",
  "type": "build.artifact.published",
  "identities": {
    "service": "payments-api",
    "commit": "a1b2c3d4e5f67890",
    "artifact": "sha256:9f2b…"
  },
  "summary": "Built payments-api",
  "tags": ["service:payments-api", "team:payments"],
  "artifacts": [
    {
      "type": "container_image",
      "name": "ghcr.io/acme/payments-api:a1b2c3d",
      "digest": "sha256:9f2b…"
    }
  ]
}

Do not send contentHash, signature, receivedAt, or changeId — ingest owns those. Unknown extra fields are kept.

FieldNotes
idStable producer id. Required for idempotent retries.
typeSDLC catalog name (plan.item.*, build.change.*, test.*, deploy.*, operate.*). Ingest rewrites legacy aliases (pr.merged, ticket.created, deploy.rolledback, …). Canonical rollback is deploy.rolled_back.
identitiesJoin plane: service, workItem, changeRequest, commit, artifact, environment, deployment. All optional.
service / commitSha / environmentLegacy flat aliases of identities.*
actors[{ "type": "human"|"service"|"ai", "id", "display" }]
tagskey:value strings for filtering
artifactsImages, packages, etc. Digests prove Test → Deploy
metadataFree-form JSON. Stamp metadata.mapper {name, version}
timestampEvidential RFC3339 (source time). Server time used if omitted
sourceOriginating system (github, kubernetes, sdk, …)

Full field rules: data model.

curl example

bash
curl -sS https://api.evitrus.dev/ingest \
  -H "Authorization: Bearer $EVITRUS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "sdk_payments-api_a1b2c3d4e5f6_smoke",
    "source": "sdk",
    "type": "build.artifact.published",
    "identities": {"service": "payments-api", "commit": "a1b2c3d4e5f6"},
    "summary": "Manual smoke event"
  }'

Ignore rules

Matching events are not stored. Ingest responds 422 with {"ignored":true} — do not retry. Tenant rules are type + optional tags (same When matcher as controls: exact or deploy.*).

Customer-hosted exporters poll the same list:

GET https://api.evitrus.dev/ingest/config
Authorization: Bearer evt_live_…
json
{ "ignore": [{ "type": "deploy.progressed", "tags": ["env:dev"] }] }

Poll failures are fail-open: POST anyway; ingest still enforces.

SDK

Use the Evidence SDK for custom emitters. Built-in exporters (Kubernetes, GitHub App, EventBridge, Terraform, GitHub Action) call the same client.

JavaScript (@evitrus/sdk)

js
const { emit, eventId, finishEvent } = require('@evitrus/sdk')

const event = finishEvent({
  id: eventId('sdk', 'payments-api', 'feature-flag'),
  type: 'operate.config.changed',
  service: 'payments-api',
  source: 'sdk',
  summary: 'Feature flag updated',
}, 'custom')
await emit(event)

Go (github.com/Evitrus/exporter/sdk)

go
client := sdk.New("https://api.evitrus.dev/ingest", os.Getenv("EVITRUS_API_KEY"))
_, err := client.Send(ctx, sdk.Finish(sdk.Event{
  ID:      sdk.EventID("sdk", "payments-api", "feature-flag"),
  Type:    "operate.config.changed",
  Service: "payments-api",
  Source:  "sdk",
  Summary: "Feature flag updated",
}, "custom"))

Python

python
from evitrus import emit, event_id, finish_event

emit(finish_event({
    "id": event_id("sdk", "payments-api", "feature-flag"),
    "type": "operate.config.changed",
    "service": "payments-api",
    "source": "sdk",
    "summary": "Feature flag updated",
}, mapper="custom"))

Authenticate with EVITRUS_API_KEY (or pass the key explicitly). Default ingest URL is https://api.evitrus.dev/ingest. The SDK polls GET {ingest}/config and treats ingest 422 {ignored:true} as a successful skip.

Sources: Evitrus/exportersdk/.

Evitrus — observability for auditability