문서
아웃바운드 웹훅: 페이로드와 서명
AutoPSEO의 알림과 리포트를 서명된 JSON 형태로 원하는 HTTPS 엔드포인트에서 받아 보세요. 요청 헤더, 본문 필드, 이벤트 유형, 서명 검증 방법을 설명합니다.
아웃바운드 웹훅은 AutoPSEO의 알림, 예약 리포트, 테스트 메시지, 리포트 요약을 직접 운영하는 엔드포인트로 보냅니다. 메시지마다 HMAC-SHA256으로 서명한 JSON POST 요청 한 건이 전송됩니다. Zapier, Make, n8n, 인시던트 관리 도구, 자체 코드와 연동할 때 사용하세요. Slack이나 Teams 계정은 필요하지 않습니다. 사이트로 글을 전달하는 웹훅을 찾고 계신가요? 그 웹훅은 콘텐츠 엔진에 속합니다. 콘텐츠 전달을 참고하세요.
웹훅 추가하기
- Outbound webhooks 섹션에서 New webhook을 클릭합니다.
- Name을 입력하고 엔드포인트 URL을 붙여넣습니다. 공개 HTTPS 주소여야 합니다. 일반
http,localhost,.local또는.internal로 끝나는 이름, 점이 없는 호스트, 사설 IP 주소와 링크 로컬 IP 주소는 허용되지 않습니다. - 저장 후 표시되는 signing secret을 복사합니다. 이 값은 이때 한 번만 표시됩니다.
- Send test를 클릭해
test이벤트를 전송한 다음, 예약 리포트나 알림을 웹훅에 연결합니다.
웹훅 요청의 구성
모든 메시지는 다음 헤더와 함께 POST로 전송됩니다.
| 헤더 | 값 |
|---|---|
Content-Type | application/json |
User-Agent | AutoPSEO-Webhook/1.0 |
X-AutoPSEO-Event | 이벤트 유형 (예: traffic_drop) |
X-AutoPSEO-Signature | sha256= 뒤에 소문자 16진수 64자 |
다음은 2026년 10월 12일 06:00 UTC 실행에서 도메인 속성에 발송된 트래픽 감소 알림이며, 읽기 쉽게 정리한 형태입니다. 실제 전송 시에는 압축된 JSON이며, id와 site_id 값은 예시용으로 만든 값입니다.
{
"id": "9b2e61f04c3a4d7e8f15a0b6c2d39e47",
"event_type": "traffic_drop",
"severity": "high",
"site_id": "4f0c1e9a2b7d4c58",
"site_url": "sc-domain:example.com",
"occurred_at": "2026-10-12T06:00:04.183Z",
"title": "Traffic drop: example.com",
"text": "Clicks 1,500 vs 2,000 in the previous window (-25.0%).",
"url": "https://app.autopseo.com/property/sc-domain%3Aexample.com",
"payload": {
"fields": [
{ "label": "Metric", "value": "clicks" },
{ "label": "Current", "value": "1,500" },
{ "label": "Baseline", "value": "2,000" },
{ "label": "Window", "value": "7 days (2026-10-05 → 2026-10-11 vs 2026-09-28 → 2026-10-04)" },
{ "label": "Change", "value": "-25.0%" }
],
"table": null,
"detector": "traffic_drop",
"window": 7,
"range": { "start": "2026-10-05", "end": "2026-10-11", "compStart": "2026-09-28", "compEnd": "2026-10-04" },
"result": {
"fired": true,
"metric": "clicks",
"value": 1500,
"baseline": 2000,
"change": -0.25,
"severity": "high",
"summary": "Clicks 1,500 vs 2,000 in the previous window (-25.0%)."
}
},
"source": "autopseo"
}
최상위 필드는 모든 이벤트에서 동일합니다.
| 필드 | 내용 |
|---|---|
id | 요청마다 새로 생성되는 32자 무작위 16진수 문자열 |
event_type | 발생한 이벤트 (다음 섹션 참고) |
severity | 알림은 critical, high, medium, low 중 하나이고, 리포트·요약·테스트는 info |
site_id | AutoPSEO의 속성 id. 테스트와 데모 속성에서는 null |
site_url | sc-domain:example.com이나 https://www.example.com/ 같은 Search Console 속성. 테스트에서는 null |
occurred_at | 메시지가 생성된 시각 (UTC 기준 ISO 8601 타임스탬프) |
title, text | Slack과 Teams에 표시되는 제목과 요약 문장 |
url | AutoPSEO의 리포트 주소. 테스트에서는 null |
payload.fields | Slack에서는 필드로, Teams에서는 팩트로 표시되는 레이블이 붙은 값 |
payload.table | 리포트와 요약의 columns와 rows. 그 외에는 null |
source | 항상 autopseo |
비율은 소수로 표기합니다. change가 -0.25면 25% 감소를, ctr이 0.034면 3.4%를 뜻합니다.
이벤트 유형
event_type | 전송 시점 | payload의 추가 키 |
|---|---|---|
traffic_drop, traffic_spike, position_drop, ctr_drop, new_lost_rankings, anomaly, index_flip, rank_drop, audit_regression | 알림 규칙이 작동할 때 (예약 실행 또는 Test rule) | detector, window, range (날짜. index_flip, rank_drop, audit_regression은 null), result |
scheduled_report | 예약 리포트가 발송될 때 (예약 실행 또는 Send now) | reportType (summary, queries, pages, rankings 중 하나), range (날짜와 label), totals, previous |
report_summary | 리포트 페이지에서 Send to Slack을 사용할 때 | 없음. 수치는 fields와 table에 담김 |
test | 웹훅에서 Send test를 클릭할 때 | sample (항상 true) |
result에는 감지기가 측정한 값이 그대로 담깁니다. fired, metric, value, baseline, change (비교 대상이 없으면 null), severity, summary가 포함되며, anomaly에는 수정된 z-점수인 z도 들어갑니다. totals와 previous에는 리포트의 두 기간에 대한 clicks, impressions, ctr, position이 담깁니다. 각 감지기가 측정하는 항목은 알림에서 확인할 수 있습니다.
서명 검증 방법
서명은 AutoPSEO가 보낸 바이트 그대로에 웹훅의 signing secret을 키로 사용해 계산한 HMAC-SHA256 값이며, sha256= 뒤에 소문자 16진수를 붙여 표기합니다. 검증 절차는 다음과 같습니다.
- JSON을 파싱하기 전에 원시 요청 본문을 읽습니다. 파싱한 뒤 다시 직렬화하면 바이트가 달라질 수 있습니다.
- 해당 바이트에 대해 HMAC-SHA256을 계산합니다. 이때 시크릿은 표시된 64자 문자열을 16진수로 디코딩하지 않고 텍스트 그대로 사용합니다.
- 결과 앞에
sha256=을 붙여X-AutoPSEO-Signature헤더와 상수 시간 방식으로 비교합니다. - 두 값이 다르면 요청을 거부합니다. 일치하면 즉시 2xx 상태로 응답하고, 시간이 걸리는 작업은 그 뒤에 처리합니다.
의존성 없이 Node.js로 작성한 수신기 예시입니다.
import { createServer } from 'node:http';
import { createHmac, timingSafeEqual } from 'node:crypto';
const secret = process.env.AUTOPSEO_WEBHOOK_SECRET;
createServer((req, res) => {
const chunks = [];
req.on('data', (chunk) => chunks.push(chunk));
req.on('end', () => {
const raw = Buffer.concat(chunks);
const expected = Buffer.from('sha256=' + createHmac('sha256', secret).update(raw).digest('hex'));
const received = Buffer.from(String(req.headers['x-autopseo-signature'] || ''));
if (received.length !== expected.length || !timingSafeEqual(received, expected)) {
res.writeHead(401).end();
return;
}
res.writeHead(204).end();
const event = JSON.parse(raw.toString('utf8'));
console.log(event.event_type, event.severity, event.site_url);
});
}).listen(8080);
Node의 timingSafeEqual은 두 버퍼의 길이가 다르면 예외를 던지므로 길이 비교를 먼저 수행합니다.
전송 규칙
- 메시지마다 한 번만 시도하며, 제한 시간은 10초입니다. 2xx가 아닌 응답은 모두 실패로 처리되고, 상태 코드와 응답 본문의 앞부분이 Delivery history에 기록됩니다.
- 리디렉션은 따라가지 않습니다. 3xx 응답은 최종 URL을 사용하라는 안내와 함께 실패로 처리됩니다.
- 타이머 기반의 재시도는 없습니다. 전송에 실패한 알림은 발송된 것으로 표시되지 않으므로, 다음 점검에서 다시 평가되어 여전히 조건에 해당하면 전송됩니다. 대부분의 감지기는 다음 날 아침, 인덱스 변동 감지기는 다음 시간에 점검합니다. 전송에 실패한 예약 리포트는 같은 기간에 다시 발송되지 않으므로, 엔드포인트를 고친 뒤 Send now를 사용하세요.
id는 요청마다 생성되므로 이벤트가 아니라 전송 건을 식별합니다. 알림 중복을 제거하려면event_type,site_url,occurred_at의 날짜를 조합하세요. 규칙은 Test rule을 실행한 경우를 제외하면 속성당 UTC 기준 하루에 최대 한 번만 전송됩니다.
서명 시크릿
AutoPSEO는 웹훅을 저장할 때 signing secret을 한 번만 보여 주며, 이후 목록에서는 다시 표시하지 않습니다. 시크릿이 보호하는 엔드포인트와 함께 수신기 설정에 바로 저장해 두세요.
URL 자체는 암호화되어 저장되며, 목록에는 호스트와 마지막 몇 글자만 표시됩니다. 웹훅을 삭제하면 해당 웹훅으로 전송하는 모든 규칙도 함께 삭제됩니다. JSON이 아니라 Teams 채널에서 읽기 편한 카드 형식으로 받고 싶다면 Microsoft Teams를 이용하세요.