Volkom Mesh — Telemetry API Reference (for implementers)

Self-contained technical document for a developer or an AI assistant (Claude Code, Cursor) instrumenting an agent. Two channels, one API, one database per tenant. Contracts verified against the code.

Mental model#

  • Tenant = the customer, an isolated ClickHouse database (volkom_<slug>).
  • Agent = agent_name (a stable slug, e.g. sales-monitor).
  • Run = every event sharing the same run_id (UUID). Sequence: started → N×in_progresscompleted | failed.
  • Execution channel → table telemetry_events. Business channel → table business_metrics.

Authentication#

Every telemetry endpoint uses the agent's Bearer token:

Authorization: Bearer <token>

The token is generated in the portal (Connect your agents → workspace key, which works for the whole fleet; or a per-agent key from the agent's page). A workspace token can report any agent_name; a per-agent key only its own (anti-spoofing: 403 Key bound to agent '<x>').

Rate limit: 100 req/60s per token. If your fleet exceeds it, use POST /telemetry/batch (N events per request) before asking for more quota.

Base URL: https://api.volkom.ai/api/v1. Interactive Swagger: https://api.volkom.ai/docs.


1) Register the agent — POST /api/v1/agents/register#

Idempotent (upsert). Declares description + business KPIs → populates the catalog (label/unit/agg) the frontend uses as its dictionary. Status: 200. Requires Authorization: Bearer.

Body (AgentRegister):

{
  "agent_name": "sales-monitor",
  "agent_version": "1.0.0",
  "description": "Monitors daily sales.",
  "telemetry_doc": "Free text describing what it reports.",
  "business_kpis": [
    {"key": "orders",  "label": "Orders",  "unit": "count",     "agg": "sum"},
    {"key": "revenue", "label": "Revenue", "unit": "money:USD", "agg": "sum"}
  ]
}

business_kpis is [] for agents with no business channel. Fields of each KPI (BusinessKPIDecl): key (required), label (default ""), unit (default "count"), agg (default "sum").


2) Execution event — POST /api/v1/telemetry#

One POST per event (started, each step, close). Status: 202.

Body (TelemetryEventIn):

{
  "agent_name": "sales-monitor",
  "agent_version": "1.0.0",
  "client_id": "shopflow",
  "run_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "event": "in_progress",
  "timestamp": "2026-05-30T14:32:05Z",

  "message": "Read orders",
  "level": "info",
  "category": "Ingest",
  "source": "store/read.py:88",
  "seq": 2,
  "metrics": {"orders": 42.0},
  "links": ["https://oms.internal/batch/abc"],

  "metadata": {"region": "US"},
  "duration_ms": null,
  "records_processed": null,
  "error_type": null,
  "error_message": null,
  "output": null,
  "workflow_run_id": null
}

Fields: | Field | Type | Notes | |-------|------|-------| | agent_name | str req | | | agent_version | str req | | | client_id | str req | = tenant slug | | run_id | UUID req | the same for every event of the run | | event | enum req | started | in_progress | completed | failed | | timestamp | ISO-8601 req | UTC recommended | | message | str | human description (key in in_progress) | | level | str | debug|info|warning|error|success (default info) | | category | str | grouping/phase; the frontend groups steps by it | | source | str | file:line | | seq | int | order within the run (default 0) | | metrics | map | numeric, chartable measurements | | links | list | external URLs/refs | | metadata | map | free flat attributes | | duration_ms | int? | usually on close | | records_processed | int? | usually on close | | error_type / error_message | str? | on failed | | output | obj? | rich nested payload | | workflow_run_id | str? | if the run is part of a Temporal workflow |

Pattern of a run (3+ POSTs with the same run_id):

POST /telemetry  {event:"started",     run_id:R, timestamp:t0}
POST /telemetry  {event:"in_progress", run_id:R, seq:1, message:"…", category:"…", metrics:{…}}
POST /telemetry  {event:"in_progress", run_id:R, seq:2, message:"…", level:"warning"}
POST /telemetry  {event:"completed",   run_id:R, duration_ms:16000, records_processed:42}

3) Business KPIs — POST /api/v1/telemetry/business#

One POST per run (that run's KPIs). Status: 202. Separate channel; creates/extends columns on demand in business_metrics.

Body (BusinessTelemetryIn):

{
  "agent_name": "sales-monitor",
  "agent_version": "1.0.0",
  "client_id": "shopflow",
  "run_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "timestamp": "2026-05-30T14:32:20Z",
  "metrics": {"orders": 42, "revenue": 9500},
  "workflow_run_id": null
}

metrics: map of key (must match a KPI declared at register) → value (number|string). run_id can be the same as the execution run to correlate them. agent_version/client_id are optional but recommended.


Via the carcass (volkom-core) — equivalent, no HTTP#

If the agent is Python, subclassing BaseAgent does the 3 POSTs for you and handles run_id/started/close/timings automatically.

from volkom_core import BaseAgent, AgentResult, KPI, VolkomConfig, TelemetryExporter

class SalesMonitor(BaseAgent):
    agent_name = "sales-monitor"
    agent_version = "1.0.0"
    description = "Monitors daily sales."
    telemetry_doc = "Orders and revenue per run."
    business_kpis = [
        KPI("orders",  "Orders",  "count",     "sum"),
        KPI("revenue", "Revenue", "money:USD", "sum"),
    ]

    def run(self) -> AgentResult:
        self.event("Connected to the store", category="Ingest", source="store/conn.py:20")
        self.event("Read orders", category="Ingest", metrics={"orders": 42})
        self.warn("3 orders without address", category="Validation", metrics={"incomplete": 3})
        self.event("Loaded into the dashboard", category="Close",
                   links=["https://oms.internal/batch/abc"])
        self.kpi("orders", 42)
        self.kpi("revenue", 9500)
        return AgentResult(records_processed=42)

Carcass API: - event(message, *, level, category, attributes, metrics, links, source, kind) — emits an in_progress (or start/finish via kind). attributesmetadata. - step / info / warn / error (message, ...) — per-level wrappers. - kpi(key, value) — business KPI (must be in business_kpis). - emit_metric(key, value) — free metadata on the closing event (NOT a KPI). - started/completed/failed, run_id, duration_ms → automatic in execute().

Registration (once, when the client starts):

exporter = TelemetryExporter(cfg)  # cfg = VolkomConfig(client_id, control_plane_url, auth_token)
exporter.register_agent(
    agent_name=SalesMonitor.agent_name,
    agent_version=SalesMonitor.agent_version,
    description=SalesMonitor.description,
    telemetry_doc=SalesMonitor.telemetry_doc,
    business_kpis=[{"key": k.key, "label": k.label, "unit": k.unit, "agg": k.agg}
                  for k in SalesMonitor.business_kpis],
)
# run once (generates run_id + started + close):
SalesMonitor(cfg, exporter).execute(run_id=<uuid|None>, workflow_run_id=<str|None>)

KPI vocabulary#

unit: count · money:<ISO> (e.g. money:USD, money:ARS, money:EUR) · percent · duration (value in seconds).

agg (how it totals over the dashboard period): sum (cumulative: sales, invoices) · last (states: stock, MRR, % occupancy) · avg (averages: wait time) · max (peaks).

Picking the wrong agg produces wrong totals: sum of a state inflates the number; last of something cumulative understates it.


Reading (what the frontends consume — reference)#

Same API, same DB; the portal and the admin panel read from here: - GET /api/v1/tenants/{slug}/dashboard/runs?include_steps=<bool>&since=&until=&limit= — runs; with include_steps=true it includes the in_progress events. - GET /api/v1/tenants/{slug}/runs/{run_id}/events — full narrative of one run. - GET /api/v1/tenants/{slug}/business/catalog — declared KPIs (label/unit/agg). - GET /api/v1/tenants/{slug}/business/summary?time_range= — KPIs aggregated by agg. - GET /api/v1/tenants/{slug}/business/series?agent_name=&key=&time_range=&bucket= — trend.


Implementation checklist#

  1. [ ] POST /agents/register with declared business_kpis (idempotent).
  2. [ ] Per run: one unique UUID run_id, reused across all its events.
  3. [ ] started at the beginning; in_progress for each step (with category, metrics, level as appropriate); completed/failed at the end with duration_ms.
  4. [ ] Business KPIs via POST /telemetry/business (keys = the declared ones).
  5. [ ] Header Authorization: Bearer <token> on everything.
  6. [ ] ALWAYS close the run (no close → perpetual "in progress").
  7. [ ] Errors with level:"error" / event:"failed" + error_type/error_message.

Errors and gotchas#

  • Different run_id per event → every step shows as a separate run. Reuse it.
  • Run without a close → shows as "in progress" forever.
  • Undeclared KPI → the value gets in but without catalog metadata (unit/agg).
  • Rate limit 429 → respect 100 req/60s per token; for bursts, POST /telemetry/batch.
  • Anti-spoofing 403 → a per-agent key can only report its own agent_name.