웹훅 구독하기
폴링 없이 주요 이벤트가 발생할 때 알림을 받으세요.
세션은 장시간 실행되는 상호작용입니다. 대부분의 실시간 상호작용은 SSE 이벤트 스트림을 통해 이루어지지만, "webhook"(웹훅)은 주요 상태 변경을 알려줍니다.
웹훅 이벤트는 전체 객체가 아닌 이벤트 type과 id를 반환합니다. 웹훅 이벤트를 수신하면 GET 호출로 객체를 직접 가져와야 합니다. 이렇게 하면 재시도 시 오래된 데이터가 전달되는 것을 방지하고 모든 전달을 작게 유지할 수 있습니다.
지원되는 이벤트 유형
이러한 이벤트 중 일부는 세션의 이벤트 스트림에 있는 대응 이벤트와 이름이 다릅니다. 예를 들어, 스트림의 session.status_idle 및 session.status_running은 session.status_idled 및 session.status_run_started 웹훅 이벤트에 해당합니다.
| 이벤트 | 트리거 |
|---|---|
session.status_run_started | 에이전트 실행이 시작되었습니다. 세션 상태가 running으로 전환될 때마다 트리거됩니다. |
session.status_idled | 에이전트가 입력을 기다리고 있습니다. 예를 들어 도구 권한 승인이나 새 사용자 메시지를 기다리는 경우입니다. |
session.budget_reached | 세션이 예산에 도달하여 일시 중지되었습니다. 설정한 각 예산 값에 대해 최대 한 번 발생하며, 예산을 변경하면 다시 활성화됩니다. |
session.status_rescheduled | 일시적인 오류가 발생하여 세션이 자동으로 재시도 중입니다. |
session.status_terminated | 복구할 수 없는 오류로 인해 또는 아카이브되어 세션이 종료되었습니다. |
session.thread_created | 새 멀티에이전트 스레드가 열렸습니다. 코디네이터가 호출한 추가 에이전트가 작업을 시작하거나, 세션의 어드바이저에게 자문을 구하는 중입니다. |
session.thread_idled | 멀티에이전트 상호작용의 에이전트가 입력을 기다리고 있습니다. |
session.thread_terminated | 멀티에이전트 스레드가 종료되었습니다. 스레드가 아카이브되었거나 재시도 횟수를 모두 소진했기 때문입니다. 코디네이터가 생성한 하위 스레드가 작업을 마치면 terminated가 아닌 idle 상태가 됩니다(어드바이저 스레드는 자문이 완료되면 종료됩니다). 하위 스레드에 대해서만 발생하며, 전체 세션 아카이브를 포함한 기본 스레드의 종료는 session.status_terminated로만 나타납니다. |
session.outcome_evaluation_ended | 단일 반복에 대한 결과 평가가 완료되었습니다. |
session.updated | 세션 속성이 변경되었습니다(예: 이름 또는 구성이 업데이트됨). |
session.deleted | 세션이 영구적으로 삭제되었습니다. 가져올 객체가 남아 있지 않으므로 이벤트 자체를 최종으로 취급하세요. |
엔드포인트 등록
Claude Console에서 Manage > Webhooks로 이동하세요.
웹훅 엔드포인트는 다음으로 구성됩니다.
- URL: 공개적으로 확인 가능한 호스트 이름을 가진 포트 443의 HTTPS여야 합니다.
- 이벤트 유형: 이 엔드포인트가 수신하는
data.type값의 목록입니다. 엔드포인트는 구독한 이벤트만 수신합니다. - 서명 시크릿: 생성 시 만들어지는
whsec_접두사가 붙은 32바이트 시크릿입니다. 한 번만 표시되므로 웹훅 전달을 검증할 수 있도록 안전하게 보관하세요.
서명 검증
모든 전달에는 webhook-id, webhook-timestamp, webhook-signature 헤더가 포함됩니다. SDK의 unwrap() 헬퍼를 사용하면 서명 검증과 이벤트 파싱을 한 번에 수행할 수 있습니다. 서명이 유효하지 않거나 페이로드가 5분 이상 지난 경우 예외를 발생시킵니다.
ANTHROPIC_WEBHOOK_SIGNING_KEY를 엔드포인트 생성 시 표시된 whsec_ 접두사가 붙은 시크릿으로 설정하세요.
from flask import Flask, request
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env
app = Flask(__name__)
@app.route("/webhook", methods=["POST"])
def webhook():
try:
# unwrap()은 서명이 유효하지 않거나 페이로드가 오래된 경우 예외를 발생시킵니다
event = client.beta.webhooks.unwrap(
request.get_data(as_text=True),
headers=dict(request.headers),
)
except Exception:
return "invalid signature", 400
if event.data.type == "session.status_idled":
print("session idled:", event.data.id)
# 다른 이벤트 유형 처리
return "", 200이벤트 처리
본문을 파싱하고 data.type에 따라 분기한 다음 ID로 리소스를 가져오세요. 확인 응답으로 임의의 2xx를 반환하세요. 그 외의 응답은 엔드포인트에 불리하게 집계됩니다. 3xx는 엔드포인트를 즉시 비활성화하며(리디렉션은 절대 따라가지 않음), 다른 실패는 재시도됩니다. 재시도 및 자동 비활성화 규칙은 전달 동작을 참조하세요.
모든 이벤트 페이로드는 이벤트 유형, 식별자, 이벤트 발생 시점의 타임스탬프를 포함하는 동일한 구조를 가집니다.
{
"type": "event",
"id": "whe_9d5c1f7e...",
"created_at": "2026-03-18T14:05:22Z",
"data": {
"type": "session.status_idled",
"id": "sesn_01XYZ...",
"organization_id": "8a3d2f1e-...",
"workspace_id": "c7b0e4d9-..."
}
}if event.data.type == "session.status_idled":
session = client.beta.sessions.retrieve(event.data.id)
notify_user(session)
return "", 204최상위 event.id는 전달별이 아닌 이벤트별로 고유합니다. 동일한 event.id를 두 번 수신했다면 재시도이므로 폐기해도 됩니다.
전달 동작
-
중복: 엔드포인트는 동일한 이벤트를 두 번 이상 수신할 수 있으며, 모든 시도는 동일한 최상위
event.id(webhook-id헤더와 동일한 값)를 전달합니다. 이 값으로 중복을 제거하세요. -
구독 범위: 이벤트는 발생 시점에 해당 유형을 구독 중인 엔드포인트에만 전달됩니다. 해당 유형을 구독한 엔드포인트가 없는 동안 발생한 이벤트는 절대 전달되지 않으며, 나중에 구독해도 소급 전달되지 않으므로 필요하기 전에 이벤트 유형을 구독하세요.
-
순서는 보장되지 않습니다. 이벤트는 발생한 순서대로 전달되지 않습니다. 결과가 먼저 생성되었더라도
session.status_idled가session.outcome_evaluation_ended보다 먼저 도착할 수 있으며, 동일한 리소스에 대해.deleted이벤트가.archived이벤트보다 먼저 도착할 수 있습니다. 이벤트 도착 순서가 아닌 가져온 리소스를 기준으로 상태를 관리하세요. -
재시도: 각 엔드포인트와 이벤트에 대해 Anthropic은 5초에서 120초 사이의 지터가 적용된 지수 백오프로 최대 세 번의 전달을 시도합니다(이 섹션 뒷부분에서 설명하는 자동 비활성화를 트리거하는 응답은 절대 재시도되지 않습니다). 모든 시도는 동일한
event.id를 전달합니다. 마지막 시도가 실패하면 이벤트는 폐기됩니다. 나중에 전달하기 위해 대기열에 저장되지 않으며 손실되었다는 신호도 없습니다. 웹훅은 영구적인 로그가 아니므로, 모든 전환을 관찰해야 한다면 API를 통해 리소스를 나열하거나 가져와서 조정하세요. -
타임스탬프:
webhook-timestamp헤더는 전달 시도가 서명될 때 기록되며 재시도마다 다시 생성되므로, 재시도가 SDK의 최신성 검사에서 거부되지 않습니다. 이는 이벤트가 아닌 전달 시도에 대한 시각입니다. 이벤트 발생 시점은 이벤트 페이로드의created_at을 사용하세요. -
자동 비활성화: 다음 세 가지 경우에 엔드포인트는 기계가 읽을 수 있는
disabled_reason과 함께 자동으로disabled로 설정됩니다.- 엔드포인트가
3xx응답을 반환하는 경우. 리디렉션은 절대 따라가지 않으며, 첫 번째 시도에서 즉시auto-disabled: endpoint URL returned a redirect (3xx)사유로 엔드포인트가 비활성화됩니다. 엔드포인트가 이동한 경우 Console에서 URL을 업데이트하고 엔드포인트를 다시 활성화하세요. - Anthropic이 연결할 때 엔드포인트의 URL이 비공개 IP 주소로 확인되는 경우.
auto-disabled: endpoint URL resolved to an invalid address사유로 엔드포인트가 즉시 비활성화됩니다. - 엔드포인트로의 전달이 지속적인 기간 동안 계속 실패하는 경우,
auto-disabled after sustained delivery failures사유로 비활성화됩니다. 트리거 기준은 전달 횟수가 아니라 엔드포인트가 중단 없이 실패한 기간입니다. 단 한 번의2xx로 기간이 초기화되므로, 불안정한 이벤트 하나로 엔드포인트가 비활성화되지는 않습니다.
세 가지 모두 되돌릴 수 있습니다. 문제를 해결한 후 Console에서 엔드포인트를 다시 활성화하세요. 엔드포인트가 비활성화된 동안 발생한 이벤트는 재전송되지 않습니다.
- 엔드포인트가
Was this page helpful?