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

# Event catalog

> Every event type Yotel can publish, with payload shape.

Every event envelope has this top-level shape:

```json theme={null}
{
  "event_id": "1f2e...-uuid",
  "event_type": "call.ended",
  "timestamp": 1744934400,
  "data": { ... }
}
```

Below, each event's `data` block is documented.

## Call lifecycle

### `call.started`

Fires when the dialer originates a call (pre-answer).

```json theme={null}
{
  "call_id": "c-123",
  "campaign_id": "camp-567",
  "lead_id": "l-890",
  "to_number": "9876543210",
  "dial_time": "2026-04-18T14:37:02.419Z"
}
```

### `call.answered`

Fires on `CHANNEL_ANSWER` — the callee picked up.

```json theme={null}
{
  "call_id": "c-123",
  "campaign_id": "camp-567",
  "answer_time": "2026-04-18T14:37:08.102Z",
  "ring_duration_s": 5
}
```

### `call.ended`

Fires on `CHANNEL_HANGUP`. The most common event customers subscribe to.

```json theme={null}
{
  "call_id": "c-123",
  "campaign_id": "camp-567",
  "lead_id": "l-890",
  "end_time": "2026-04-18T14:39:02.102Z",
  "hangup_cause": "NORMAL_CLEARING",
  "talk_duration_s": 114,
  "disposition": "interested",
  "recording_url": null
}
```

`recording_url` is `null` on `call.ended`; wait for `call.recording_ready`
once the GCS upload completes (typically +2–5s after hangup).

### `call.recording_ready`

Fires when the recording is uploaded and a signed URL is available.

```json theme={null}
{
  "call_id": "c-123",
  "recording_url": "https://storage.googleapis.com/yotel-recordings/...",
  "duration_s": 114
}
```

<Warning>
  The signed URL expires **7 days** after issue. If you archive
  recordings, pull them down promptly.
</Warning>

### `call.transcript_ready`

Fires when Gemini transcription completes (typically +30–60s after
recording upload).

```json theme={null}
{
  "call_id": "c-123",
  "transcript_url": "https://storage.googleapis.com/yotel-transcripts/...",
  "language": "ta-IN",
  "duration_s": 114
}
```

## Lead lifecycle

### `lead.completed`

Fires when a lead reaches a terminal state (`completed`, `failed`,
`dnd`, or `no_answer` after exhausted retries).

```json theme={null}
{
  "lead_id": "l-890",
  "campaign_id": "camp-567",
  "status": "completed",
  "attempts": 2
}
```

## Campaign lifecycle

### `campaign.started`

```json theme={null}
{ "campaign_id": "camp-567" }
```

### `campaign.paused`

Fires on BOTH manual and auto-pause. Distinguish via `data.source`:

| `source`                  | Meaning                                                                                       |
| ------------------------- | --------------------------------------------------------------------------------------------- |
| `"manual"`                | Operator paused via dashboard or `/predictive/pause` endpoint. `user_id` + `reason` included. |
| `"abandon_rate_exceeded"` | D11 in-memory controller tripped the 60s sustained-breach threshold.                          |
| `"safety_sweep"`          | D13 Celery sweep's DB-based fallback tripped the threshold. Runner was probably crashed.      |

```json theme={null}
{
  "campaign_id": "camp-567",
  "source": "manual",
  "reason": "Carrier complaint — investigate",
  "user_id": "user-42"
}
```

### `campaign.resumed`

Symmetric to `paused`. `source` is `"manual"` in all current paths
(auto-resume from safety-sweep doesn't exist — humans re-enable).

```json theme={null}
{
  "campaign_id": "camp-567",
  "source": "manual",
  "reason": "Abandon rate back under 1%",
  "user_id": "user-42"
}
```

### `campaign.completed`

```json theme={null}
{
  "campaign_id": "camp-567",
  "stats": {
    "total_leads": 1250,
    "connected": 847,
    "abandoned": 31,
    "connect_rate": 0.678
  }
}
```

## Agent lifecycle

### `agent.logged_in`

```json theme={null}
{
  "agent_id": "ag-7",
  "extension": "1042",
  "user_id": "user-42"
}
```

### `agent.logged_out`

```json theme={null}
{ "agent_id": "ag-7" }
```

## v2 events (coming)

These will land in v1.1 / v2:

| Event                        | Purpose                                                 |
| ---------------------------- | ------------------------------------------------------- |
| `campaign.created`           | CRM sync — register the campaign on your side           |
| `lead.added`                 | Post-create hook for just-added leads                   |
| `agent.state_changed`        | Fine-grained agent state stream (noisy — may be opt-in) |
| `supervisor.session_started` | Barge / whisper / monitor start                         |
