웹훅 개요
Sursa는 중요한 변경마다 이벤트를 기록해요. 카드가 게시, 검증, 보관되거나, 누군가 공지를 읽거나, 질문에 답변하지 못한 경우 등이에요. 이벤트는 두 가지 방법으로 받을 수 있으며, 둘을 함께 써도 돼요.
| 웹훅 | 이벤트 피드 | |
|---|---|---|
| 방식 | Sursa가 이벤트가 생길 때마다 HTTPS 수신 주소로 보내요. | 원할 때 GET /v1/webhook-events를 읽어요. |
| 지연 시간 | 몇 초. | 폴링하는 주기만큼. |
| 필요한 것 | 공개된 HTTPS 수신 서버. | admin 역할과 webhooks:manage 스코프가 있는 API 키만. |
| 적합한 용도 | 변경에 대응하기: 알림, 동기화, 워크플로 시작. | 일괄 작업, 중단 후 따라잡기, 대조. |
둘 다 같은 이벤트를 같은 ID로 전달해요. 모든 이벤트는 웹훅의 구독 여부와 관계없이 피드에 30일 동안 보관돼요.
이벤트
{
"id": "evt_0123456789abcdef0123456789abcdef",
"type": "card.updated",
"created_at": "2026-10-24T10:00:03Z",
"org_id": 1,
"api_version": "1.60.0",
"sequence": 1042,
"object": {"type": "card", "id": "3f2c9a7e-5b1d-4c8e-9f0a-1b2c3d4e5f60"},
"payload": "full",
"data": {
"card": {"id": "3f2c9a7e-5b1d-4c8e-9f0a-1b2c3d4e5f60", "title": "Refund policy", "collection": "support", "version": 4},
"actor_id": 7,
"previous_attributes": {"title": "Refunds"}
},
"test": false
}
| 필드 | 의미 |
|---|---|
id | 이벤트 ID. evt_와 16진수 32자로 이루어져요. 모든 전송 시도와 피드에서 같아요. |
type | 일어난 일. <object>.<past-tense verb> 형식이에요. 예: card.updated, access_request.resolved. |
created_at | 변경이 일어난 시각(UTC). |
api_version | data가 따르는 스키마의 API 버전. |
object | 이벤트가 다루는 객체의 유형과 ID. |
data | 세부 정보. 형태는 유형에 따라 다르며 이벤트 카탈로그에 설명되어 있어요. |
data.previous_attributes | *.updated 이벤트에서 바뀐 필드의 이전 값. |
sequence, payload, test | 웹훅 전송에만 있어요. 웹훅의 전송 순서에서의 위치, 페이로드 모드, 테스트 이벤트 여부예요. |
data에는 객체를 읽을 수 있는 사람이라면 누구나 API로 볼 수 있는 정보만 담겨요. 접근 목록, 다른 사람의 권한, 숨겨진
필드는 절대 들어가지 않아요. 이벤트의 이유는 no_hits, restricted, index_failed 같은 짧은 중립 코드예요.
이벤트 유형
이벤트는 다음 영역을 다뤄요.
| 영역 | 예 |
|---|---|
| 카드 | card.created, card.updated, card.published, card.archived, card.deleted, card.indexed, card.index_failed |
| 검증 | card.verified, card.unverified, verification.expired, verification.requested |
| 구조 | collection.created, folder.updated |
| 협업 | comment.created, draft.publish_requested, announcement.sent, announcement.read |
| 답변 | question.unanswered, question.feedback_created, access_request.created |
| 사용자와 접근 권한 | user.created, user.deactivated, group.member_added, api_key.expiring |
| 소스와 가져오기 | source.sync_completed, import.sync_completed, import.document_failed |
| 작업 | job.completed, job.failed, export.ready |
| 웹훅 | webhook.endpoint_disabled, webhook.ping |
각 이벤트의 스키마와 예시를 담은 전체 목록은 API 레퍼런스의 이벤트 카탈로그에 있어요.
GET /v1/webhooks/catalog는 사용하는 설치 환경에서 쓸 수 있는 유형을 반환해요.
새 이벤트 유형은 계속 추가되고, 기존 이벤트에는 필드가 늘어나요. 이벤트 유형은 이름이 바뀌거나 제거되지 않아요. 수신 서버는 모르는 유형과 필드를 건너뛰어야 해요.
누가 무엇을 받는지
웹훅은 이벤트의 객체가 그 웹훅에 보일 때만 이벤트를 받아요. 기준은 API 키와 같아서 웹훅의 역할, 데이터 프로필,
컬렉션으로 정해져요. support 컬렉션으로 제한된 웹훅은 finance에 있는 카드에 관한 이벤트를 받지 않아요. 사용자, 키,
소스, 가져오기, 작업, 내보내기, 결제, 애플리케이션, 웹훅 자체에 관한 이벤트는 admin 역할의 웹훅으로만 가요.
그 범위 안에서 각 웹훅은 받을 이벤트를 골라요.
- 유형별로, 마스크를 사용해서: 모든 카드 이벤트는
card.*, 모든 이벤트는*. - 카드 이벤트는 컬렉션별, 태그별로.
웹훅의 페이로드 모드는 full(data가 있는 이벤트, 기본값) 또는 thin(data가 없는 이벤트. 수신 서버가 자체 키로
API를 통해 객체를 읽으므로 항상 현재 상태를 봐요)이에요.
전송 요약
- 이벤트를 JSON으로 담은 HTTPS
POST요청이며, 웹훅의 시크릿으로 만든 두 가지 형식의 서명이 붙어요. Standard Webhooks(webhook-signature)와 이전 형식인X-KB-Signature예요. - 10초 안에
2xx로 응답하면 성공이에요. - 실패하면 간격을 늘려 가며 거의 3일 동안 재시도해요.
429나503응답의Retry-After를 따르며,410응답을 받으면 웹훅이 비활성화되고, 10번 연속 실패하면 다시 시작할 때까지 웹훅이 일시 중지돼요. - 한 웹훅으로 가는 전송은 엄격하게 순서대로 보내요. 느린 웹훅이 다른 웹훅을 지연시키지 않아요.
- 같은 이벤트가 두 번 이상 도착할 수 있어요.
webhook-id값으로 중복을 걸러 내세요.
웹훅 수신하기에서 Node.js, Python, Go의 서명 검증을 포함한 완전한 수신 서버를 단계별로 살펴볼 수 있어요.
웹훅 관리
| 하려는 일 | 엔드포인트 |
|---|---|
| 웹훅을 만들고 시크릿 받기 | POST /v1/webhooks |
| 수신 주소, 이벤트, 상태 변경 | PATCH /v1/webhooks/{webhook_id} |
| 테스트 이벤트 보내기 | POST /v1/webhooks/{webhook_id}/test |
| 시크릿 교체 | POST /v1/webhooks/{webhook_id}/rotate-secret |
| 일시 중지된 웹훅 다시 시작 | POST /v1/webhooks/{webhook_id}/resume |
| 전송과 응답 확인 | GET /v1/webhooks/{webhook_id}/deliveries |
| 전송 다시 보내기 | POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver |
웹훅을 관리하려면 admin 역할과 webhooks:manage 스코프가 있는 키가 필요해요. 관리자는 관리자 화면에서도 관리할 수 있어요. 조직당
웹훅은 최대 20개예요. 웹훅은 조직의 요금제에 포함되는 기능이며, 포함되어 있지 않으면 이 엔드포인트들은 403 상태와
webhooks.feature_disabled 코드로 응답해요.