---
title: Events
description: Event types accepted by the AgentPing ingest API and the payload shape for each.
section: api
order: 3
---

# Events

Every event has a `type`. Three built-in types (`llm_call`, `log`, `tool_call`) feed specific dashboard features; any other string is a custom event that shows up verbatim in the run timeline.

Envelope:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | Yes | Client-generated. Format `evt_<region>_<32 hex>`. |
| `type` | string | Yes | `llm_call`, `log`, `tool_call`, or custom. |
| `ts` | string (ISO 8601, UTC) | Yes | Client timestamp, millisecond precision. |
| `data` | object | Yes | Type-specific payload. |

## llm_call

A single call to a language model. Feeds total cost, latency, model mix, cache utilisation.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `provider` | string | Yes | `anthropic`, `openai`, `gemini`, `bedrock`, `mistral`, `cohere`. |
| `model` | string | Yes | Provider-specific identifier, e.g. `claude-opus-4-7`, `gpt-5`. |
| `input_tokens` | integer | Yes | Tokens billed as input. |
| `output_tokens` | integer | Yes | Tokens billed as output. |
| `cached_input_tokens` | integer | No | Provider's cache-read rate. |
| `cache_creation_input_tokens` | integer | No | Provider's cache-write rate (Anthropic prompt caching). |
| `mode` | string | No | `chat`, `completion`, `embedding`, `image`, `audio`. Default `chat`. |
| `latency_ms` | integer | No | Wall-clock latency. |

```json
{
  "id": "evt_eu_018f3a2b9c1d7e8fa4b9c2d7e8f1a3b7",
  "type": "llm_call",
  "ts": "2026-05-15T14:32:02.456Z",
  "data": {
    "provider": "anthropic",
    "model": "claude-opus-4-7",
    "input_tokens": 1200,
    "cached_input_tokens": 800,
    "cache_creation_input_tokens": 0,
    "output_tokens": 340,
    "mode": "chat",
    "latency_ms": 4500
  }
}
```

:::warning
`cost_usd` is server-computed from the rate card and never client-supplied. Any `cost_usd` on an `llm_call` event is ignored. Override per-model pricing in the dashboard rate card.
:::

## log

A free-form log line. Rendered inline in the run timeline.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `message` | string | Yes | The log text. |
| `level` | string | No | `debug`, `info`, `warn`, `error`. Default `info`. |

Additional keys are preserved verbatim under `data` and shown in the dashboard expand view.

```json
{
  "id": "evt_eu_018f3a2b9c1d7e8fa4b9c2d7e8f1a3b8",
  "type": "log",
  "ts": "2026-05-15T14:32:03.001Z",
  "data": {
    "message": "Routed to billing department",
    "level": "info",
    "ticket_id": "T-12345"
  }
}
```

## tool_call

A call from the agent to an external tool, function, or API.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tool` | string | Yes | Tool name. |
| `args` | object | No | Arguments passed. |
| `result` | object or string | No | Tool return value. |
| `latency_ms` | integer | No | Wall-clock latency. |
| `success` | boolean | No | Default `true`. |
| `error` | string | No | Error message if `success` is `false`. |

```json
{
  "id": "evt_eu_018f3a2b9c1d7e8fa4b9c2d7e8f1a3b9",
  "type": "tool_call",
  "ts": "2026-05-15T14:32:03.500Z",
  "data": {
    "tool": "search_knowledge_base",
    "args": {"query": "invoice not received", "limit": 5},
    "result": {"hits": 3, "top_score": 0.91},
    "latency_ms": 120,
    "success": true
  }
}
```

## Custom types

Any other `type` string is accepted. `data` is stored verbatim and rendered under the supplied type label.

```json
{
  "id": "evt_eu_018f3a2b9c1d7e8fa4b9c2d7e8f1a3ba",
  "type": "guardrail_check",
  "ts": "2026-05-15T14:32:04.100Z",
  "data": {
    "policy": "no_pii_in_output",
    "passed": true,
    "matches": []
  }
}
```

:::tip
Custom types are the right home for domain-specific signals (guardrail outcomes, retrieval hits, plan-step transitions). Use `tool_call` only when something external was actually invoked.
:::

## Deduplication

The `events` table enforces global uniqueness on `id`. Retries are safe; re-sending an event with the same `id` is a no-op. The batch response distinguishes accepted from duplicate:

```json
{ "accepted": 2, "duplicates": 1 }
```

`accepted: 0` means every event in the batch was already stored.
