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

# Manual Events API

> Send telemetry directly to whyops-analyse, including detailed event schemas, pipeline behavior, and tool/runtime use cases.

While the WhyOps Proxy is the easiest way to instrument your agents automatically, you can send events directly to `whyops-analyse` when you need custom tools, non-LLM reasoning steps, or internal framework logic in the graph.

## When to use manual events

Use manual events for anything the proxy cannot see by itself:

* your actual tool execution input/output
* retries inside your framework
* orchestration steps outside the LLM boundary
* runtime crashes after the LLM asked for a tool
* internal thinking blocks or planning state you choose to expose

<CardGroup cols={2}>
  <Card title="Manual Events Reference" icon="book" href="/docs/integrations/manual-events-reference">
    Detailed event payload shapes, recommended runtime patterns, idempotency guidance, and tool pipeline examples.
  </Card>

  <Card title="SDK Runtime Guides" icon="diagram-project" href="/docs/integrations/sdk-packages">
    Use the published SDKs if you want the same event model with typed helpers instead of raw JSON payloads.
  </Card>
</CardGroup>

## Endpoint Overview

**Base URL**: `https://a.whyops.com/api` (or your self-hosted URL)

**Authentication**:
You must include an API key generated from the WhyOps Dashboard in the `Authorization` header.

```http theme={null}
Authorization: Bearer <WHYOPS_API_KEY>
```

The API key automatically resolves your `userId`, `projectId`, and `environmentId`.

If you do not use API key auth, you must send all of these headers manually:

```http theme={null}
X-User-Id: <uuid>
X-Project-Id: <uuid>
X-Environment-Id: <uuid>
```

### Optional headers

You can optionally send `X-External-User-Id` to link traces to your application's user entities:

```http theme={null}
X-External-User-Id: user_12345
```

This is useful when you want to associate traces with your own user IDs without exposing WhyOps internal user IDs.

## The ingestion pipeline

```mermaid theme={null}
flowchart LR
  A[Your App or Worker] --> B[/api/events/ingest]
  B --> C[Redis Stream Queue]
  C --> D[Analyse Worker]
  D --> E[EventService.processEvent]
  E --> F[TraceService.ensureTraceExists]
  F --> G[Postgres: traces + trace_events]
  G --> H[UI graphs, analytics, analyses]
```

### What happens on ingest

WhyOps validates the payload, merges auth context, enqueues the event when Redis is available, processes it sequentially per trace, resolves sampling plus missing step or span identifiers, and persists the final event to `trace_events`.

If queueing is unavailable, the controller falls back to direct synchronous processing.

## Ingesting Events

Use the `/events/ingest` endpoint to send one or more events.

### `POST /api/events/ingest`

Accepts a single event object or an array of event objects.

<Tabs>
  <Tab title="Request">
    ```bash theme={null}
    curl -X POST https://a.whyops.com/api/events/ingest \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "traceId": "123e4567-e89b-12d3-a456-426614174000",
        "spanId": "tool-call-1",
        "eventType": "tool_call_request",
        "agentName": "my-customer-support-agent",
        "timestamp": "2024-03-20T12:00:00Z",
        "content": {
          "toolCalls": [
            {
              "name": "search_database",
              "arguments": { "query": "latest orders" }
            }
          ]
        },
        "metadata": {
          "tool": "search_database"
        }
      }'
    ```
  </Tab>
</Tabs>

<CardGroup cols={2}>
  <Card title="TypeScript Runtime Guide" icon="code" href="/docs/integrations/typescript-sdk-runtime">
    Use the `@whyops/sdk` trace builder instead of hand-rolling the event payloads in Node.js or Bun services.
  </Card>

  <Card title="Python Runtime Guide" icon="terminal" href="/docs/integrations/python-sdk-runtime">
    Use the `whyops` sync and async trace helpers instead of posting raw REST payloads in Python services.
  </Card>
</CardGroup>

### Response behavior

* `202 Accepted` when the event was queued successfully
* `201 Created` when it falls back to direct processing
* `400` for validation or missing auth context
* `409` when a trace is already bound to a different agent version

Typical queued response:

```json theme={null}
{
  "success": true,
  "accepted": true,
  "queuedCount": 1
}
```

## Other useful endpoints

* `POST /api/events` processes events directly instead of queue-first and accepts the same payload shape.
* `POST /api/events/tool-result` is a convenience endpoint that automatically sets `eventType: "tool_call_response"`.
* `GET /api/events/help` returns the machine-readable help payload for supported event types.
* `GET /api/events?traceId=...&include=content,metadata` and `GET /api/events/:id?include=content,metadata` let you inspect stored events while debugging.

## Event schema summary

Every event you send must conform to this schema:

| Field            | Type      | Required | Description                                                   |
| ---------------- | --------- | -------- | ------------------------------------------------------------- |
| `eventType`      | `string`  | **Yes**  | See below for allowed types.                                  |
| `traceId`        | `string`  | **Yes**  | 1-128 chars. Groups events into a single agent run.           |
| `agentName`      | `string`  | **Yes**  | 1-255 chars. Identifies the specific agent.                   |
| `spanId`         | `string`  | No       | Links related requests and responses (e.g. tool calls).       |
| `stepId`         | `integer` | No       | Used for ordering events sequentially (>= 1).                 |
| `parentStepId`   | `integer` | No       | Used to build nested tree structures (>= 1).                  |
| `timestamp`      | `string`  | No       | ISO 8601 string. Defaults to the time received.               |
| `content`        | `any`     | No       | The actual payload (e.g., messages, tool outputs).            |
| `metadata`       | `object`  | No       | Additional context (e.g., latency, tokens, model used).       |
| `idempotencyKey` | `string`  | No       | Prevents duplicate event ingestion.                           |
| `externalUserId` | `string`  | No       | Your application user ID to link traces to end-user entities. |

## How step and span resolution works

If you do not send `stepId`, WhyOps auto-increments from the last event in that trace.

If you do not send `parentStepId`, WhyOps uses the previous step as parent.

If you do not send `spanId`, WhyOps generates one for you.

**Recommendation:**

* explicitly set `spanId` for request/response pairs
* optionally let WhyOps generate `stepId` unless you already have a stable internal step model

## Allowed event types

To ensure accurate static analysis and graph rendering, you must use one of the predefined event types.

### LLM Interactions

* `user_message`: user input to the model or the full message history.
* `llm_response`: model response content and optional tool calls. Requires `metadata.model` and `metadata.provider`.
* `embedding_request`: input payload sent to an embedding provider.
* `embedding_response`: output summary from an embedding provider. Requires `metadata.model` and `metadata.provider`.
* `llm_thinking`: internal reasoning blocks such as Anthropic thinking.

### Tool Execution

* `tool_call`: legacy combined tool call event; backend splits it into `tool_call_request` + `tool_call_response` automatically.
* `tool_call_request`: a request to execute a tool. **Requires** `metadata.tool`.
* `tool_call_response`: the output returned from a tool execution. **Requires** `metadata.tool`.
* `tool_result`: tool results returned back to the model by your framework.

### Errors

* `error`: upstream API failures, tool crashes, framework exceptions, or app-side failures.

## Detailed payload reference

The full payload catalog, runtime patterns, idempotency rules, and tool pipeline examples are documented in [Manual Events Reference](/docs/integrations/manual-events-reference).
