Outbound 웹훅 관리
PAT로 Outbound 웹훅(ZeroTalk → 내 서버)을 직접 등록하고 관리하는 엔드포인트입니다. 대시보드 없이 BFF에서 웹훅을 프로비저닝하고 상태를 모니터링할 수 있습니다.
- Outbound 웹훅 (이 페이지) — ZeroTalk가 내 서버로 이벤트를 보냅니다. 페이로드·서명·전달 규약은 웹훅 문서를 참고하세요.
- Inbound 웹훅 설정(
inbound-webhook-configs) — 외부 메신저 공급사(kakao·naver 등)가 ZeroTalk로 메시지를 전달하는 수신 설정입니다. 조회 전용이며 인바운드 웹훅 설정에 있습니다.
Base: /api/public/v1/outbound-webhooks · 인증: Authorization: Bearer ztpat_… · Scope: 조회는 webhooks:read, 변경은 webhooks:write.
엔드포인트 요약
| Method · Path | Scope | 용도 |
|---|---|---|
GET /outbound-webhooks | webhooks:read | 목록 (시크릿 미반환) |
POST /outbound-webhooks | webhooks:write | 생성 (평문 secret 1회 반환) |
GET /outbound-webhooks/{id} | webhooks:read | 단건 조회 (status·failure_count·last_delivered_at) |
PATCH /outbound-webhooks/{id} | webhooks:write | name·url·event_filters·match_filters 수정 (부분) |
DELETE /outbound-webhooks/{id} | webhooks:write | 삭제 |
POST /outbound-webhooks/{id}/regenerate-secret | webhooks:write | 시크릿 회전 (평문 1회 반환) |
POST /outbound-webhooks/{id}/toggle | webhooks:write | 활성/비활성 전환 (error 상태에서 재활성하면 failure_count 리셋) |
GET /outbound-webhooks/{id}/delivery-logs | webhooks:read | 최근 전달 시도 |
서명 secret은 생성과 시크릿 회전 응답에서만 평문으로 반환됩니다. 목록·단건 조회는 시크릿을 절대 반환하지 않습니다. 받은 즉시 서버에 저장하세요.
URL 제약
- HTTPS만 허용됩니다.
- 사설·예약 IP 대역은 차단됩니다. 등록 시점과 전달 시점 양쪽에서 SSRF·DNS rebinding을 검증합니다.
자세한 차단 규칙은 웹훅 설정과 같습니다.
목록 조회
GET /outbound-webhooks — 워크스페이스에 등록된 Outbound 웹훅을 모두 조회합니다.
- Scope:
webhooks:read
아래 요청은 웹훅 목록을 조회합니다.
curl "https://api.talk.zeroworks.ai/api/public/v1/outbound-webhooks" \
-H "Authorization: Bearer ztpat_..."
응답
{
"success": true,
"data": [
{
"id": "wh-1a2b-...",
"name": "My BFF webhook",
"url": "https://bff.example.com/zerotalk/webhook",
"event_filters": ["message.created", "channel.assigned"],
"match_filters": {},
"status": "active",
"failure_count": 0,
"last_delivered_at": "2026-06-02T01:23:45Z",
"created_at": "2026-06-01T00:00:00Z",
"updated_at": "2026-06-02T01:23:45Z"
}
]
}
last_delivered_at은 전달 이력이 없으면, error_message는 직전 전달이 성공했거나 이력이 없으면 null이 아니라 키가 생략됩니다. secret은 읽기 경로에서 항상 생략됩니다(생성·회전 응답에서만 1회 노출). 이 목록에는 페이지네이션이 없습니다.
생성
POST /outbound-webhooks
- Scope:
webhooks:write - Body
| 필드 | 타입 | 설명 |
|---|---|---|
name | string | 웹훅 이름 (필수, 1–255자) |
url | string | 수신 HTTPS URL (필수, 최대 2048자) |
event_filters | string[] · 선택 | 구독할 이벤트 목록. 비우면 전체 구독 |
match_filters | object · 선택 | 이벤트를 받을 상담의 조건. 생략하거나 {}이면 조건 없이 모두 받습니다 (전달 조건) |
아래 요청은 message.created와 channel.assigned 이벤트를 받는 웹훅을 만듭니다.
curl -X POST "https://api.talk.zeroworks.ai/api/public/v1/outbound-webhooks" \
-H "Authorization: Bearer ztpat_..." \
-H "Content-Type: application/json" \
-d '{
"name": "My BFF webhook",
"url": "https://bff.example.com/zerotalk/webhook",
"event_filters": ["message.created", "channel.assigned"]
}'
응답 — 201. secret은 이 응답에서만 평문으로 반환됩니다.
{
"success": true,
"data": {
"id": "wh-1a2b-...",
"name": "My BFF webhook",
"url": "https://bff.example.com/zerotalk/webhook",
"event_filters": ["message.created", "channel.assigned"],
"match_filters": {},
"status": "active",
"secret": "9f8e7d...64자hex",
"failure_count": 0,
"created_at": "2026-06-01T00:00:00Z",
"updated_at": "2026-06-01T00:00:00Z"
}
}
생성 오류는 다음과 같습니다.
| 코드 | 발생 조건 | 대응 |
|---|---|---|
422 VALIDATION_FAILED | name·url 누락, 길이 초과, URL 형식 오류, match_filters의 알 수 없는 축이나 허용되지 않는 값 | error.details의 필드별 사유를 보고 고칩니다 |
403 WEBHOOK_LIMIT_EXCEEDED | 현재 플랜에서 만들 수 있는 웹훅 수를 넘음 | 쓰지 않는 웹훅을 삭제합니다 |
단건 조회
GET /outbound-webhooks/{id}
- Scope:
webhooks:read - 모니터링에 쓰는
status·failure_count·last_delivered_at을 포함하며, 직전 전달이 실패했을 때만error_message가 추가됩니다 (시크릿 제외).
아래 요청은 웹훅 한 건의 상태를 조회합니다.
curl "https://api.talk.zeroworks.ai/api/public/v1/outbound-webhooks/wh-1a2b-..." \
-H "Authorization: Bearer ztpat_..."
응답
{
"success": true,
"data": {
"id": "wh-1a2b-...",
"name": "My BFF webhook",
"url": "https://bff.example.com/zerotalk/webhook",
"event_filters": ["message.created", "channel.assigned"],
"match_filters": {},
"status": "active",
"failure_count": 0,
"last_delivered_at": "2026-06-02T01:23:45Z",
"created_at": "2026-06-01T00:00:00Z",
"updated_at": "2026-06-02T01:23:45Z"
}
}
수정
PATCH /outbound-webhooks/{id} — name·url·event_filters·match_filters를 부분 수정합니다 (보낸 필드만 변경).
- Scope:
webhooks:write
아래 요청은 이름과 구독 이벤트만 바꿉니다.
curl -X PATCH "https://api.talk.zeroworks.ai/api/public/v1/outbound-webhooks/wh-1a2b-..." \
-H "Authorization: Bearer ztpat_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Renamed webhook", "event_filters": ["message.created"] }'
event_filters를 생략하면 구독은 그대로 유지됩니다.[](빈 배열)을 보내면 모든 필터가 초기화되어 전체 이벤트 구독으로 바뀝니다(생성 시 빈 필터와 같습니다). 이벤트 수신을 멈추려면 빈 필터를 보내지 말고 활성/비활성 전환으로 웹훅을 비활성화하세요.match_filters를 생략하거나null로 보내면 저장된 조건이 그대로 유지됩니다.{}를 보내면 조건이 모두 지워져 모든 이벤트를 받습니다.
전달 조건
match_filters는 event_filters(이벤트 종류)와 별개로, 어떤 상담에서 난 이벤트를 받을지 좁히는 조건입니다. 대시보드 웹훅 설정의 「전달 조건」과 같습니다. 키는 조건 이름(축)이고 값은 문자열 배열입니다. 한 축 안의 값은 하나라도 맞으면 통과하고(OR), 여러 축은 모두 맞아야 통과합니다(AND). 빈 배열인 축은 조건이 없는 것으로 봅니다.
아래 요청은 사람 상담원이 담당하는 상담에서 고객이 보낸 메시지 이벤트만 받도록 설정합니다.
curl -X PATCH "https://api.talk.zeroworks.ai/api/public/v1/outbound-webhooks/wh-1a2b-..." \
-H "Authorization: Bearer ztpat_..." \
-H "Content-Type: application/json" \
-d '{ "match_filters": { "channel.assigned_type": ["human"], "message.sender_type": ["contact"] } }'
| 축 | 허용 값 |
|---|---|
channel.provider | 채널 종류 코드 (예: kakao, naver, web, native) |
channel.tag | 상담 태그 이름 |
channel.assigned_type | ai · human |
channel.assigned_member_id | 멤버 UUID, 또는 담당자가 없는 상담을 뜻하는 unassigned |
message.sender_type | member · contact · system · bot · workflow · external · integration |
- 축은 최대 10개, 축마다 값은 최대 50개, 값 하나는 200자 이하입니다. 빈 문자열은 허용되지 않습니다.
channel.provider·channel.tag는 대소문자를 구분하지 않고 비교합니다.channel.assigned_type·message.sender_type은 위 표의 철자 그대로 보내야 합니다.message.sender_type은 메시지 이벤트에만 적용됩니다. 채널 이벤트에는 발신자가 없으므로 이 조건과 무관하게 전달됩니다.- 아직 태그가 없는 상담의 이벤트는
channel.tag조건과 무관하게 전달됩니다. 봇이 응대하는 구간의 첫 메시지 대부분이 여기에 해당합니다. channel.tag는 태그 이름으로 비교하므로, 태그 이름을 바꾸면 이전 이름으로 건 조건은 더 이상 맞지 않습니다.unassigned는 AI가 담당하는 상담을 포함하지 않습니다.channel.assigned_member_id가["unassigned"]뿐이면channel.assigned_type과 함께 보낼 수 없습니다. 둘을 모두 만족하는 상담이 없기 때문입니다.
삭제
DELETE /outbound-webhooks/{id}
- Scope:
webhooks:write - 응답:
204 No Content
시크릿 회전
POST /outbound-webhooks/{id}/regenerate-secret — 서명 시크릿을 회전합니다.
- Scope:
webhooks:write - 새 시크릿은 응답에서만 평문으로 반환됩니다. 서버 측 grace 기간이 없으므로, 회전 직후 짧은 시간 동안 옛 시크릿으로 서명된 요청이 도착할 수 있습니다 (서명 검증 참고).
{ "success": true, "data": { "id": "wh-1a2b-...", "secret": "a1b2c3...64자hex" } }
활성/비활성 전환
POST /outbound-webhooks/{id}/toggle — 웹훅을 활성/비활성으로 전환합니다.
- Scope:
webhooks:write - 웹훅이 연속 실패로
status=error가 되어 자동 비활성화된 경우, toggle로 재활성화하면failure_count가 리셋되고 다시active가 됩니다 (자동 비활성화 규칙은 전달 참고). - 재활성화하면 비활성 기간에 보류된 미전달분이 이벤트 접수 시점부터 24시간 윈도우 내에서 전달됩니다.
{ "success": true, "data": { "id": "wh-1a2b-...", "status": "active", "failure_count": 0 } }
전달 로그
GET /outbound-webhooks/{id}/delivery-logs — 최근 전달 시도(성공 여부·HTTP 상태·에러·소요 시간)를 조회합니다.
- Scope:
webhooks:read - 쿼리:
limit(기본 50, 최대 100),offset(기본 0, 최대 9,999) — offset 기반 페이징입니다. 응답은data.items·data.total·data.has_more입니다. limit이 100을 넘으면 오류 없이 100으로 보정합니다.limit이 양의 정수가 아니면400 INVALID_LIMIT,offset이 0 이상 9,999 이하의 정수가 아니면400 INVALID_OFFSET을 반환합니다.- 페이지를 시작할 수 있는
offset은 9,999까지입니다. 그 안에서 시작한 페이지는 요청한limit만큼 돌려주므로 마지막 페이지가 10,000번째 이후의 로그를 포함할 수 있습니다.data.has_more는 다음 페이지의offset이 9,999 이하일 때만true이므로,has_more를 따라 페이지를 넘기면 이 범위 끝에서 정상적으로 멈춥니다.
{
"success": true,
"data": {
"items": [
{
"id": 1,
"webhook_id": "wh-1a2b-...",
"event_type": "message.created",
"success": true,
"http_status": 200,
"duration_ms": 142,
"created_at": "2026-06-02T01:23:45Z"
}
],
"total": 1,
"has_more": false
}
}
success(불리언)는 각 항목에 항상 포함되는 성공 여부 판별 필드입니다. http_status·error_message는 값이 없으면 null이 아니라 키가 생략됩니다. 성공한 시도(success: true)는 error_message가 빠지고, HTTP 응답을 받지 못한 시도는 http_status가 빠집니다.
참고
- 페이로드·서명·재시도·멱등 등 수신 측 처리 규약은 웹훅 문서를 참고하세요.
- 콘솔 전체 흐름(웹훅 자가 등록 → 수신 → 렌더 → 답장)은 상담 콘솔 임베드 가이드에 있습니다.