> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Event reporting

> The event protocol, custom events, and how `saved` and `chat` shape downstream views.

Everything Observatory shows is built on top of a stream of events. The decorators emit them for you, but you can also call `track_event()` directly when you need a custom signal.

***

## The wire protocol

Events are HTTP-POSTed to `/api/report` as a JSON object with an `events` array. Each event is one object:

```json theme={"system"}
{
  "name": "agent_start",
  "app_id": "...",
  "run_id": "...",
  "parent_run_id": null,
  "timestamp": "2026-05-29T08:42:11.123Z",
  "type": "agent",
  "metadata": {"user_email": "..."},
  "input": { ... },
  "output": null
}
```

The API authenticates each request with `Authorization: Bearer <FLOWX_OBSERVATORY_API_KEY>` and resolves `app_id` from the metadata or, failing that, from the project the key belongs to.

***

## Batching

The SDK queues events in memory and a background consumer flushes them on a short interval, posting each batch as `{"events": [...]}` to `/api/report`. On shutdown, the SDK flushes the remaining queue. Delivery is best-effort — if a flush fails, those events are dropped rather than blocking your app.

***

## Cost and token rules

The Observatory API computes cost only for events with `type=llm`. The rules:

1. The event must include `prompt_tokens` and `completion_tokens` (or `total_tokens`).
2. The event must include `model`.
3. There must be a row for the model in the API's model cost table.

Missing any of these → `cost = 0` for that event.

<Warning>
  Adding a new model to your stack requires adding a cost row. Until that happens, the model's calls show up in traces but accumulate zero cost. Run the cost recalc job after editing the table.
</Warning>

***

## Custom events

Three custom events have semantic meaning to Observatory:

### `saved`

Marking a run as **saved** persists it past retention windows and surfaces it for dataset workflows.

```python theme={"system"}
from flowxobservatory import track_event

track_event("saved", run_id=ctx.run_id)
```

Use after a human has signed off on the run — e.g. a reviewer approving a generated document.

### `chat`

A `chat` event represents one message in a conversation. The Threads view groups consecutive `chat` events by `thread_id` into a single thread.

```python theme={"system"}
track_event(
    "chat",
    role="user" or "assistant",
    content="...",
    thread_id="cust-42",
)
```

Without `thread_id`, the SDK falls back to grouping by agent + user within a 30-minute window.

### `feedback`

Attaching feedback to a run or thread message.

```python theme={"system"}
track_event(
    "feedback",
    run_id=ctx.run_id,
    rating="positive" or "negative",
    comment="...",
)
```

Feedback shows up alongside the run in LLM Calls, on the message in Threads, and contributes to the Manage 4.2 NIST control.

***

## Advanced filtering

The runs endpoint accepts the full filter set used by the LLM Calls UI:

```http theme={"system"}
GET /api/run/?app_id=...&status=error&model=gpt-4&min_latency_s=8&tags=high-risk
```

Supported filters:

| Parameter                         | Type          | Notes                            |
| --------------------------------- | ------------- | -------------------------------- |
| `status`                          | enum          | `success` / `error` / `started`. |
| `model`                           | string        | Exact match.                     |
| `agent`                           | string        | Exact match.                     |
| `min_latency_s` / `max_latency_s` | float         | Wall-clock seconds.              |
| `min_cost` / `max_cost`           | float         | USD.                             |
| `tags`                            | list          | Any-of match.                    |
| `user_email`                      | string        | Exact match.                     |
| `from` / `to`                     | ISO timestamp | Inclusive window.                |

***

## Direct API use

If you can't use the Python SDK (different language, edge environment), report events directly:

```bash theme={"system"}
curl -X POST "$FLOWX_OBSERVATORY_API_URL/api/report" \
  -H "Authorization: Bearer $FLOWX_OBSERVATORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events": [{
    "name": "agent_start",
    "type": "agent",
    "run_id": "01HXYZ...",
    "timestamp": "2026-05-29T08:42:11.123Z",
    "input": {"prompt": "..."},
    "metadata": {"agent": "claims-approval"}
  }]}'
```

The shape mirrors the Python SDK's payload exactly.

***

## Related resources

<CardGroup cols={2}>
  <Card title="Decorators" icon="at" href="./decorators">
    The Python wrappers that emit standard events for you.
  </Card>

  <Card title="Threads" icon="comments" href="../observability/threads">
    Where `chat` events become conversations.
  </Card>
</CardGroup>
