---
title: "이벤트 카탈로그"
description: "ThunderPhone이 발생시키는 모든 웹훅 이벤트 유형입니다."
---

모든 웹훅 본문에는 이 페이지의 이벤트 유형 중 하나를 값으로 갖는 `type` 필드가 있습니다. [엔드포인트](/ko/webhooks/endpoints)를 구독할 때 `events` 배열에는 원하는 이벤트 유형을 포함해야 합니다(모든 이벤트를 구독하려면 비워 둘 수 있음. 단, 명시적으로 이름을 지정한 엔드포인트에만 전달되는 턴별 이벤트 [`telephony.turn`](#telephony-turn) / [`web.turn`](#web-turn)은 제외).

이 이벤트는 두 가지 전달 방식으로 제공됩니다:

* **엔드포인트 전송**은 항상 [재시도](/ko/webhooks/overview)를 사용하는 **비차단** 알림입니다. 모든 2xx로 응답하십시오. 엔벌로프에는 중복 제거에 사용할 `event_id`가 포함됩니다.
* **차단** 교환은 [레거시 단일 URL 웹훅](/ko/webhooks/overview)에서만 실행됩니다. 여기에는 [`telephony.incoming` / `web.incoming`](/ko/webhooks/call-incoming) 구성 요청(웹훅 모드 번호: 재시도 1회를 포함해 13초, 웹훅 모드 위젯 키: 10초)과 웹훅 모드 [도구 디스패치](/ko/tools/overview)가 포함됩니다. 응답이 진행 중인 통화를 구성합니다.

아래 예제 페이로드는 전송 순서의 엔드포인트 엔벌로프를 보여 줍니다(키는 알파벳순으로 정렬됨: `data`, `event_id`, `type`). 레거시 전달은 `event_id` 없이 동일한 `data`를 포함합니다.

## 통화 이벤트

### `telephony.incoming`

수신 통화가 [전화번호](/api-reference/phone-numbers) 중 하나에 도달하면 전송됩니다. 엔드포인트 전송은 에이전트 구성 여부나 웹훅 구성 여부와 관계없이 **모든** 수신 통화에 대해 전송 후 응답을 기다리지 않는 알림입니다. 에이전트가 할당되지 않은 번호는 레거시 웹훅에서 추가로 **블로킹** 구성 요청을 받습니다. 전체 요청/응답 스키마는 [`telephony.incoming` / `web.incoming`](/ko/webhooks/call-incoming)을 참조하세요.

```json
{
  "data": {
    "call_id": 987654321,
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  },
  "event_id": "3f6b2ad0-1c9e-4a57-9f2b-8f6f0f9d2f11",
  "type": "telephony.incoming"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

### `telephony.complete`

수신 또는 발신 전화 통화가 종료되면 전송됩니다. 블로킹되지 않습니다. 대화 내용, 사용 가능한 경우 녹음 URL 및 청구 요약을 포함합니다. 페이로드 스키마는 [`telephony.complete` / `web.complete`](/ko/webhooks/call-complete)를 참조하세요.

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

### `telephony.tool`

전화 통화가 [함수 도구](/ko/tools/overview)를 호출한 후 전송됩니다. 블로킹되지 않는 감사 알림입니다. 이 이벤트가 전송될 때 도구는 이미 실행된 상태입니다. 자체 함수 도구와 기본 제공 `send_sms` 도구에 적용되며, 다른 기본 제공 도구, 지식 기반 도구, 앱 연결 도구 또는 MCP 도구에는 적용되지 않습니다. `send_sms`의 경우 `arguments.to` 및 `arguments.body`는 모두 `[REDACTED]`입니다. 완료 대화 내용에서도 해당 값은 마스킹됩니다. 응답의 `message_id`를 사용하여 조직 범위 SMS 리소스에서 콘텐츠와 수신자를 조회하세요.

```json
{
  "data": {
    "arguments": { "date": "2026-04-21" },
    "call_id": 987654321,
    "from_number": "+14155550199",
    "response": {
      "response": { "available_slots": ["9:00 AM", "2:00 PM"] },
      "status": 200
    },
    "to_number": "+15551234567",
    "tool_name": "search_appointments"
  },
  "event_id": "1f0a7c3e-52d4-4a0e-8f4b-b1a6a1c0d9e2",
  "type": "telephony.tool"
}
```

`response`는 실행 결과입니다. 성공 시 `{"status": <http status>,
"response": <your endpoint's JSON>}`이고, 실패 시
`{"status": <status>, "error": "<message>"}`입니다.

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

### `telephony.turn`

전화 통화가 **진행 중**인 동안 발화가 포함된 각 턴이 발생할 때마다 전송됩니다. 에이전트의 음성 완료 응답과 발신자의 전사된 턴이 포함됩니다. [`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript)를 폴링하는 대신 일반 웹훅을 통해 실시간 대화를 추적할 수 있습니다. 블로킹되지 않습니다.

<Note>
  **명시적 구독이 필요합니다.** 다른 모든 이벤트 유형과 달리 턴 이벤트는 비어 있는 `events` 배열(전체 구독)을 통해 전송되지 않습니다. 엔드포인트의 `events` 목록에 `telephony.turn` / `web.turn`이 명시적으로 포함된 경우에만 수신합니다.
</Note>

```json
{
  "data": {
    "call_id": 987654321,
    "entry_type": "completion",
    "from_number": "+14155550199",
    "position": 7,
    "role": "assistant",
    "start_ms": 15200,
    "text": "How many employees does your company have?",
    "to_number": "+15551234567"
  },
  "event_id": "8d3f5a2c-7b1e-4c9a-b6d0-2e4f6a8c0d1e",
  "type": "telephony.turn"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `position` | integer | 통화 기록 내 턴의 인덱스이며, 순서 지정을 위한 안정적인 식별자 |
| `role` | string | `assistant`(에이전트 발화) 또는 `user`(발신자 발화) |
| `text` | string | 전송 시점에 알려진 턴의 대화 내용 텍스트 |
| `entry_type` | string | 기본 기록 항목 유형: `completion`(에이전트) 또는 `user_turn` / `span`(발신자) |
| `start_ms`, `end_ms` | integer | 통화 시작 이후의 밀리초 단위 오디오 오프셋이며, 전송 시점에 재생 타이밍을 이미 알고 있는 경우에만 제공 |

<Note>
  전송은 최소 한 번 보장되며 턴은 **순서와 다르게** 도착할 수 있습니다. 엔벨로프의 `event_id`로 중복을 제거하고 `position`으로 순서를 지정하세요. 발신자 대화 내용은 전송 후에도 계속 보정될 수 있습니다. 통화 후 [`telephony.complete`](/ko/webhooks/call-complete) 대화 내용이 기준입니다. 발화된 턴만 전송되며, 도구 호출, 속삭임 및 기타 내부 항목은 이 스트림에 표시되지 않습니다.
</Note>

### `web.incoming`

[웹 위젯](/ko/widget/overview) 세션 또는 빌더 마이크 테스트 통화가 시작될 때 전송되는 `telephony.incoming`의 웹 채널 버전입니다. 엔드포인트 전송은 모든 웹 세션에 대해 전송 후 응답을 기다리지 않습니다. `mode="webhook"`의 공개 키는 레거시 웹훅에서 추가로 **블로킹** 구성 요청을 받습니다. 해당 블로킹 요청은 형태가 다르며(`origin_domain`, `publishable_key_prefix`, 전화번호 없음), [`telephony.incoming` / `web.incoming`](/ko/webhooks/call-incoming)을 참조하세요.

```json
{
  "data": {
    "call_id": 987654322,
    "from_number": "web",
    "origin_domain": "https://example.com",
    "publishable_key_prefix": "pk_live_a1b2",
    "to_number": "+15551234567"
  },
  "event_id": "9a2b4c6d-8e0f-4a1b-9c2d-3e4f5a6b7c8d",
  "type": "web.incoming"
}
```

`from_number`는 항상 리터럴 `"web"`입니다. 웹훅 모드 위젯 세션의 경우 `to_number`는 비어 있습니다(세션의 에이전트 번호는 구성 후 할당됨). 빌더 마이크 테스트 통화의 경우 `origin_domain` 및 `publishable_key_prefix`가 비어 있습니다.

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

### `web.complete`

웹 위젯 통화(`direction: "web"`)와 빌더 마이크 테스트 통화(`direction: "test"`)를 처리하는 `telephony.complete`의 웹 채널 버전입니다. 블로킹되지 않습니다. [`telephony.complete`](/ko/webhooks/call-complete)와 동일한 페이로드 형태이며 `origin_domain`이 추가되고 `from_number`는 `"web"`으로 설정됩니다.

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

<Note>
  레거시 단일 URL 웹훅에서는 빌더 마이크 테스트 통화가 과거부터 `telephony.complete`로 보고됩니다. 해당 위치에서는 `direction: "web"` 통화만 `web.complete` 유형을 사용합니다. 엔드포인트 시스템은 웹 및 테스트 통화를 모두 `web.*`에 매핑합니다. 이전 페이로드에는 레거시 `direction` 값인 `widget` 또는 `mic`이 포함될 수 있습니다.
</Note>

### `web.tool`

`telephony.tool`의 웹 채널 버전입니다. `data`에는 `from_number` / `to_number` 대신 `origin_domain`이 포함됩니다.

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

### `web.turn`

웹 위젯 통화와 빌더 마이크 테스트 통화를 처리하는 [`telephony.turn`](#telephony-turn)의 웹 채널 버전입니다. `from_number` / `to_number` 대신 `origin_domain`을 포함하는 동일한 페이로드 형태를 사용합니다. `telephony.turn`과 마찬가지로 **명시적 구독**이 필요하며, 비어 있는 `events` 배열을 통해서는 전송되지 않습니다.

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 할당된 경우 통화를 처리한 에이전트 |

### `call.transfer_completed`

통화당 한 번 전송되며, [통화 전환](/ko/guides/call-transfers)의 최종 결과를 포함합니다. 결과는 콜드 전환이거나, 에이전트가 빠진 후 웜 전환으로 남는 사람 간 브리지입니다.
차단 없이 처리되며 엔드포인트로만 전송됩니다(레거시 단일 URL 웹훅으로는 전송되지 않음). 통화의 `telephony.complete` 이후에 도착합니다.
통신사가 통화를 인계받거나 전환에 실패한 후, 또는 ThunderPhone이 통화를 브리지한 경우 브리지가 종료된 후 도착합니다.

```json
{
  "data": {
    "agent_id": 12,
    "agent_name": "Front desk",
    "bridge_kind": "cold_fallback",
    "bridge_seconds": 255,
    "bridge_total_cents": 5,
    "call_id": 987654321,
    "destination": "+15555550102",
    "sip_status": 403,
    "transfer_outcome": "bridged"
  },
  "event_id": "5b0f6c1e-9d2a-5e7b-8c4d-1a2b3c4d5e6f",
  "type": "call.transfer_completed"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `call_id` | integer | 전환된 통화입니다 |
| `agent_id` | integer \| null | 할당된 경우 통화를 처리한 에이전트입니다 |
| `agent_name` | string \| null | 할당된 경우 통화를 처리한 에이전트입니다 |
| `transfer_outcome` | string | `refer_accepted`: 통신사가 통화를 인계받았습니다. `bridged`: 통신사가 콜드 전환을 거부했거나 웜 전환의 에이전트가 빠졌기 때문에 ThunderPhone이 발신자와 착신 번호를 직접 연결한 상태입니다. `bridge_unanswered`: 통신사가 콜드 전환을 거부했고 착신 번호가 ThunderPhone의 자체 발신에 응답하지 않았습니다. `failed`: 전환이 완료되지 않았고 통화가 종료되었습니다 |
| `destination` | string \| null | 통화가 전환된 번호입니다 |
| `sip_status` | integer \| null | 통신사가 콜드 전환을 거부했을 때의 SIP 상태입니다. `403`(예: 통화 전환을 지원하지 않는 Twilio 트렁크), `405` 또는 `501`입니다. 통신사가 거부하지 않은 경우 `null`입니다 |
| `bridge_kind` | string \| null | `bridged`에만 해당: `cold_fallback`(ThunderPhone이 연결한 거부된 콜드 전환) 또는 `warm`(에이전트가 떠난 후 웜 전환에 남은 두 사람)입니다 |
| `bridge_seconds` | integer \| null | `bridged`에만 해당: ThunderPhone이 청구한 브리지 시간(초)입니다 |
| `bridge_total_cents` | integer \| null | `bridged`에만 해당: 무료 통화 크레딧 적용 후 브리지 비용(USD 센트)입니다. 통화의 `billing_total_cents`와 별도로 청구됩니다 |

동일한 값은 [통화 객체](/api-reference/calls#call-object)에도
`transfer_outcome`, `transfer_destination`, `transfer_sip_status`,
`transfer_bridge_kind`, `transfer_bridge_seconds`,
`transfer_bridge_total_cents`로 제공됩니다. `event_id`는 통화의 모든 전송에서 동일하므로,
반복 보고가 두 번째 이벤트를 생성하지 않습니다.

---

## SMS 이벤트

SMS 이벤트는 조직의 연결된 Twilio 또는 Telnyx 계정을 통해 전송된 메시지를 설명합니다. 메시지 본문은 생략되고 `to`는 마스킹됩니다. 콘텐츠 또는 수신자가 필요한 경우
[`GET /v1/sms/{message_id}`](/api-reference/sms#retrieve-a-message)에서 조직 범위 메시지를 조회합니다.

### `sms.sent`

고객의 통신사가 발신 SMS 요청을 수락하면 전송됩니다. 이는 단말기 수신 확인이 아닙니다.

```json
{
  "data": {
    "accepted_at": "2026-09-19T15:42:10.113Z",
    "agent_id": 12,
    "call_id": 987654321,
    "created_at": "2026-09-19T15:42:09.901Z",
    "direction": "outbound",
    "error_code": null,
    "error_message": null,
    "from_number": "+15551234567",
    "id": "313a3117-827b-46e9-a01a-ac1493838724",
    "provider": "twilio",
    "provider_id": "SM0123456789abcdef0123456789abcdef",
    "segment_count": 1,
    "source": "agent_tool",
    "status": "accepted",
    "to": "[REDACTED]"
  },
  "event_id": "0ae347b4-1716-45bb-9a0e-b52373586e37",
  "type": "sms.sent"
}
```

### `sms.failed`

통신사가 영구적으로 거부한 후, 일시적 오류에 대한 재시도가 모두 소진된 후 또는 결과를 최종적으로 알 수 없는 경우 전송됩니다. `error_code`는 안정적인 ThunderPhone 코드이며, `error_message`는 가능한 경우 고객이 취해야 할 조치를 설명합니다. `status: "unknown"`은 통신사가 SMS를 수락했을 수 있음을 의미합니다. ThunderPhone은 자동으로 재전송하지 않으므로 통신사 로그에서 조정합니다.

### `sms.received`

수신 SMS 지원을 위해 예약되어 있습니다. 현재 릴리스에서는 `sms.received` 이벤트가 발생하지 않습니다. 수신 통신사 웹훅 구성 및 서명 검증은 별도로 제공될 예정입니다.

---

## 음성 이벤트

사용자 지정 음성 생성은 비동기적으로 처리됩니다. 이러한 비차단 이벤트를 사용하면
[클론 세부 정보 엔드포인트](/api-reference/custom-voices#list-and-retrieve-clones)를 폴링하는 대신 최종 결과에 대응할 수 있습니다.

`voice.ready` 및 `voice.failed`는 `events: []`가 설정된 조직 전체 엔드포인트로만 전달됩니다. 명시적 이벤트 필터로 선택할 수 없습니다.

### `voice.ready`

사용자 지정 음성 처리가 완료되어 에이전트에 할당할 수 있게 되면 전송됩니다.

```json
{
  "data": {
    "voice": {
      "created_at": "2026-07-30T14:12:08.317Z",
      "display_name": "Support voice",
      "failure_reason": "",
      "gender": "female",
      "id": "cv_2f6f90b0e9a34ee8b39be7d1",
      "language": "en",
      "name": "custom:cv_2f6f90b0e9a34ee8b39be7d1",
      "status": "ready",
      "updated_at": "2026-07-30T14:13:31.605Z"
    }
  },
  "event_id": "2d5f0a61-e9b5-4a3c-b684-29d7d9e4b214",
  "type": "voice.ready"
}
```

### `voice.failed`

사용자 지정 음성 처리가 영구적 실패 상태에 도달하면 전송됩니다.

```json
{
  "data": {
    "reason": "audio sample could not be processed",
    "voice": {
      "created_at": "2026-07-30T14:12:08.317Z",
      "display_name": "Support voice",
      "failure_reason": "audio sample could not be processed",
      "gender": "female",
      "id": "cv_2f6f90b0e9a34ee8b39be7d1",
      "language": "en",
      "name": "custom:cv_2f6f90b0e9a34ee8b39be7d1",
      "status": "failed",
      "updated_at": "2026-07-30T14:13:31.605Z"
    }
  },
  "event_id": "3493e985-1a75-4f77-a10a-e74af440cd31",
  "type": "voice.failed"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `voice.id` | string | 사용자 지정 음성 공개 ID |
| `voice.name` | string | `custom:<public_id>` 형식의 에이전트 음성 값 |
| `voice.display_name` | string | 조직에 표시되는 음성 이름 |
| `voice.language` | string | 클론의 단일 언어 코드 |
| `voice.gender` | string | `male`, `female` 또는 빈 문자열 |
| `voice.status` | string | `voice.ready`의 경우 `ready`, `voice.failed`의 경우 `failed` |
| `voice.failure_reason` | string | 성공 시 비어 있음, 실패 시 처리 실패 세부 정보 |
| `voice.created_at`, `voice.updated_at` | timestamp | ISO 8601 타임스탬프 |
| `reason` | string | 실패 세부 정보이며 `voice.failed`에만 존재 |

## 품질 이벤트

### `call.graded`

통화의 [AI 평가 실행](/api-reference/calls#ai-call-grading)이 완료될 때마다 전송됩니다. 차단하지 않습니다.

```json
{
  "data": {
    "call_id": 987654321,
    "grade": {
      "call_outcome": "success",
      "created_at": "2026-04-20T18:25:11.002Z",
      "detected_issues": [],
      "graded_at": "2026-04-20T18:25:11.002Z",
      "grader_model": "heuristic-v1",
      "id": 5512,
      "score": 92,
      "status": "completed",
      "summary": "Caller asked about their policy and got a full answer…"
    },
    "report": null
  },
  "event_id": "7c1d2e3f-4a5b-4c6d-8e9f-0a1b2c3d4e5f",
  "type": "call.graded"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 할당된 경우 통화를 처리한 에이전트 |
| `grade.id` | integer | 평가 ID |
| `grade.score` | integer \| null | 0–100 |
| `grade.call_outcome` | string | `success`, `failure`, `unknown` 또는 `no_conversation` |
| `grade.summary` | string | 한 문단 요약 |
| `grade.detected_issues` | array | 평가기가 발견한 이슈 문자열 |
| `grade.status` | string | 항상 `completed` — 완료된 실행에서만 발생합니다 |
| `grade.grader_model` | string | 결과를 생성한 평가기(예: `heuristic-v1`) |
| `grade.graded_at`, `grade.created_at` | timestamp | |
| `report` | object \| null | 고객 대상 [통화 보고서](/api-reference/calls#call-report): 결과 레이블, 주의 필요 플래그 및 사유, 발신자, 한 줄 요약, 발신자 경험. 초기 휴리스틱 평가에서는 `null`이거나 필드가 자리표시자인 `pending` 보고서입니다. 전체 평가 이벤트에는 완료된 보고서가 포함됩니다 |

<Note>
  통화는 두 번 이상 평가될 수 있습니다. 녹음이 제공되면 빠른 휴리스틱 평가
  뒤에 전체 모델 평가가 이어지는 경우가 많으며, 수동 재평가도 가능합니다.
  완료된 각 실행은 자체 `call.graded` 이벤트를 전송합니다. 가장 최근의
  `graded_at`을 기준으로 처리하십시오.
</Note>

### `call.data_extracted`

[`POST /v1/calls/{call_id}/extract`](/api-reference/calls#run-data-extraction)를 통한
수동 재실행 또는 `telephony.complete` / `web.complete` 이후의 지연 재시도를 포함하여,
구조화된 데이터 추출이 성공적으로 완료될 때마다 전송됩니다. 차단하지 않습니다.

차단 추출 모드에서는 완료 이벤트가 일반적으로 75초의 추출 예산보다 오래 기다리지
않습니다. 추출 워커 프로세스가 손실되면 프로덕션 완료 처리 작업은 5분마다 다른
추출 시도를 시작하기 전에 `blocking_deadline_at`이 지난 완료를 해제합니다. 이후
성공한 결과는 이 이벤트로 별도로 전송됩니다.

<Warning>
  추출된 값은 대화를 해석한 모델의 결과입니다. null이 아닌 각 값에는 해당 값의
  근거가 된 인용문, 발화자 역할, 턴 인덱스와 실행 수준의 검증 플래그가 포함됩니다.
  금전, 신원 또는 접근과 관련된 결정을 내리기 전에 결과를 검증하십시오.
</Warning>

```json
{
  "data": {
    "call_id": 987654321,
    "agent_id": 12,
    "agent_name": "Acme intake",
    "extracted_data": {
      "status": "completed",
      "fields": {
        "customer_name": "Alex Morgan",
        "appointment_date": "2026-04-23"
      },
      "evidence": {
        "customer_name": {
          "quote": "My name is Alex Morgan",
          "speaker_role": "caller",
          "turn_index": 4
        },
        "appointment_date": {
          "quote": "April 23 works for me",
          "speaker_role": "caller",
          "turn_index": 7
        }
      },
      "verification": "verified",
      "field_reasons": {},
      "schema_version": "92850758e231a3c95a..."
    },
    "extracted_at": "2026-04-20T18:25:11.002Z",
    "model": "<model id>",
    "summary": "Alex Morgan called to book a follow-up visit and confirmed Thursday, April 23."
  },
  "event_id": "4d79ef1d-c2b1-4ed6-85b8-8326bd2895ef",
  "type": "call.data_extracted"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 에이전트가 배정된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 에이전트가 배정된 경우 통화를 처리한 에이전트 |
| `summary` | string \| null | 에이전트에서 **통화 요약 생성**을 켠 경우의 완료된 [통화 요약](/ko/webhooks/call-complete)입니다. 따라서 늦은 추출 이벤트도 완료 시간 제한을 놓친 요약을 제공합니다. 그 외의 경우에는 `null`입니다. 요약은 있지만 추출 필드가 없는 에이전트는 이 이벤트를 절대 발생시키지 않습니다. 늦은 요약은 `GET /v1/calls/{call_id}`에서 읽으십시오. |
| `extracted_data.status` | string | 이 이벤트에서는 항상 `completed`입니다. |
| `extracted_data.fields` | object | 구성된 추출 필드 키를 기준으로 하는 값입니다. 사용할 수 없는 값은 `null`입니다. |
| `extracted_data.evidence` | object | 추출 필드를 기준으로 하는 증거입니다. null이 아닌 값에는 정확히 구조적으로 검증된 인용문(최대 1,000자), `speaker_role`(`caller` 또는 `agent`), `turn_index`가 포함됩니다. 모델이 반환한 더 긴 인용문은 잘리지 않고 거부되며, 해당 필드가 `null`이면 증거도 `null`입니다. |
| `extracted_data.verification` | string | 독립적인 증거 검증 단계가 모든 후보 필드에 대해 정확히 하나의 유효한 판정을 반환한 경우에만 `verified`입니다. `unavailable`은 검증 단계가 실패했거나, 시간 초과되었거나, 예산이 부족했거나, 잘못된 형식 또는 부분 출력을 반환했음을 의미합니다. 전체 검증 단계를 사용할 수 없는 경우에도 고객 검토를 위해 구조적 근거가 있는 값을 유지합니다. 부분 출력의 경우 유효한 판정이 적용되며, 정확히 하나의 유효한 판정이 없는 각 후보는 null 처리됩니다. |
| `extracted_data.field_reasons` | object | 구조적 근거 검증 또는 독립 검증기에 의해 null 처리된 필드를 기준으로 하는 사유입니다. |
| `extracted_data.schema_version` | string | 이 추출에 사용된 정확한 필드 스키마의 해시입니다. |
| `extracted_at` | timestamp | ISO 8601 완료 시간입니다. |
| `model` | string | 추출에 사용된 모델입니다. |

### `campaign.completed`

캠페인의 일정이 종료되었거나 모든 연락처가 최종 상태에 도달하여 캠페인이 `running`에서
`completed`로 전환될 때 한 번 전송됩니다. 실행기 재시도는 다른 이벤트를 전송하지
않습니다. 이 조직 수준 수명 주기 이벤트는 에이전트 범위 엔드포인트가 아닌 조직 범위
엔드포인트에만 전달됩니다. 차단하지 않습니다.

```json
{
  "data": {
    "campaign_id": "3f6b2c9e-2a0d-4c63-b6d6-a708dc98f403",
    "name": "May win-back",
    "agent_id": 12,
    "status": "completed",
    "started_at": "2026-04-20T17:00:00Z",
    "completed_at": "2026-04-20T18:25:11Z",
    "counts": {
      "contacts_total": 150,
      "completed": 121,
      "failed": 11,
      "no_answer": 18,
      "remaining": 0
    }
  },
  "event_id": "8d8f52ce-6b46-423f-9dde-cea0b91ec135",
  "type": "campaign.completed"
}
```

네 가지 결과 수는 서로 겹치지 않으며 합계는 `contacts_total`과 같습니다.
`completed`에는 성공한 연락처가 포함됩니다. `no_answer`에는 최종 결과가 응답 없음인
최종 실패 또는 소진 상태의 연락처가 포함됩니다. `failed`에는 그 외 모든 최종 실패 또는
소진 상태의 연락처가 포함됩니다. `remaining`에는 대기 중, 예약됨 또는 현재 통화 중인
연락처가 포함됩니다. 가장 최근 시도가 응답 없음이었더라도 재시도를 기다리는 연락처는
`remaining`입니다. 한 번만 생성되는 완료 스냅샷 전에 진행 중인 통화가 조정됩니다.
`started_at`은 구성된 캠페인 시작 시간이며, 시작 시간이 구성되지 않은 경우 캠페인 생성
시간입니다.

### `issue.reported`

[이슈 보고서](/api-reference/issue-reports)가 생성될 때 전송됩니다. 즉, 대시보드에서
사용자가 제출한 경우(`source: "user"`) 또는 통화 평가가 자동으로 생성한 경우
(`source: "system"`)입니다. 차단하지 않습니다.

```json
{
  "data": {
    "call_id": 987654321,
    "issue_report": {
      "created_at": "2026-04-20T18:25:11.002Z",
      "description": "Five-second silence before responding to the main question.",
      "id": 4321,
      "severity": "warning",
      "source": "system",
      "status": "open",
      "title": "Agent paused too long"
    }
  },
  "event_id": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
  "type": "issue.reported"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 배정된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 배정된 경우 통화를 처리한 에이전트 |
| `issue_report.severity` | string | `critical`, `warning` 또는 `info` |
| `issue_report.status` | string | `open` 또는 `resolved` |
| `issue_report.source` | string | `user`(대시보드에서 제출) 또는 `system`(평가로 생성) |

<Note>
  통화를 재평가하면 시스템에서 생성한 이슈 보고서가 다시 생성되며, 생성된 보고서마다
  `issue.reported`가 다시 전송됩니다. 근본 문제마다 알림을 하나만 받으려면
  `call_id` + `title`을 기준으로 중복을 제거하십시오.
</Note>

### `issue.escalated`

이슈 패턴이 직원 검토를 위해 ThunderPhone으로 전송될 때 전송됩니다.
**ThunderPhone에 보고** 후 또는 Fix with AI가 고객 측 해결을 확인하지 못해 이슈를
자동으로 라우팅하는 경우에 발생합니다. 이 이벤트는 조직 전체 엔드포인트만 수신합니다.

```json
{
  "data": {
    "automatic": false,
    "cluster_id": "7ac2844c-2df0-4fa8-a560-7378da649e19",
    "escalation_id": "ec99f52b-c8c0-41dd-a4f2-dd8a07b10894",
    "status": "open"
  },
  "event_id": "d8f8f420-7a42-45ba-bcf1-b747f9bbecda",
  "type": "issue.escalated"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `automatic` | boolean | 자동 에스컬레이션은 `true`, 수동 에스컬레이션은 `false` |
| `cluster_id` | UUID | 에스컬레이션된 이슈 패턴 |
| `escalation_id` | UUID | 에스컬레이션 기록 |
| `status` | string | 이벤트 전송 시 `open` |

이것은 근거 묶음이 아니라 알림입니다. `cluster_id`를 사용하여 이슈 패턴과 연결하십시오.
[ThunderPhone에 보고](/ko/guides/issues)를 참조하십시오.

---

## 테스트 통화 이벤트

### `test-call.completed`

[test-call 실행](/api-reference/test-calls#test-call-run-object)이
통화가 시작되지 않아 실패한 실행을 포함하여 `completed` 또는 `failed`의
최종 상태에 도달하면 전송됩니다. 차단하지 않습니다. 일괄 CI 실행을
채팅/알림 시스템에 연결하는 데 유용합니다.

```json
{
  "data": {
    "test_call_run": {
      "call_id": 987654321,
      "completed_at": "2026-04-20T18:25:04.822Z",
      "error_message": "",
      "id": 7110,
      "status": "completed",
      "target_id": 12,
      "target_type": "agent"
    }
  },
  "event_id": "2b3c4d5e-6f7a-4b8c-9d0e-1f2a3b4c5d6e",
  "type": "test-call.completed"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `agent_id` | integer \| null | 할당된 경우 통화를 처리한 에이전트 |
| `agent_name` | string \| null | 할당된 경우 통화를 처리한 에이전트 |
| `test_call_run.target_type` | string | `agent` 또는 `phone_number` |
| `test_call_run.target_id` | integer | `target_type`과 일치하는, 실행 대상 에이전트 ID 또는 전화번호 ID |
| `test_call_run.status` | string | `completed` 또는 `failed` |
| `test_call_run.call_id` | integer \| null | 통화가 시작되기 전에 실행이 실패한 경우 `null` |
| `test_call_run.error_message` | string | 성공 시 비어 있음 |

---

## 알림 이벤트

### `alert.triggered`

**개발자 웹훅으로 전달** 채널이 활성화된 [알림 규칙](/ko/guides/alerts)이
임계값을 넘으면 전송됩니다. 차단하지 않습니다. 규칙은 한 번 발생한
후 쿨다운을 준수하므로, 지속적인 위반은 쿨다운 기간당 하나의 이벤트를
생성합니다.

```json
{
  "data": {
    "comparator": "lt",
    "event_id": "b8e6a1d4-2c3f-4a5b-9c8d-7e6f5a4b3c2d",
    "fired_at": "2026-04-20T18:00:00+00:00",
    "metric": "success_rate",
    "metric_value": 71.4,
    "rule_id": "d2c3b4a5-6f7e-4d8c-9b0a-1c2d3e4f5a6b",
    "rule_name": "Success rate below 80%",
    "threshold": 80.0,
    "window_hours": 24
  },
  "event_id": "4d5e6f7a-8b9c-4d0e-9f1a-2b3c4d5e6f7a",
  "type": "alert.triggered"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `event_id` (`data` 내) | UUID | 알림 **발생** ID — 엔벨로프의 전달 `event_id`와 다릅니다 |
| `rule_id`, `rule_name` | UUID, string | 발생한 규칙 |
| `metric` | string | `success_rate`, `failure_rate`, `avg_score`, `call_volume` 또는 `suite_regression` |
| `comparator` | string | `lt`, `lte`, `gt` 또는 `gte` |
| `metric_value` | number | 규칙이 발생했을 때 평가 기간 내 메트릭 값 |
| `threshold` | number | 구성된 임계값 |
| `window_hours` | integer | 후행 평가 기간 |
| `fired_at` | timestamp | |

규칙, 메트릭, 쿨다운 및 이메일 / Slack 채널을 만드는 방법은
[알림 가이드](/ko/guides/alerts)를 참조하세요.

---

## 마이그레이션 이벤트

### `migration.call.complete`

[`POST /v1/migrations/{id}/calls`](/api-reference/migration-calls)를 통해 발신한 통화가
어느 플랫폼에서 처리되었는지와 관계없이 종료되면 전송됩니다. 비차단 방식입니다. 통화가 종료된 후
최대 약 5분 내에 도착합니다. `data`에는
[`telephony.complete`](/ko/webhooks/call-complete)의 모든 필드와 `migration` 블록이 포함됩니다.
ThunderPhone에서 발신한 통화는 동일한 `call_id`와 함께 평소와 같이 `telephony.complete`도 전송합니다.
다른 플랫폼의 통화가 2시간 후에도 종료되지 않았거나, 마이그레이션이 먼저 완료되었거나 키를
잃은 경우에는 `status: "unknown"` 및 트랜스크립트 없이 전송됩니다.

```json
{
  "data": {
    "agent_id": 42,
    "agent_name": "Front desk",
    "billable_minutes": null,
    "billing_total_cents": null,
    "call_id": null,
    "direction": "outbound",
    "duration_seconds": 94,
    "end_reason": "user_hangup",
    "end_time": "2026-10-06T17:01:34+00:00",
    "extracted_data": null,
    "from_number": "+15551234567",
    "migration": {
      "arm": "vendor",
      "id": "5f0c2a9e-7b1d-4c3e-8a6f-2d9e1b4c7a30",
      "metadata": {
        "lead_id": "8812"
      },
      "routing_id": "9b2f8c1e-4d3a-4f6b-8e2d-1a7c5b9e0f34",
      "vendor": "retell",
      "vendor_call_id": "call_6a1f0e",
      "vendor_cost_usd": "0.1240"
    },
    "product": "",
    "recording_mode": "vendor",
    "recording_url": "https://…",
    "start_time": "2026-10-06T17:00:00+00:00",
    "status": "completed",
    "summary": "The callee booked a table for two on Thursday.",
    "thunderphone_retained_call_content": true,
    "to_number": "+14155552671",
    "transcripts": [
      {
        "content": "Hi Ada, this is Rosa's calling about your booking.",
        "role": "agent"
      },
      {
        "content": "Yes, Thursday works.",
        "role": "user"
      }
    ],
    "transfer_number": null,
    "unresolved_variables": [],
    "variables": {
      "first_name": "Ada"
    },
    "voice": ""
  },
  "event_id": "7c1e2f3a-4b5c-4d6e-8f7a-9b0c1d2e3f4a",
  "type": "migration.call.complete"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `migration.id` | UUID | 마이그레이션 |
| `migration.routing_id` | UUID | 통화를 발신할 때 반환된 라우팅된 통화 |
| `migration.arm` | string | `ours`(ThunderPhone에서 발신) 또는 `vendor`(다른 플랫폼에서 발신) |
| `migration.vendor` | string | `retell`, `vapi`, `elevenlabs` 또는 `bland` |
| `migration.vendor_call_id` | string \| null | 다른 플랫폼의 통화 ID이며, `ours`의 경우 `null` |
| `migration.metadata` | object | 통화 발신 시 전송한 `metadata` |
| `migration.vendor_cost_usd` | string \| null | 다른 플랫폼이 제공한 경우 해당 플랫폼에서 보고한 통화 비용 |
| `call_id` | integer \| null | ThunderPhone 통화 ID이며, `vendor`의 경우 `null` |
| `status` | string | `vendor`의 경우: 수신자가 통화한 경우 `completed`, 통화하지 않은 경우 `failed`, 위에서 설명한 경우 `unknown`입니다. `ours`의 경우: `telephony.complete`와 동일합니다. |

`vendor` 통화의 경우 ThunderPhone에는 청구 또는 추출 데이터가 없습니다.
`billable_minutes`, `billing_total_cents`, `extracted_data`는 `null`이며,
`product`와 `voice`는 비어 있고, `recording_mode`는 `vendor`입니다.
`recording_url`은 다른 플랫폼에서 제공한 경우 해당 플랫폼의 링크입니다.

### `migration.rollback`

마이그레이션의 점진적 전환이 자동으로 모든 통화를 다른 플랫폼으로 되돌릴 때 전송됩니다. 다음 경우에 발생합니다. 15분 내 ThunderPhone 연결에 3번 실패한 경우, ThunderPhone으로 전송된 통화 2건이 에이전트가 응답하기 전에 종료된 경우, 또는 ThunderPhone의 작업 결과율이 다른 플랫폼보다 구성된 격차를 초과하여 낮아진 경우입니다. 롤백당 한 번 전송되며 차단하지 않습니다. 이후 ThunderPhone의 통화 비중은 0%가 되고, 누군가 에이전트의 마이그레이션 탭에서 재개할 때까지 마이그레이션이 일시 중지됩니다. 페이로드에는 전화번호, 대화 기록 또는 통화 ID가 포함되지 않습니다.

```json
{
  "data": {
    "agent_id": 42,
    "agent_name": "Front desk",
    "dropped": 0,
    "migration_id": "5f0c2a9e-7b1d-4c3e-8a6f-2d9e1b4c7a30",
    "ours": {
      "graded": 20,
      "success_rate": 0.8
    },
    "ours_failed": 3,
    "ours_percent": 0,
    "previous_percent": 25,
    "reason": "ours_failed_burst",
    "rolled_back_at": "2026-10-10T12:05:00+00:00",
    "step": 1,
    "theirs": {
      "graded": 20,
      "success_rate": 0.8
    },
    "vendor": "retell"
  },
  "event_id": "3d1f6b2a-8c4e-5a7b-9d0e-2f1a3b4c5d6e",
  "type": "migration.rollback"
}
```

| 필드 | 유형 | 설명 |
|-------|------|-------------|
| `migration_id` | UUID | 마이그레이션 |
| `vendor` | 문자열 | `retell`, `vapi`, `elevenlabs` 또는 `bland` |
| `reason` | 문자열 | `ours_failed_burst`(15분 내 연결 실패 3건), `dropped_calls`(30분 내 에이전트가 응답하기 전에 종료된 통화 2건) 또는 `rate_gap`(각 측에서 최소 `rollback_min_calls`건의 평가된 통화를 기준으로 한 작업 결과율 격차) |
| `previous_percent` | 정수 | 롤백 전 ThunderPhone의 통화 비중 |
| `ours_percent` | 정수 | 항상 `0` |
| `step` | 정수 \| null | 적용 중이던 일정 단계(0부터 시작) |
| `ours` / `theirs` | 객체 \| null | 현재 단계의 평가된 통화와 성공률입니다. 단계의 통화 수가 너무 적으면 `theirs`는 최근 30일 기준일 수 있습니다. 통화가 없으면 `null`입니다. |
| `ours_failed` | 정수 | 최근 15분 동안 집계된 연결 실패 수 |
| `dropped` | 정수 | 최근 30분 동안 중단으로 집계된 통화 수 |
| `rolled_back_at` | 타임스탬프 | |

---

## 관련 항목

<CardGroup cols={2}>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/ko/webhooks/call-incoming">
    반드시 응답해야 하는 차단형 수신 통화 페이로드입니다.
  </Card>
  <Card title="telephony.complete / web.complete" icon="phone-slash" href="/ko/webhooks/call-complete">
    통화 후 기록 및 메트릭입니다.
  </Card>
  <Card title="웹훅 엔드포인트" icon="bolt" href="/ko/webhooks/endpoints">
    이러한 이벤트의 일부를 URL로 구독합니다.
  </Card>
  <Card title="함수 도구" icon="screwdriver-wrench" href="/ko/tools/overview">
    `telephony.tool` / `web.tool` 이벤트가 생성되는 방식입니다.
  </Card>
</CardGroup>
