Wire envelope spec
The three SDKs (Python, Node, Go) send the exact same JSON envelope to
POST /api/v1/ai/ingest with Authorization: Bearer aiv_.... You can
also POST directly without an SDK; the envelope is the contract.
Endpoint
Section titled “Endpoint”POST /api/v1/ai/ingestHost: api.monsys.aiAuthorization: Bearer aiv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxContent-Type: application/jsonTop-level envelope
Section titled “Top-level envelope”{ "trace_id": "uuid", // client-generated UUIDv4 (required) "root_op": "rag.chat", // free-text label (required) "status": "ok" | "error" | "partial", // (required) "started_at": "2026-05-12T09:50:00Z", // RFC3339 (required) "ended_at": "2026-05-12T09:50:01Z", // RFC3339 (optional) "user_session_hash": "", // SHA256 of session id, optional "sampling_decision": "full", // "full" | "head" | "summary" "spans": [ ... ] // 1..N spans (required)}{ "span_id": "uuid", // UUIDv4 (required) "parent_span_id": "uuid", // "" for root span "op": "openai.chat", // free-text (required) "provider": "openai", // canonical provider name "model": "gpt-4o-mini", // model id "started_at": "2026-05-12T09:50:00.001Z", "ended_at": "2026-05-12T09:50:00.987Z", "status": "ok", // "ok" | "error" | "refused" | "partial" "input_tokens": 42, "output_tokens": 17,
"prompt": "...", // raw — redacted on the hub "completion": "...", // raw — redacted on the hub "system_msg": "...", "tool_io": "...", // tool call or function result
"attributes": { ... }, // optional top-level metadata "events": [ { ... }, ... ] // optional event stream}Agent channels (events)
Section titled “Agent channels (events)”An agent is more than prompt and completion. What a tool returns, which tool schema the model is shown and what it writes to memory are the channels through which indirect prompt injection, MCP tool poisoning and memory poisoning arrive. You record them as typed events on a span; the hub hashes and redacts the content like prompts, scans for deterministic injection indicators and tracks MCP schemas for drift. monsys blocks nothing (ADR-0009, ADR-0029).
"events": [ { "kind": "tool_call", "name": "read_email", "args": { "folder": "inbox" }, "at": "2026-10-30T09:00:00Z" }, { "kind": "tool_result", "name": "read_email", "result": "…text the model gets to see…" }, { "kind": "mcp_schema", "server": "crm", "name": "delete_user", "description": "Deletes a user by id", "input_schema": { "type": "object", "properties": { "id": { "type": "string" } } } }, { "kind": "memory_write", "store": "vector", "key": "customer-404", "content": "…" }]| kind | required | what the hub does |
|---|---|---|
tool_call |
name, args |
hash + redaction; tool statistics |
tool_result |
name, result |
hash + redaction; injection indicators |
mcp_schema |
server, name, description, input_schema |
canonical hash; drift alert on change; indicators on the description |
memory_write |
store, key, content |
hash + redaction; injection indicators |
Indicators (regex, no model): ignore_previous, role_override,
system_prompt_exfil, tool_invocation, secrecy, hidden_html,
zero_width, markdown_exfil, shell_pipe, prompt_delimiters,
base64_blob. A hit raises an ai_injection alert in the action inbox; a
changed schema raises ai_mcp_drift and counts as configuration drift in the
Trust Score until you confirm the change. Everything lands hashed in the
evidence pack (agent_events.jsonl, mcp_schemas.json).
SDK helpers: Python span.tool_call(), span.tool_result(),
span.mcp_schema(), span.memory_write(); Node span.toolCall() etc.;
Go span.ToolCall() etc.
Canonical provider names
Section titled “Canonical provider names”| Provider | provider value |
|---|---|
| OpenAI | openai |
| Anthropic Claude | anthropic |
| Google Gemini | google |
| Mistral AI | mistral |
| Azure OpenAI | azure |
| Local (Ollama) | ollama |
Other providers are accepted but get no pricing lookup (cost = 0).
Pricing lookup
Section titled “Pricing lookup”On ingest the hub looks up ai_model_pricing by (provider, model).
Match is exact: new models not in the table get
total_cost_micro_eur = 0. Request an update via
info@be-hosted.be (pricing changelog page: coming soon).
The pricing row used is pinned per span via a pricing_snapshot_id
FK: backfilling prices does not change historical costs.
Response
Section titled “Response”{ "received": true, "trace_id": "uuid", "spans": 3, "pii_hits": 1, "total_cost_micro_eur": 603290}HTTP status:
200: processed (see body)401: token invalid or revoked403: tenant hasai_observability_enabled = false400: invalid envelope (seeerrorin body)5xx: hub-side error; the SDK does not retry (failures are silent)
Example curl
Section titled “Example curl”curl -X POST https://api.monsys.ai/api/v1/ai/ingest \ -H "Authorization: Bearer aiv_xxxx" \ -H "Content-Type: application/json" \ -d '{ "trace_id": "0e2216d5-7b6d-448a-924c-c7a08b1a7e4a", "root_op": "manual_test", "status": "ok", "started_at": "2026-05-12T09:50:00Z", "ended_at": "2026-05-12T09:50:01Z", "sampling_decision": "full", "spans": [{ "span_id": "a1b2c3d4-e5f6-4789-9abc-def012345678", "parent_span_id": "", "op": "openai.chat", "provider": "openai", "model": "gpt-4o-mini", "started_at": "2026-05-12T09:50:00.001Z", "ended_at": "2026-05-12T09:50:00.987Z", "status": "ok", "input_tokens": 22, "output_tokens": 12, "prompt": "My IBAN is BE68 5390 0754 7034.", "completion": "I cannot help with banking details.", "attributes": {}, "events": [] }] }'OpenTelemetry GenAI compat
Section titled “OpenTelemetry GenAI compat”We map OTel GenAI semantic conventions onto this envelope internally, but don’t accept OTLP directly yet. Reason: OTLP/HTTP binary overhead is overkill for one-call-per-trace. If you have an OTel-only stack, write a thin adapter from OTel span → our envelope (see envelope-from-otel.md: coming soon).