이벤트
ZeroTalk Outbound Webhook은 9개 이벤트를 전송합니다. 등록 시 일부만 구독하도록 필터링할 수 있으며, 필터를 비우면 전체를 구독합니다. (PAT API로 등록할 때는 event_filters로 지정합니다.)
웹훅 이벤트는 고객 상담 채널(channel_type: user_chat)에서만 전송됩니다. 팀 채널 등 내부 채널의 이벤트는 전송되지 않습니다.
상담원의 내부 메모(sender_type가 internal인 이벤트)는 생성·수정·삭제 모두 웹훅으로 전송되지 않습니다.
이벤트 목록
메시지
| 이벤트 | 설명 |
|---|---|
message.created | 새 메시지 |
message.updated | 메시지 수정 |
message.deleted | 메시지 삭제 |
채널
고객 상담 채널(user_chat)의 상태 변화에만 발생합니다.
| 이벤트 | 설명 |
|---|---|
channel.created | 채널 생성 |
channel.closed | 채널 종료 |
channel.reopened | 종료된 채널 재개 |
channel.assigned | 담당자 배정/변경 |
channel.snoozed | 대기 상태로 전환 |
channel.snooze_expired | 대기 만료 |
페이로드 구조
웹훅 요청은 POST, Content-Type: application/json이며, 본문은 모든 이벤트가 동일한 envelope을 쓰는 JSON입니다.
{
"event_type": "message.created",
"workspace_id": "ws_abc123",
"timestamp": "2026-04-25T12:00:00.482Z",
"data": {
"message_id": "msg_def456",
"sender_id": "ct_xyz789",
"sender_type": "contact",
"sender_name": "방문자",
"content_type": "text",
"content": "안녕하세요",
"seq": 42,
"seq_timestamp": 1714032000000,
"created_at": "2026-04-25T12:00:00Z"
},
"channel": { "id": "ch_abc123" },
"contact": { "id": "ct_xyz789", "name": "방문자" }
}
| 필드 | 설명 |
|---|---|
event_type | 이벤트 종류 |
workspace_id | 발생 워크스페이스 ID |
timestamp | 웹훅 전송(봉투 생성) 시각 (RFC 3339, UTC). 이벤트 발생 시각이 아니며, 같은 이벤트의 재전송 간에는 고정됩니다 |
data | 이벤트별 본문 (키는 이벤트 종류에 따라 다름; 위는 message.created 예시) |
contact·channel·assigned_member·integration | 관련 리소스의 스냅샷. 해당 이벤트에 존재할 때만 포함됩니다(없으면 키 자체가 생략). 전체 필드는 아래 스냅샷 필드를 참고하세요 — 위 예시는 channel·contact만 일부 필드로 보여줍니다 |
모든 필드는 snake_case입니다 (event_type, workspace_id, channel_id 등).
data 키는 이벤트마다 다릅니다. 메시지 이벤트의 data.seq는 채널 내 메시지 순번(채널 단위로 증가, 워크스페이스 전역 순번 아님), data.seq_timestamp는 서버 발번 시각(밀리초)입니다. message.created의 data에는 위 예시 외에 attachments·metadata·seq_id·version도 있고(message.updated·message.deleted는 version만), 채널 이벤트는 모두 version(채널 집계 버전)을 싣습니다.
발생 시각 필드는 일부 이벤트에만 있습니다 — created_at은 message.created·channel.created, deleted_at은 message.deleted, closed_at은 channel.closed에만 있고, 나머지(message.updated·channel.reopened·channel.assigned·channel.snooze_expired)는 data에 발생 시각이 없습니다(channel.snoozed의 snoozed_until은 만료 예정 시각). 웹훅 전달 순서는 보장되지 않으니, 정렬·순서 처리는 전달의 순서 보장을 따르세요.
data에서 빠지는 식별자봉투(envelope)나 스냅샷에 이미 있는 식별자는 중복을 피하려고 data에서 제거됩니다 — workspace_id(항상 봉투에 있음), 그리고 해당 스냅샷이 포함될 때 channel_id·contact_id·assigned_member_id·new_assigned_member_id. 이 값들은 봉투나 스냅샷(channel.id·contact.id·assigned_member.id)에서 읽으세요. 따라서 채널 이벤트의 엔티티 ID는 data가 아니라 channel.id 스냅샷에 있습니다(메시지 이벤트는 data.message_id가 그대로 유지됩니다).
sender_type
메시지의 data.sender_type은 다음 값 중 하나입니다.
| 값 | 의미 |
|---|---|
member | 상담원 |
contact | 방문자·고객 |
system | 시스템 메시지 |
bot | 봇 |
workflow | 워크플로우 자동 발송 |
external | 외부 연동(공급자) 발송 |
integration | Public API(PAT)로 전송된 메시지 |
integration 발신
상담 콘솔이 Public API로 답장을 보내면(sender_member_id 없이) 그 메시지의 sender_type은 integration입니다. 상담원 쪽 UI에서는 member와 동일하게 상담원 측 메시지로 렌더링하고, 표시 이름은 토큰 이름을 사용하세요.
data.sender_type이 internal인 상담원 내부 메모는 웹훅으로 전송되지 않습니다 (위 표에 없는 이유).
이벤트별 data
data는 이벤트 종류마다 키가 다릅니다. 아래는 각 이벤트에서 실제로 받는 대표 필드입니다(위 data에서 빠지는 식별자 규칙 적용 후 기준).
페이로드에는 문서에 없는 내부용 필드가 함께 올 수 있습니다. 모르는 필드는 무시하고 필요한 키만 읽도록 구현하세요(전방 호환).
메시지
message.created
새로 생성된 메시지. content는 미리보기가 아니라 전체 본문입니다.
{
"message_id": "msg_def456",
"sender_id": "ct_xyz789",
"sender_type": "contact",
"sender_name": "방문자",
"content_type": "text",
"content": "안녕하세요, 환불 문의드립니다.",
"attachments": [
{
"id": "att_01",
"type": "image",
"url": "https://cdn.example.com/a.png",
"name": "screenshot.png",
"size": 20480,
"mime_type": "image/png",
"thumbnail_url": "https://cdn.example.com/a_thumb.png"
}
],
"seq": 42,
"seq_timestamp": 1714032000000,
"created_at": "2026-04-25T12:00:00Z",
"parent_id": "msg_def000"
}
sender_type: 가능한 값은 위 sender_type 표 참고content_type:text·markdown·image·file·card·button·video·audio·location·sticker·blocks등attachments: 첨부가 있을 때만 채워집니다parent_id: 답글(스레드)인 경우에만content_type이blocks이면 구조화 본문이blocks키로 추가됩니다
message.updated
메시지 내용이 수정됨. changes에 바뀐 필드가 담깁니다.
{
"message_id": "msg_def456",
"sender_id": "mbr_111",
"sender_type": "member",
"content_type": "text",
"content": "수정된 전체 본문입니다.",
"is_last_message": true,
"changes": { "content": "수정된 전체 본문입니다." }
}
message.deleted
메시지가 삭제됨. 본문(content) 없이 식별자와 시각만 옵니다.
{
"message_id": "msg_def456",
"deleted_at": "2026-04-25T12:05:00Z",
"is_last_message": false,
"actor_id": "mbr_111"
}
actor_id: 삭제를 수행한 주체(상담원 등). 시스템 삭제 등에서는 생략됩니다
채널
채널 이벤트의 data는 대체로 작습니다 — 채널의 이름·상태·담당자 같은 맥락은 envelope의 channel·contact·assigned_member 스냅샷에서 읽으세요. version은 채널의 수정 버전 번호로, 이벤트 순서 판단이나 멱등 처리에 활용할 수 있습니다.
channel.created
새 채널(상담)이 생성됨.
{
"id": "ch_abc123",
"channel_type": "user_chat",
"provider": "widget",
"status": "open",
"name": "지원 문의",
"is_private": false,
"tags": ["환불"],
"message_count": 0,
"assigned_type": "human",
"integration_id": "int_01",
"created_at": "2026-04-25T11:30:00Z"
}
assigned_type:human·ai·workflowintegration_id: 외부 채널(카카오·라인 등) 연동 채널일 때만
channel.closed
채널이 종료됨.
{
"closed_at": "2026-04-25T13:00:00Z",
"version": 5
}
channel.reopened
종료된 채널이 다시 열림. data에는 버전만 오고, 바뀐 상태는 channel 스냅샷(status: "open")에서 확인하세요.
{
"version": 6
}
channel.assigned
담당자가 배정·변경·해제됨.
{
"previous_member_id": "mbr_111",
"new_assigned_member_id": null,
"assigned_type": "ai",
"version": 8
}
previous_member_id: 직전 담당자(없었으면 생략)new_assigned_member_id: 새 담당자. 사람 담당자면assigned_member스냅샷의id로 전달되어 여기선 제거되고, **담당 해제나 AI 배정이면null**입니다assigned_type:human(사람 배정) ·ai(AI 배정). 담당 해제 시에는 생략됩니다
channel.snoozed
채널이 대기(스누즈) 상태로 전환됨.
{
"snoozed_until": "2026-04-26T09:00:00Z",
"snooze_reason": "고객 회신 대기",
"version": 4
}
snooze_reason: 사유를 설정한 경우에만
channel.snooze_expired
대기 기간이 만료되어 채널이 자동 재개됨. data에는 버전만 옵니다.
{
"version": 7
}
스냅샷 필드
각 스냅샷의 주요 필드입니다 (해당 이벤트에서만 채워집니다).
- contact:
id,external_id,name,is_name_auto_generated,email,phone,avatar_url,is_member,profile,created_at - channel:
id,channel_type,provider,status,name,description,is_private,contact_id,assigned_member_id,assigned_type,tags,message_count,metadata,created_at,integration_id(연동 채널만) - assigned_member:
id,name,role,avatar_url,status - integration:
id,provider,name