지식 베이스
웹훅
폴링하게 하는 대신, 메일이 도착하면 사용자의 엔드포인트에 알려 줍니다.
세부 사항
- 오늘부터 Settings → Webhooks와 API로 사용할 수 있습니다. https 엔드포인트를 등록하고, 스무 가지 이벤트 중 어떤 것을 받을지 고른 다음, whsec_ 서명 비밀 값을 복사하세요. 이 값은 생성 시점과 교체 시점에만 표시되고 그 뒤로는 다시 볼 수 없습니다. 전달은 API 호출이 아니라 메일함 자체가 일으키는 실제 서명된 POST이므로, 수신 메일과 열람 및 클릭에 대해 메시지를 무엇으로 보냈든 발생합니다. 발송 이벤트는 모든 경로에서 발생하는데, 예전에는 일부에서만 발생했습니다. API, MCP, 템플릿 또는 규칙을 통한 발송은 email.sent를 일으켰지만 앱의 작성 창에서 보낸 메시지는 그렇지 않았습니다. 작성 창은 이벤트를 내보내는 발송 서비스를 거치지 않고 메일함에 직접 기록하기 때문입니다. 이제 이벤트는 모든 경로가 만나는 지점인 메일함 자체에서 발생하므로, 앱에서 작성하는 것과 화요일로 예약하는 것과 API로 요청하는 것은 같은 웹훅을 일으키는 세 가지 방법입니다. 지연 발송은 두 번 알립니다. 접수될 때 email.scheduled 또는 email.queued가, 실제로 나갈 때 email.sent가 발생하며, 그사이에 취소하면 email.cancelled가 발생합니다. 메일함당 엔드포인트는 10개이며, 이 화면에서만이 아니라 엔드포인트가 등록되는 모든 곳에서 적용됩니다.
- 이벤트는 세 갈래로 나뉩니다. 열다섯 개는 개별 메시지에 관한 것입니다. email.received, email.replied, email.sent, email.delivered, email.failed, email.cancelled, email.scheduled, email.queued(예약의 발송 취소 짝), email.delivery_delayed, email.bounced, email.complained, email.suppressed, email.opened, email.clicked, email.downloaded입니다. email.sent는 발송 서비스가 메시지를 수락했다는 뜻이고, email.delivered는 수신 서버가 수락했다는 뜻이며, email.delivery_delayed는 아직 도착하지 않아 재시도가 계속되고 있다는 뜻입니다. email.replied는 도착한 메시지가 메일함에 이미 있는 메시지에 답하는 경우 email.received와 함께 발생하므로, 둘 다 원하는 소비자는 둘 다 받습니다. email.downloaded는 다운로드 링크로 나간 파일을 사람이 내려받을 때 발생하며, 스캐너와 링크 미리보기를 집계에서 빼는 동일한 분류기가 적용됩니다. 또한 수신자를 지목하지 않는데, 링크가 메시지를 받은 모든 사람에게 동일하기 때문입니다. 세 개는 도메인에 관한 것입니다. 수신을 시작할 때의 domain.verified, 발송 판정이 바뀔 때의 domain.sending_changed, 그리고 사용자가 요청했든 7일 수거기가 미확인 상태로 정리했든 도메인이 제거될 때의 domain.deleted입니다. 두 개는 email.suppressed와는 다른 것인 억제 목록 자체에 관한 것입니다. 주소가 목록에 올라갈 때의 suppression.added, 다시 허용될 때의 suppression.removed입니다. 아무것도 구독하지 않으면 email.replied를 제외한 모든 메시지 이벤트, 즉 현재 열네 개를 받게 되며 나중에 추가되는 갈래는 절대 포함되지 않고, API는 이를 ["*"]로 돌려줍니다. 명시적으로 지정하고 싶다면 원하는 이벤트를 나열하세요. 각 전달에는 t=<unix>,v1=<hex> 형식의 X-OpenEmail-Signature가 실리는데, 타임스탬프와 점, 그리고 원본 본문에 대한 HMAC-SHA-256입니다. 여기에 X-OpenEmail-Event와 X-OpenEmail-Delivery도 함께 실립니다. 검증은 도착한 그대로의 바이트를 대상으로 하세요. 파싱한 뒤 다시 직렬화하면 키 순서가 바뀌어 서명이 깨집니다. 300초 재전송 허용 창은 수신 측이 적용할 몫이며, SDK의 검증기는 이 값을 기본값으로 씁니다.
- https가 아니거나 공인망에서 라우팅되지 않는 주소(loopback, RFC1918, link-local, CGNAT 및 이에 대응하는 IPv6 대역)는 등록이 거부되며, 리다이렉트는 따라가지 않으므로 3xx는 다른 곳으로 쫓아가지 않고 실패한 전달로 기록됩니다. 수신 측에는 5초가 주어지고, 엔드포인트는 병렬로 전달되므로 열 개여도 50초가 아니라 5초면 됩니다. 최근 시도는 응답 코드와 소요 시간과 함께 해당 엔드포인트의 페이지에 나열됩니다.
- 전달은 최대 다섯 번 시도합니다. 첫 번째는 이벤트가 발생하는 즉시 나가고, 저절로 해소될 여지가 있는 실패는 1분 뒤, 그다음 5분, 25분, 2시간 뒤에 재시도하므로 하나의 이벤트가 약 두 시간 반에 걸쳐 분산됩니다. 재시도는 메모리가 아니라 내구성 있는 작업으로 보관되므로, 그 사이에 배포가 일어나도 유실되지 않습니다. 반복할 가치가 있는 실패만 반복합니다. 타임아웃, 연결 거부, 408, 425, 429, 그리고 모든 5xx가 여기에 해당합니다. 그 밖의 4xx는 엔드포인트가 페이로드를 의도적으로 거부한 것이며, 네 번 더 요청해 봐야 같은 답을 받으면서 부하만 네 배가 됩니다. 이벤트 id는 한 번만 발급되고 모든 시도가 X-OpenEmail-Delivery에 그 값을 실어 보내므로, 같은 id를 두 번 본 수신 측은 두 번 처리하는 대신 두 번째를 버릴 수 있습니다. 100개의 이벤트가 연속으로 모든 시도에 실패하면 해당 엔드포인트는 비활성화되고, 워크스페이스로 메일이 발송되며, 그 이유를 엔드포인트에서 바로 읽을 수 있습니다. 410 Gone으로 응답하는 엔드포인트는 그 자리에서 비활성화됩니다.
- 연속으로 100번 실패한 엔드포인트는 끝없이 호출되지 않고 꺼지며, 웹훅 접근 권한이 있는 모든 사람에게 그 사실이 메일로 통지됩니다. 어떤 엔드포인트인지, 마지막 시도가 무엇을 보고했는지, 그리고 실패하는 동안 아무것도 큐에 쌓이지 않았다는 내용입니다. 이 횟수는 연속 실패 횟수이며 한 번이라도 전달에 성공하면 초기화되므로, 지난 3월의 좋지 않았던 오후가 쌓여 오늘 엔드포인트가 꺼지는 일은 없습니다. 다시 켜면 횟수도 함께 지워집니다. 콘솔은 토글 하나만 보여 주는 대신 두 상태를 구분합니다. 사용자가 끈 엔드포인트는 저희가 끈 엔드포인트와 다르게 표시됩니다.
- 엔드포인트 관리는 출입구가 둘인 하나의 작업입니다. API에서는 POST /webhooks와 수정, 삭제, 비밀 값 교체, 테스트, 전달 로그가 있고 SDK에도 각각에 대응하는 메서드가 있습니다. 앱에서는 Settings → Webhooks이며, 별도의 레지스트리가 아니라 동일한 레지스트리를 대상으로 합니다. 읽기는 webhooks:read로 제한되므로, 소유자가 아니어도 연동을 만드는 사람이라면 엔드포인트와 그 전달 이력(어떤 것이 발생했는지, 수신 측이 무엇을 응답했는지, 얼마나 걸렸는지)을 볼 수 있습니다. 등록, 편집, 테스트, 교체, 삭제에는 두 화면 모두에서 webhooks:write와 메일함 소유권이 함께 필요하며, 뒤쪽 조건은 의도된 것입니다. 엔드포인트에는 주소 축이 없어서 워크스페이스가 보유한 모든 주소의 메일을 제목과 수신자까지 함께 받게 되는데, 어떤 권한도 “그 전부를 받아도 된다”는 뜻은 아니기 때문입니다. 연동을 만들되 메일은 읽지 않는 역할은 대신 워크스페이스 키로 이를 다룹니다.