웹훅
Usher는 환경에서 일어나는 일을 실시간으로 서버에 알려 줄 수 있습니다. 누군가 가입하거나, 로그인하거나, 정보를 바꾸거나, 탈퇴할 때입니다. 각 이벤트는 지정한 주소로 보내는 서명된 POST 요청이며, 서버가 응답하지 않으면 다시 시도합니다.
엔드포인트 추가하기
Usher Console에서 환경을 열고 웹훅 → 새 엔드포인트로 이동하세요. 소유한 https:// 주소를 입력하고, 받을 이벤트를 선택합니다. Console은 엔드포인트의 서명 시크릿(whsec_…)을 한 번만 보여 줍니다. 서버의 다른 시크릿과 함께 보관하세요.
| 이벤트 | 발생 시점 |
|---|---|
user.created | 누군가 계정을 만들었을 때 |
user.updated | 계정이 바뀌었을 때: 이메일, 이름, 확인 상태 또는 차단(SCIM을 통한 조직 디렉터리의 차단 포함) |
user.deleted | 계정이 삭제되었을 때 |
session.created | 누군가 로그인했을 때 |
session.ended | 세션이 끝났을 때: 로그아웃, 폐기 또는 조기 만료 |
테스트 보내기를 누르면 구독한 이벤트와 관계없이 그 엔드포인트에만 webhook.test 이벤트를 보냅니다.
전달되는 내용
POST /usher/webhooks HTTP/1.1
content-type: application/json
user-agent: Usher-Webhooks/1
webhook-id: msg_2fk7x…
webhook-timestamp: 1793643116
webhook-signature: v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE={
"type": "user.created",
"timestamp": "2026-11-02T18:11:56.412Z",
"data": {
"user": {
"id": "Qz3m…",
"email": "lena@acme.com",
"emailVerified": false,
"name": "Lena Live",
"image": null,
"banned": false,
"createdAt": "2026-11-02T18:11:56.380Z",
"updatedAt": "2026-11-02T18:11:56.380Z"
}
}
}이벤트는 사용자와 세션을 설명할 뿐, 시크릿은 절대 담지 않습니다. 토큰, 암호 해시, 역할, 차단 사유는 포함되지 않습니다.
모든 전달 확인하기
Usher는 Standard Webhooks 사양에 따라 서명하므로, 이 사양의 어떤 라이브러리로든 서명을 확인할 수 있습니다. 검증되지 않는 요청은 모두 거부하세요. 라이브러리는 타임스탬프가 5분 넘게 차이 나는 요청도 거부하므로, 오래된 전달을 재전송할 수 없습니다.
// a route that receives Usher's webhooks (Node, the standardwebhooks package)
import { Webhook } from 'standardwebhooks'
const wh = new Webhook(process.env.USHER_WEBHOOK_SECRET!) // whsec_…, from Usher Console
export async function POST(request: Request) {
const body = await request.text() // the raw body: the signature covers it byte for byte
let event: { type: string; timestamp: string; data: Record<string, unknown> }
try {
event = wh.verify(body, Object.fromEntries(request.headers)) as typeof event
} catch {
return new Response('bad signature', { status: 400 })
}
// webhook-id is the same on every attempt: skip one you have handled already
if (await alreadyHandled(request.headers.get('webhook-id')!)) return new Response(null, { status: 204 })
await queue(event) // answer quickly; do the work afterwards
return new Response(null, { status: 204 })
}- 15초 이내에 아무
2xx코드로 응답하세요. 리디렉션을 포함해 그 밖의 응답은 모두 실패로 간주됩니다. - 중복을 예상하세요. 메시지는 모든 시도에서 같은
webhook-id를 유지합니다. 이미 처리한 메시지는 건너뛰세요. - 순서에 의존하지 마세요. 재시도된 메시지가 더 최근 이벤트보다 늦게 도착할 수 있습니다.
서버가 응답하지 않을 때
Usher는 5초, 5분, 30분, 2시간, 5시간, 10시간, 10시간 뒤에 다시 시도하고(총 약 하루 반), 그다음에는 포기합니다. Console의 전달 로그에서 모든 메시지의 시도 기록과 서버의 마지막 응답을 확인할 수 있으며, 다시 보내기로 전달을 처음부터 다시 시작할 수 있습니다.
새 시크릿
Console에서 새 시크릿을 누르면 시크릿이 하나 더 만들어지고 한 번만 표시됩니다. 이후 24시간 동안 각 전달에는 시크릿마다 하나씩 두 개의 서명이 담기므로, 아무것도 놓치지 않고 새 시크릿을 배포할 수 있습니다. 그 뒤로는 새 시크릿으로만 서명합니다.
전달이 나가는 곳
웹훅은 Usher의 이그레스 프록시를 거쳐 나가며, 이 프록시는 사설 주소나 내부 주소에 연결하지 않습니다. 엔드포인트는 공개 이름을 쓰는 https:// 주소여야 합니다. IP 주소, 사설 이름, Usher 자체 도메인의 주소는 사용할 수 없습니다.