Skip to content

Ingest endpoints

Collector → cloud uploads. Authenticated via Authorization: Bearer <collector_or_tenant_token>. Browsers should never hit these — they're for the on-prem collector's uplink.py.

POST /v1/ingest/events

Upload a batch of decision events.

Request:

json
{
  "events": [
    {
      "id": "ev_W9zQoQ3bk",
      "ts": 1715520000.12,
      "status": "redact",
      "request": {
        "id": "req_k753ucn5",
        "agent": "support-bot-v2",
        "action": "http_request",
        "destination": { "host": "api.anthropic.com", "ip": "160.79.104.10", "port": 443 },
        "args": { "method": "POST", "path": "/v1/messages" },
        "metadata": {
          "source_ip": "10.0.3.42",
          "user_agent": "anthropic-sdk-python/0.30",
          "url": "https://api.anthropic.com/v1/messages",
          "method": "POST",
          "identity": {
            "name": "support-bot-v2",
            "hostname": "ops-mbp",
            "mac": "3c:22:fb:a1:b2:c3",
            "principal_type": "agent",
            "labels": { "hostname_source": "reverse_dns" }
          }
        }
      },
      "decision": {
        "action": "redact",
        "matched_rule": "redact_pii_on_egress",
        "reason": "PII detected in egress payload",
        "redacted_fields": ["body.card_number"],
        "duration_ms": 1.83
      }
    }
  ]
}

Response:

json
{ "ok": true, "ingested": 1 }

Notes:

  • Events with duplicate id are upserted (idempotent — collector can retry safely on network blip).
  • request.args should not contain Authorization / x-api-key etc. The collector's _classify_request scrubs these before shipping; cloud will accept them but it's wasted bandwidth and a security smell.
  • ts is unix seconds, float.

POST /v1/ingest/usage

Periodic token / cost rollups for cost attribution.

Request:

json
{
  "records": [
    {
      "ts": 1715520000.0,
      "agent": "support-bot-v2",
      "model": "claude-sonnet-4-6",
      "provider": "anthropic",
      "prompt_tokens": 1820,
      "completion_tokens": 642,
      "cache_read_tokens": 0,
      "cache_write_tokens": 0,
      "cost_usd": 0.018,
      "flow_id": "flow_xxx",
      "identity": { ... }
    }
  ]
}

Response:

json
{ "ok": true, "ingested": 1 }

These feed the Dashboard's Spend KPI and the (planned) Usage & Cost page. The collector emits one row per LLM call when token data is available.

POST /v1/ingest/agents

Agent-profile sync — for each agent the collector has seen, its baseline (hosts touched, tools used, models invoked, principal type, last identity). The cloud aggregates and serves this for the Agents page when there isn't enough fresh /v1/audit data to derive the profile.

Request:

json
{
  "agents": {
    "support-bot-v2": {
      "principal_type": "agent",
      "display_name": "support-bot-v2",
      "first_seen": 1714e9,
      "last_seen": 1715e9,
      "total_requests": 12480,
      "hosts": ["api.anthropic.com", "hooks.slack.com"],
      "tools": ["slack.post"],
      "models": ["claude-sonnet-4-6"],
      "actions": ["http_request", "llm_prompt"],
      "identity": { ... }
    }
  }
}

Response:

json
{ "ok": true, "synced": 1 }

Keys in the agents object are the agent names; values are profile dicts. Upsert on (tenant_id, name).

Suggested ingest cadence

  • Events: as they happen, batched. The collector buffers up to 50 events or 1 second, whichever comes first, then POSTs.
  • Usage: every 60 seconds, batched.
  • Agents: every 5 minutes — this is reference data, not real-time.

Errors

  • 400 — invalid JSON / shape. The error detail will be Pydantic's diagnostic.
  • 401 — bad credential.
  • 429 — rate limit (default 200 req/s/tenant, burst 500). Back off and retry.
  • 503 — store is down. Retry with exponential backoff.

→ See audit & stats for what comes back out.

Documentation for kilasec — the AI Agent Firewall.