웹훅
AgentSend 계정에서 이벤트가 발생할 때 실시간으로 알림을 받으세요. 새 이메일 수신, 전송 확인, 반송 감지 등.
개요
새 메시지를 확인하기 위해 API를 폴링하는 대신, 웹훅을 사용하면 AgentSend가 이벤트 발생 즉시 서버로 이벤트 데이터를 푸시할 수 있습니다. HTTPS 엔드포인트를 등록하면 AgentSend는 구독 이벤트가 발생할 때마다 JSON 페이로드와 함께 POST 요청을 보냅니다.
웹훅은 반응형 에이전트를 구축하는 권장 방법입니다. 답장이 에이전트의 수신함에 도착하면 애플리케이션은 몇 초 내에 전체 메시지 페이로드를 받아 즉시 조치할 수 있습니다.
웹훅 엔드포인트는 HTTPS를 통해 공개적으로 접근 가능해야 합니다. localhost URL은 허용되지 않습니다. 로컬에서 개발할 때는 ngrok이나 터널 서비스를 사용하세요.
웹훅 만들기
/webhooks로 POST 요청을 보내 새 웹훅을 등록합니다. url 필드는 필수이며, 다른 모든 필드는 선택 사항입니다.
/webhooks
| 파라미터 | 타입 | 설명 |
|---|---|---|
url 필수 |
string | AgentSend가 이벤트를 POST할 HTTPS URL. |
description |
string | 대시보드에서 웹훅을 식별하는 사람이 읽을 수 있는 레이블. |
events |
string[] | 구독할 이벤트 타입 목록. 생략하면 모든 이벤트를 받습니다. |
const res = await fetch("https://api.agentsend.io/webhooks", { method: "POST", headers: { "x-api-key": process.env.AGENTSEND_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://myapp.example.com/hooks/agentsend", description: "Production inbox events", events: ["message.received", "message.bounced"], }), }); const webhook = await res.json(); console.log(webhook.id); // wh_01j9... console.log(webhook.secret); // whsec_... store this securely!
secret은 생성 응답에서 단 한 번만 반환됩니다. 안전한 환경 변수에 즉시 저장하세요. 다시 가져올 수는 없지만 PUT /webhooks/{id}로 순환시킬 수 있습니다.
이벤트 타입
events 배열에 식별자를 전달하여 특정 이벤트를 구독합니다. 배열을 생략하면 AgentSend가 모든 이벤트 타입을 엔드포인트에 전달합니다.
| 이벤트 | 설명 |
|---|---|
domain.verified |
커스텀 도메인이 DNS 검증을 통과하여 사용 준비가 되었습니다. |
message.received |
수신 이메일이 에이전트의 수신함 중 하나에 도착했습니다. |
message.sent |
발신 메시지가 수락되어 전송 대기 중입니다. |
message.delivered |
수신자의 메일 서버가 성공적인 전송을 확인했습니다. |
message.bounced |
메시지를 전송할 수 없습니다 (하드 또는 소프트 바운스). |
message.complained |
수신자가 메일 클라이언트에서 메시지를 스팸으로 표시했습니다. |
웹훅 페이로드
모든 웹훅 전송은 Content-Type: application/json 본문을 포함하는 POST 요청입니다. 페이로드는 이벤트 타입과 관계없이 일관된 형식을 갖습니다.
{
"id": "evt_01j9xkq7z8abc",
"type": "message.received",
"createdAt": "2026-04-16T10:23:45.000Z",
"webhookId": "wh_01j9abc123",
"data": {
"id": "msg_01j9xkp4n5def",
"inboxId": "inbox_01j8mq3r2t",
"threadId": "thr_01j9xkp4m1ghi",
"fromAddress": "alice@example.com",
"fromName": "Alice Chen",
"toAddresses": ["a1b2c3@agentsend.io"],
"subject": "Re: Your proposal",
"bodyText": "Looks great, let's move forward.",
"bodyHtml": "<p>Looks great, let's move forward.</p>",
"hasAttachments": false,
"receivedAt": "2026-04-16T10:23:44.812Z"
}
}서명 검증
모든 웹훅 요청에는 X-AgentSend-Signature 헤더가 포함됩니다. 이는 웹훅의 secret으로 서명된 원시 요청 본문의 HMAC-SHA256 해시입니다. 위조된 요청으로부터 보호하기 위해 전송을 처리하기 전에 항상 이 서명을 검증하세요.
import crypto from "node:crypto"; app.post("/hooks/agentsend", express.raw({ type: "application/json" }), (req, res) => { const sig = req.headers["x-agentsend-signature"]; const secret = process.env.AGENTSEND_WEBHOOK_SECRET; const expected = crypto .createHmac("sha256", secret) .update(req.body) // raw Buffer — do NOT parse JSON first .digest("hex"); if (!crypto.timingSafeEqual( Buffer.from(sig), Buffer.from(expected) )) { return res.status(401).send("Invalid signature"); } const event = JSON.parse(req.body); // handle event.type … res.sendStatus(200); });
import hmac, hashlib, os from flask import Flask, request, abort app = Flask(__name__) @app.route("/hooks/agentsend", methods=["POST"]) def webhook(): sig = request.headers.get("X-AgentSend-Signature", "") secret = os.environ["AGENTSEND_WEBHOOK_SECRET"].encode() expected = hmac.new(secret, request.data, hashlib.sha256).hexdigest() if not hmac.compare_digest(sig, expected): abort(401) event = request.get_json() # handle event["type"] … return "", 200
타이밍 공격을 방지하려면 서명을 확인할 때 항상 상수 시간 비교(예: crypto.timingSafeEqual 또는 hmac.compare_digest)를 사용하세요.
재시도 정책
엔드포인트가 10초 이내에 2xx HTTP 상태 코드로 응답하면 AgentSend는 전송이 성공한 것으로 간주합니다. 요청이 타임아웃되거나 2xx가 아닌 응답이 반환되면 지수 백오프로 전송이 재시도됩니다:
| 시도 | 이전 시도 이후 지연 |
|---|---|
| 1차 재시도 | 5초 |
| 2차 재시도 | 30초 |
| 3차 재시도 | 5분 |
| 4차 재시도 | 30분 |
| 5차 재시도 | 2시간 |
5회 재시도 실패(총 6회 시도) 후 전송은 실패로 표시되며 더 이상 시도하지 않습니다. 실패한 전송은 전송 로그에서 확인하고 대시보드에서 수동으로 재생할 수 있습니다.
가능한 한 빨리 200 OK로 응답하세요. 처리 로직에 시간이 걸리면 페이로드를 즉시 수락하고 큐나 백그라운드 작업으로 비동기 처리하세요.
웹훅 관리
웹훅 API를 사용하면 등록된 엔드포인트를 나열, 업데이트, 삭제할 수 있습니다.
웹훅 목록
계정에 등록된 모든 웹훅을 반환합니다.
# GET /webhooks curl https://api.agentsend.io/webhooks \ -H "x-api-key: $AGENTSEND_API_KEY"
웹훅 업데이트
URL, 설명 또는 구독 이벤트 타입을 변경합니다. "rotateSecret": true를 전달해 서명 시크릿을 순환할 수도 있습니다. 응답에 새 secret이 포함됩니다.
# PUT /webhooks/{id} curl -X PUT https://api.agentsend.io/webhooks/wh_01j9abc123 \ -H "x-api-key: $AGENTSEND_API_KEY" \ -H "Content-Type: application/json" \ -d '{"events": ["message.received", "message.delivered", "message.bounced"]}'
웹훅 삭제
웹훅을 영구적으로 제거합니다. 삭제가 적용되기 전에도 진행 중인 전송은 시도됩니다.
# DELETE /webhooks/{id} curl -X DELETE https://api.agentsend.io/webhooks/wh_01j9abc123 \ -H "x-api-key: $AGENTSEND_API_KEY"
활동 모니터링
특정 웹훅의 최근 이벤트 볼륨 및 오류율 요약을 가져옵니다.
# GET /webhooks/activity curl "https://api.agentsend.io/webhooks/activity?webhookId=wh_01j9abc123" \ -H "x-api-key: $AGENTSEND_API_KEY"
전송 로그
모든 전송 시도가 기록됩니다. logs 엔드포인트를 쿼리해 HTTP 응답 코드, 응답 본문, 타이밍을 포함한 웹훅의 전체 기록을 확인하세요.
GET/webhooks/logs
| 쿼리 파라미터 | 타입 | 설명 |
|---|---|---|
webhookId |
string | 특정 웹훅으로 로그를 필터링합니다. |
status |
string | success, failed, 또는 pending. |
limit |
number | 반환할 레코드 수 (기본값 50, 최대 200). |
before |
string | 페이지네이션 커서; 마지막으로 본 레코드의 id를 전달합니다. |
# Fetch failed deliveries for a webhook curl "https://api.agentsend.io/webhooks/logs?webhookId=wh_01j9abc123&status=failed" \ -H "x-api-key: $AGENTSEND_API_KEY"
각 로그 항목에는 다음이 포함됩니다:
- id — 고유 전송 시도 ID.
- eventId — 원본 이벤트 ID.
- eventType — 예:
message.received. - status —
success,failed, 또는pending. - httpStatus — 엔드포인트가 반환한 HTTP 응답 코드.
- durationMs — 왕복 시간 (밀리초).
- attempt — 몇 번째 시도인지 (1 = 첫 번째 전송).
- createdAt — 시도 시각.