페이지 콘텐츠로 건너뛰기
문서
Make Agent Fast 문서

웹훅

공개 엔드포인트를 등록하고 서명된 원시 페이로드를 검증하고 이벤트 중복을 제거하고 재시도를 안전하게 운영합니다.

이 페이지 한눈에

공개 엔드포인트를 등록하고 서명된 원시 페이로드를 검증하고 이벤트 중복을 제거하고 재시도를 안전하게 운영합니다.

웹훅 전달서명된 이벤트가 플랫폼에서 엔드포인트로 전달됩니다
플랫폼 이벤트서명HMACHTTPS POST
HMAC-SHA256최소 한 번8회 시도

개발자 웹훅은 계정 이벤트를 서버로 전송해 폴링을 줄입니다. 전달은 최소 한 번 방식이므로 이벤트가 중복되거나 지연되거나 순서가 바뀔 수 있습니다.

엔드포인트 등록#

webhooks:write 범위의 키를 만들고 공개 HTTPS 수신 URL과 필요한 최소 이벤트 집합을 등록합니다.

curl --fail-with-body https://makeagent.fast/api/v1/webhook-endpoints \
  -X POST \
  -H "Authorization: Bearer $MAF_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: webhook-production-v1" \
  -d '{
    "url": "https://api.example.com/webhooks/make-agent-fast",
    "events": ["lead.created", "conversation.started"]
  }'

URL은 HTTPS를 사용해야 하고 사용자 이름, 비밀번호, 프래그먼트, 사용자 지정 포트를 포함할 수 없으며 공개 IP 주소로만 해석되어야 합니다. 전달 중 리디렉션을 따르지 않습니다.

201 응답은 whsec_... 서명 시크릿을 한 번만 공개합니다. 응답을 닫기 전에 수신 서버의 시크릿 관리자에 저장하세요. 이후 엔드포인트 목록은 URL, 이벤트, 상태, 마지막 전달, 마지막 오류를 반환하지만 시크릿은 반환하지 않습니다.

이벤트 유형#

이벤트발생 시점
site.published사이트가 게시됨
site.unpublished사이트가 초안 상태로 돌아감
lead.created에이전트가 새 리드를 수집함
conversation.started새 대화 스레드가 시작됨
conversation.message.created대화에 메시지가 추가됨
knowledge.ready지식 소스 처리가 완료됨
broadcast.sent브로드캐스트 전송 워크플로가 완료됨
domain.activated폐지됨 — 사용자 지정 도메인은 제공하지 않으며 이 이벤트는 발생하지 않음

요청 형식#

Make Agent Fast는 Content-Type: application/json, User-Agent: MakeAgentFast-Webhooks/1.0 및 다음 헤더로 POST를 보냅니다.

maf-event-id: evt_...
maf-event-type: lead.created
maf-signature: t=1784210566,v1=HEX_HMAC_SHA256

JSON 본문은 안정적인 형식을 사용합니다. data 안의 필드는 이벤트에 따라 다르며 필드가 추가될 수 있습니다.

{
  "id": "evt_2c34...",
  "type": "lead.created",
  "created_at": "2026-07-16T10:02:46.000Z",
  "data": {
    "site_id": "SITE_ID",
    "lead_id": "LEAD_ID"
  }
}

본문의 id 또는 maf-event-id를 중복 제거 키로 사용하세요. 전달 시각이나 새로 생성한 데이터베이스 ID를 사용하지 마세요.

Node.js에서 서명 검증#

JSON 파싱 전에 정확한 원시 바이트를 읽습니다. 서명 값은 TIMESTAMP + "." + RAW_BODY입니다.

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyWebhook(rawBody: Buffer, header: string, secret: string) {
  const values = Object.fromEntries(header.split(",").map((part) => part.split("=", 2)));
  const timestamp = values.t;
  const supplied = values.v1;
  if (!timestamp || !supplied || !/^[a-f0-9]{64}$/.test(supplied)) return false;

  const age = Math.abs(Date.now() / 1_000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest("hex");
  return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(supplied, "hex"));
}

잘못된 서명과 허용한 재생 시간보다 오래된 타임스탬프를 거부하세요. 5분이 합리적인 기본값입니다. 고정 길이 바이트를 상수 시간으로 비교합니다.

Python에서 서명 검증#

import hashlib
import hmac
import time

def verify_webhook(raw_body: bytes, header: str, secret: str) -> bool:
    values = dict(part.split("=", 1) for part in header.split(",") if "=" in part)
    timestamp = values.get("t", "")
    supplied = values.get("v1", "")
    try:
        if abs(time.time() - int(timestamp)) > 300:
            return False
    except ValueError:
        return False

    signed = timestamp.encode() + b"." + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, supplied)

서명 검증이 성공한 뒤에만 JSON을 파싱하고 처리하세요.

확인 응답 및 처리#

  1. 서명과 타임스탬프를 검증합니다.
  2. 이벤트 ID를 고유 제약이 있는 테이블에 삽입합니다.
  3. 이미 존재하면 부작용을 반복하지 않고 204를 반환합니다.
  4. 이벤트를 커밋하거나 내부 작업을 대기열에 넣습니다.
  5. 빠르게 2xx 응답을 반환합니다.

Make Agent Fast 발신자는 10초 뒤 시간 초과됩니다. 2xx만 성공으로 보고 리디렉션을 따르지 않습니다.

재시도 동작#

실패한 전달은 최대 8회 시도됩니다. 백오프는 약 2초에서 시작해 지터와 함께 두 배가 되고 1시간으로 제한되며 워커가 바쁘면 실제 전달은 더 늦을 수 있습니다. 재시도는 시작 요청보다 오래 지속되고 순서가 바뀔 수 있으므로 한 이벤트가 다른 이벤트 직전에 즉시 온다고 가정하지 마세요.

Make Agent Fast가 재시도하기를 원할 때만 2xx가 아닌 응답을 반환하세요. 영구적으로 지원하지 않는 이벤트 버전이나 삭제된 목적지는 계속 일시 오류를 만들지 말고 수락해 기록하거나 엔드포인트를 삭제합니다.

서명 시크릿 교체#

서명 시크릿은 다시 표시하거나 수정할 수 없습니다. 안전한 교체 방법은 다음과 같습니다.

  1. 임시 또는 버전이 지정된 수신 경로를 가리키는 두 번째 엔드포인트를 만듭니다.
  2. 새로 표시된 시크릿을 저장합니다.
  3. 두 엔드포인트의 이벤트를 모두 받고 중복을 제거합니다.
  4. 새 엔드포인트가 유효한 전달을 받는지 확인합니다.
  5. DELETE /webhook-endpoints/{endpoint_id}로 이전 엔드포인트를 삭제합니다.

수신 URL이 같다면 중복 기간 동안 수신 애플리케이션이 두 시크릿을 모두 받고 이벤트 ID로 중복 부작용을 막도록 합니다.

문제 해결#

증상확인 항목
전달 없음엔드포인트 상태, 선택 이벤트, 공개 DNS, HTTPS 인증서, 실제 이벤트 발생 여부
서명 불일치원시 본문 접근, 정확한 timestamp.body 연결, 올바른 엔드포인트 시크릿, 프록시 본문 변환
반복 전달반환 코드, 10초 제한, 내부 예외, 이벤트 ID 고유성
사설 주소 오류DNS는 공개 주소로만 해석되어야 하며 localhost와 내부 범위는 거부됨
리디렉션 실패최종 HTTPS URL을 직접 등록, 리디렉션은 따르지 않음

지원 메시지에 서명 시크릿, Bearer 키 또는 전체 방문자 페이로드를 넣지 마세요. 엔드포인트 ID, 이벤트 ID, 실패 시각, 정리한 수신 서버 로그를 포함하세요.