OpenTelemetry ingest
If you already export GenAI spans, point an OTLP/JSON exporter at VIGIL. POST /otel/v1/traces (scope events:write) accepts a trace export (resourceSpans) and turns the spans VIGIL understands into events with source: otel. They go through the same scoring pipeline as POST /events/, registries included.
The mapping
Attribute names are pinned in api/otel_map.py; the GenAI semantic conventions are still marked Development upstream, so VIGIL pins a version rather than tracking the draft.
| Span | Event |
|---|---|
any span with vigil.config.key | config_write { key, value } |
gen_ai.operation.name == "invoke_agent" | output_generated { content_length: gen_ai.usage.output_tokens * 4, model } |
gen_ai.operation.name == "execute_tool" and gen_ai.tool.name matches write, copy, move, delete, transfer | file_operation { operation, asset_type: "data", tool_name } (move maps to transfer) |
| anything else | ignored, counted |
agent_id is gen_ai.agent.id, else the resource service.name; agents seen for the first time are registered under that id. actor is enduser.id when present. Every mapped event carries span_name, trace_id and span_id in its payload.
The response
{
"accepted": 3,
"ignored": 1,
"gates": [
{ "agent_id": "otel-agent", "event_type": "output_generated", "event_id": 91, "action_id": 91,
"gate_status": "pass", "bii": 0.91, "requires_human_review": false, "actor": null }
]
}
One gate per accepted span, in order. Your exporter does not read them; your reviewer sees the holds in the console or through a webhook.
Send a trace
Two spans: an invoke_agent with 120 output tokens and an execute_tool named write_file.
curl -sf -X POST "$VIGIL_URL/otel/v1/traces" -H "Authorization: Bearer $VIGIL_KEY" -H "Content-Type: application/json" -d '{
"resourceSpans": [{
"resource": { "attributes": [{ "key": "service.name", "value": { "stringValue": "docs-otel-agent" } }] },
"scopeSpans": [{
"scope": { "name": "docs.example" },
"spans": [
{ "traceId": "5b8aa5a2d2c872e8321cf37308d69df2", "spanId": "051581bf3cb55c13", "name": "invoke_agent billing", "kind": 1,
"startTimeUnixNano": "1757330000000000000", "endTimeUnixNano": "1757330001200000000",
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "invoke_agent" } },
{ "key": "gen_ai.request.model", "value": { "stringValue": "claude-haiku-4-5" } },
{ "key": "gen_ai.usage.output_tokens", "value": { "intValue": "120" } } ] },
{ "traceId": "5b8aa5a2d2c872e8321cf37308d69df2", "spanId": "5fb397be34d26b51", "parentSpanId": "051581bf3cb55c13",
"name": "execute_tool write_file", "kind": 3,
"startTimeUnixNano": "1757330000400000000", "endTimeUnixNano": "1757330000500000000",
"attributes": [
{ "key": "gen_ai.operation.name", "value": { "stringValue": "execute_tool" } },
{ "key": "gen_ai.tool.name", "value": { "stringValue": "write_file" } } ] }
] }] }] }'
Expected: accepted: 2, two gates, agent docs-otel-agent registered if it was not.