# Call Events and Outcomes

> GET /v1/calls/{CallSid}/events returns a call's event timeline. Reference for call statuses, hangup reasons, call outcomes and how SIP response codes map to them.

Source: https://edesy.in/docs/voice-api/calls/call-events-and-outcomes

Every call keeps an event timeline, readable at `GET /v1/calls/{CallSid}/events`, and ends with three fields that explain the result: `hangup_cause` (why it ended), `sip_response_code` (the raw SIP code) and `call_outcome` (who or what it ended at). Use `call_outcome` to decide whether to retry: `network` usually means try again later, `invalid_number` means never.

## Event timeline

`GET https://voice-api.edesy.in/v1/calls/{CallSid}/events`

```json
{
  "data": [
    { "id": "...", "event_type": "call.initiated", "data": {}, "created_at": "2026-10-28T09:30:00Z" },
    { "id": "...", "event_type": "call.ringing", "data": {}, "created_at": "2026-10-28T09:30:03Z" },
    { "id": "...", "event_type": "call.answered", "data": {}, "created_at": "2026-10-28T09:30:12Z" },
    { "id": "...", "event_type": "call.stream.connected", "data": {}, "created_at": "2026-10-28T09:30:12Z" },
    { "id": "...", "event_type": "call.completed", "data": {}, "created_at": "2026-10-28T09:31:02Z" }
  ]
}
```

| Event | Meaning |
|---|---|
| `call.initiated` | The call was placed |
| `call.ringing` | The destination is ringing |
| `call.answered` | The callee answered |
| `call.completed` | The call ended |
| `call.failed` | The call could not be connected |
| `call.dtmf` | A keypad digit was pressed |
| `call.recording.started` / `call.recording.stopped` | Recording started or stopped |
| `call.stream.connected` / `call.stream.failed` | Edesy reached, or could not reach, your media-stream WebSocket. `failed` carries the reason |
| `call.stream.started` / `call.stream.stopped` | The media stream started or stopped |
| `call.relay.connected` / `call.relay.failed` | Edesy reached, or could not reach, your Conversation Relay bot |
| `call.relay.handoff` | Your relay bot ended the session, with its `handoffData` |
| `call.relay.usage` | Speech usage for a relay call: TTS characters, STT seconds, turns |

When a bot "never got the call", the stream and relay events tell you why (DNS, TLS, refused upgrade) without opening a ticket.

## Statuses

| `status` | Terminal | Meaning |
|---|---|---|
| `queued` | no | Accepted, not yet dialled |
| `initiated` | no | Dialling |
| `ringing` | no | Ringing |
| `in-progress` | no | Answered and running |
| `completed` | yes | Answered, then ended |
| `busy` | yes | Busy or rejected |
| `no-answer` | yes | Rang out, phone off, or out of coverage |
| `failed` | yes | Could not be connected |
| `canceled` | yes | Ended before answer, for example by the caller |

## Hangup reasons and SIP codes

`hangup_cause` on the call (and `HangupReason` in status callbacks) is carrier-neutral:

| SIP code | `status` | Hangup reason |
|---|---|---|
| 200 (answered) | `completed` | `completed` |
| 486, 600 | `busy` | `busy` |
| 603 | `busy` | `rejected` |
| 487, or 408 after ringing | `no-answer` | `no_answer` |
| 487 when the caller hung up first | `canceled` | `caller_canceled` |
| 480 | `no-answer` | `unavailable` |
| 404, 410, 484, 485, 604 | `failed` | `invalid_number` |
| 401, 403, 407 | `failed` | `call_blocked` |
| 408 before ringing | `failed` | `network_timeout` |
| 402, 429, 5xx | `failed` | `carrier_error` |
| none or other | `failed` | `call_failed` |

## Call outcomes

`call_outcome` groups the reason by who or what the call ended at. `failed` alone cannot tell these apart.

| `call_outcome` | Hangup reasons | Meaning | What to do |
|---|---|---|---|
| `answered` | `completed` | Answered | Nothing |
| `callee` | `busy`, `rejected`, `no_answer`, `caller_canceled` | The person did not take the call | Retry later, within your calling hours |
| `unreachable` | `unavailable` | Phone switched off or out of coverage | Retry later |
| `invalid_number` | `invalid_number` | The number does not exist | Do not retry; fix the number |
| `network` | `network_timeout`, `carrier_error`, `call_blocked` | The telecom network did not connect the call; usually temporary | Retry later |
| `account` | `insufficient_balance`, `missed_call` | Refused by your wallet balance or number settings | Top up, or check the number's routing |
| `platform` | `call_failed` | Edesy could not place the call | Contact support with the `call_sid` |

Unanswered calls of every kind are free. See [Pricing and Limits](https://edesy.in/docs/voice-api/getting-started/pricing-and-limits).
