상담 콘솔 임베드 가이드
외부 서비스에 자체 상담 콘솔을 붙이는 엔드투엔드 레시피입니다. ZeroTalk의 Outbound 웹훅으로 이벤트를 받아 자신의 콘솔로 중계하고, Public API(PAT)로 대화 내역을 읽고 답장을 보냅니다.
아키텍처
PAT는 비밀 값이며 워크스페이스 전체를 읽고 쓸 수 있고, Public API는 CORS를 허용하지 않습니다. 따라서 BFF(Backend for Frontend) 를 반드시 두고, 브라우저는 BFF와만 통신합니다.
- ZeroTalk → BFF: 서명된 웹훅 이벤트 (수신)
- BFF → 콘솔: 자체 WebSocket/SSE로 중계
- BFF → ZeroTalk: PAT로 내역 조회 + 답장 전송
단계별 레시피
1. PAT 발급
대시보드 → Settings → Developers → Personal Access Tokens에서 토큰을 발급합니다 (인증 참고). 콘솔에 권장하는 scope 세트:
| Scope | 용도 |
|---|---|
messages:read + messages:write | 내역 조회 + 답장 전송 |
attachments:read (+ messages:read) | 첨부 다운로드(읽기) |
channels:read + channels:write | 채널 목록·상세 + 읽음/배정/종료 |
contacts:read | 고객 정보 표시 |
members:read | 담당자 매핑·표시 |
macros:read (선택) | 매크로(상용구) |
webhooks:read + webhooks:write (선택) | 웹훅 자가 등록·모니터링 |
쓰기 전용 토큰은 내역을 렌더할 수 없습니다. 반드시 read scope를 함께 부여하세요.
2. PAT를 BFF에 저장
토큰은 서버 사이드에만 보관합니다. 브라우저·콘솔 클라이언트에 절대 노출하지 마세요.
3. 웹훅 등록
BFF의 공개 HTTPS URL을 가리키는 웹훅을 POST /outbound-webhooks로 등록하고, 응답에서 1회만 반환되는 secret을 서버에 저장합니다. (대시보드 UI로 등록해도 됩니다.)
4. 이벤트 중계
수신한 각 이벤트를:
- 서명을 검증합니다 (raw body로 HMAC-SHA256,
sha256=접두사 비교). - 멱등 처리로 중복 제거합니다 (at-least-once이므로 같은 이벤트가 두 번 올 수 있음).
- 자체 WS/SSE로 콘솔에 중계합니다.
- UI는 수신 순서가 아니라 한 채널 안 메시지는
data.seq(채널 순번)로 정렬합니다 —data.created_at은message.created등 일부 이벤트에만 있고, 채널 간 전역 순서는 가정하지 마세요 (순서 보장).
5. 렌더
| 화면 | 엔드포인트 |
|---|---|
| 대화 목록 | GET /channels |
| 대화 내역 | GET /channels/{id}/messages?since=… |
| 고객 정보 | GET /contacts/{id} |
| 담당자("누가 처리 중") | 채널의 assigned_member_id + 웹훅 envelope의 assigned_member 스냅샷 |
6. 운영(답장·읽음·배정·종료)
메시징 쓰기 API로 대화를 조작합니다.
- 답장:
POST /channels/{id}/messages— 상담원 귀속은sender_member_id로, 생략하면 토큰의 integration 신원으로 귀속됩니다. - 읽음:
POST /channels/{id}/read - 배정:
POST /channels/{id}/assign— 행위자(actor_member_id)는channel:operate권한을 가져야 합니다. Viewer 역할 멤버로 매핑하면403 FORBIDDEN으로 거부됩니다. - 종료/재개:
POST /channels/{id}/close·/reopen
읽음·배정·종료/재개는 답장의 sender_member_id와 별개로 행위자를 가리키는 actor_member_id 본문 필드가 필수입니다(누락 시 400 INVALID_BODY). 정확한 요청 본문은 메시징 쓰기 API를 참고하세요.
7. 매크로(상용구)
GET /macros로 템플릿을 읽고, 변수 치환은 BFF에서 수행한 뒤 렌더된 텍스트를 POST /channels/{id}/messages로 전송합니다. 서버 측 "매크로 적용" 엔드포인트는 없습니다.
8. 에코 처리
전송한 답장은 message.created 웹훅으로 되돌아옵니다. 웹훅에는 client_request_id가 없으므로 전송 응답의 data.id 를 웹훅의 data.message_id와 대조해 자신의 에코를 식별하고 중복 렌더를 막으세요 (에코 correlation 참고).
9. 웹훅 헬스 모니터링
GET /outbound-webhooks/{id}의 status·failure_count·last_delivered_at·error_message를 주기적으로 폴링하거나 전달 로그를 확인합니다. 연속 10회 실패 시 웹훅이 status=error로 자동 비활성화되므로, error를 감지하면 원인을 고친 뒤 toggle로 재활성화하세요(failure_count가 리셋됩니다).
알려진 제약
- 첨부 전송 미지원 — 첨부 다운로드(읽기)는 되지만, 첨부를 보내는 기능은 없습니다 (Phase 3 예정). 본문은 text·markdown만 가능합니다.
- 리치 블록·버튼 전송 미지원 — text·markdown 외 메시지 형식은 보낼 수 없습니다.
- 내부 메모 송수신 미지원 — 상담원 내부 메모는 전송할 수 없고, 웹훅으로도 수신되지 않습니다 (필터링됨).
- 읽음 확인·타이핑·실시간 presence 이벤트 없음 — 동시 응대 충돌 UX는
assigned_member로 직접 구성하세요. - 1 PAT = 워크스페이스 전체 — 팀·채널 단위 scope가 없습니다. 콘솔을 자신의 하위 테넌트에게 재판매(resale)하는 격리 용도로는 적합하지 않습니다 (Phase 3 예정).
sender_member_id매핑 필요 — 상담원 귀속을 쓰려면 내 상담원을 ZeroTalk membership id에 매핑해야 합니다. 생략하면 토큰의integration신원으로 귀속됩니다.