본문으로 건너뛰기
개발자

커뮤니티 웹훅

커뮤니티에서 어떤 일이 일어나면 Mateflow가 지정한 URL로 HTTP POST를 보냅니다. 이 페이지에서는 수신 측이 받는 내용, 검증 방법, 실패했을 때의 동작, 그리고 모든 이벤트의 모든 필드를 설명합니다.

빠른 시작

각 전송은 JSON 본문을 담은 POST입니다. 여러 웹훅이 같은 이벤트를 구독하면 웹훅마다 각각 사본을 받습니다.

  1. 커뮤니티 관리자 페이지에서 설정 → 연동 → 웹훅을 열고 웹훅을 만드세요. URL을 입력하고, 이벤트를 고르고, 시크릿을 생성한 다음 켜면 됩니다.
  2. 요청마다 다음 순서로 처리하세요.
    1. 원본 요청 본문을 읽고 서명을 검증합니다.
    2. test가 true이면 200을 반환하고 중단합니다.
    3. event_id로 중복을 제거합니다.
    4. 작업은 자체 큐에 넣고 10초 안에 2xx를 반환합니다.
  3. 웹훅 테스트를 눌러 테스트 요청을 보낸 다음, 전송 로그를 열어 요청과 응답을 확인하세요.

요금제와 설정

웹훅은 요금제 기능이며, 커뮤니티 관리자만 관리할 수 있습니다.

요금제커뮤니티당 웹훅 수
Free사용 불가
Starter사용 불가
Growth5
Business20
Enterprise무제한
  • 웹훅이 없는 요금제로 다운그레이드해도 웹훅 설정은 유지되지만, 전송도 로그 기록도 이루어지지 않습니다. 다시 업그레이드하면 전송이 재개됩니다.
  • 웹훅은 관리자 콘솔에서 만들고 관리합니다. Community Open API에는 웹훅용 엔드포인트가 없습니다.

구성

설정요구 사항
URL관리자 폼은 https:// URL만 받으며, 최대 1,024자입니다.
접근할 수 없는 주소루프백, 사설, 링크 로컬 대역으로 해석되는 주소로의 요청은 연결 시점에 차단되며 네트워크 오류로 처리됩니다. 로컬 개발에는 공개 터널을 사용하세요.
이름최대 100자입니다.
이벤트아래 이벤트 목록에서 하나 이상 선택해야 합니다. 이벤트 목록이 비어 있는 상태로 이전에 저장된 웹훅은 모든 이벤트를 받습니다.
시크릿선택 사항이며 최대 128자입니다. 한 번 저장하면 교체할 수는 있지만 다시 읽을 수는 없습니다. 시크릿이 없으면 요청에 서명이 붙지 않으므로 반드시 설정하기를 권장합니다. 시크릿 자체는 요청에 절대 포함되지 않습니다.

전송 형식

모든 전송은 POST이며, 요청당 타임아웃은 10초입니다.

헤더

헤더값비고
Content-Typeapplication/json항상 JSON입니다.
User-AgentMateflow-Webhook/1.0보낸 쪽이 Mateflow임을 나타냅니다.
X-Mateflow-TimestampUnix 초이 요청을 보낸 시각입니다. 서명에 포함됩니다.
X-Mateflow-Signaturesha256= 뒤에 16진수 다이제스트웹훅에 시크릿이 있을 때만 포함됩니다.

커스텀 웹훅에는 X-Mateflow-Delivery-Id 헤더가 없습니다. delivery_id는 본문에서 읽으세요.

본문

필드타입설명
eventstring이벤트 코드입니다.
event_idstring이벤트의 고유 식별자입니다. 이 값으로 중복을 제거하세요. 자동 재시도와 수동 재전송을 거쳐도, 그리고 해당 이벤트를 구독한 모든 웹훅에서도 같은 값을 유지합니다. 불투명한 문자열로 취급하세요.
occurred_atRFC 3339이벤트가 일어난 시각이며 초 단위입니다. 재시도해도 같은 값을 유지합니다.
delivery_idstring (decimal)이번 전송을 식별합니다. 자동 재시도마다 바뀌며, 수동 재전송은 원래 값을 유지합니다.
timestampRFC 3339본문이 생성된 시각입니다. 수동 재전송은 원래 값을 유지하므로, 최신성 판단에는 헤더를 사용하세요.
webhook_idhashid이번 전송을 받는 웹훅입니다.
site_idhashid해당 커뮤니티입니다.
dataobject이벤트의 데이터이며, 이벤트 목록에 설명되어 있습니다.

드물게 ID 생성에 실패하면 event_id와 occurred_at이 없을 수 있습니다. 이 경우도 받아들이고, event와 data 안의 비즈니스 ID를 조합해 중복을 판별하세요.

JSON
{
  "event": "payment.received",
  "event_id": "86116692152877061",
  "occurred_at": "2026-11-03T00:00:00Z",
  "delivery_id": "86116692152877060",
  "timestamp": "2026-11-03T00:00:01Z",
  "webhook_id": "qP3aXm71Bd",
  "site_id": "Z5Kf08mFv8r",
  "data": {
    "member_id": "b7Lq2WxR9e",
    "username": "sample_member",
    "email": "sample.member@example.com",
    "invoice_id": "in_1Abc",
    "subscription_id": "sub_1Abc",
    "amount_cents": 2900,
    "currency": "usd",
    "period_start": "2026-11-03T00:00:00Z",
    "period_end": "2026-12-03T00:00:00Z"
  }
}

서명 검증

웹훅에 시크릿이 있으면 모든 요청은 타임스탬프 헤더와 원본 본문에 대한 HMAC-SHA256으로 서명됩니다.

서명
X-Mateflow-Signature = "sha256=" + hex(
  HMAC-SHA256(secret, X-Mateflow-Timestamp + "." + raw_request_body)
)
  • 원본 바이트를 사용하세요. 본문에서는 HTML에서 특수한 문자가 이스케이프될 수 있으므로, JSON을 파싱한 뒤 다시 직렬화하면 바이트가 달라져 서명이 일치하지 않습니다.
  • 타임스탬프는 본문이 아니라 헤더에서 가져오세요. 수동 재전송은 헤더의 타임스탬프와 서명만 갱신합니다.
  • 타임스탬프 검사는 재시도를 방해하지 않습니다. 모든 자동 재시도와 수동 재전송은 새로 서명되므로 5분 윈도우면 충분합니다.
Node.js
const crypto = require('crypto');

// rawBody must be the untouched bytes, e.g. from express.raw({ type: 'application/json' }).
function verifyMateflowSignature(rawBody, headers, secret, toleranceSeconds = 300) {
  const timestamp = headers['x-mateflow-timestamp'];
  const received = headers['x-mateflow-signature'] || '';
  if (!timestamp || Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) {
    return false;
  }
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.`)
    .update(rawBody)
    .digest('hex');
  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python
import hashlib
import hmac
import time


def verify_mateflow_signature(raw_body: bytes, headers, secret: str, tolerance_seconds: int = 300) -> bool:
    timestamp = headers.get("X-Mateflow-Timestamp", "")
    received = headers.get("X-Mateflow-Signature", "")
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > tolerance_seconds:
        return False
    signed = timestamp.encode() + b"." + raw_body
    expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(received.encode(), expected.encode())

테스트 벡터

서명 계산을 확인하는 데 이 값을 사용하세요. 타임스탬프는 이미 만료되었으므로, 테스트하는 동안에는 시간 윈도우 검사를 완화하세요.

테스트 벡터
secret                 my_signing_secret
X-Mateflow-Timestamp   1793664000
raw body (UTF-8)       {"event":"member.joined","data":{"username":"张三"}}
X-Mateflow-Signature   sha256=3f4c83828ce0086150c9bd48c1654899357b7c05265c1d1b6c172225ee04b4b4

시크릿 교체

새 시크릿은 저장하는 즉시 적용되며, 대기 중인 재시도도 새 시크릿으로 서명됩니다. Mateflow가 두 시크릿으로 모두 서명하는 기간은 없으므로, 전환하는 동안 수신 측에서 이전 시크릿과 새 시크릿을 모두 허용하세요.

응답과 재시도

전송의 성공 여부는 상태 코드로만 판정합니다. 응답 본문은 파싱하지 않습니다.

수신 측 응답결과재시도
2xx성공안 함
5xx, 408, 429실패함
타임아웃, 연결 실패, 차단된 주소실패, 상태 0으로 기록함
그 밖의 모든 4xx실패안 함
  • 재시도 일정: 실패한 뒤 Mateflow는 1분, 10분, 1시간, 6시간 순으로 기다립니다. 첫 시도를 포함하면 약 7시간 동안 최대 5번 시도합니다.
  • 전송을 포기하는 경우: 재시도 횟수를 모두 소진했거나 재시도 대상이 아닌 상태 코드를 받으면, 커뮤니티 소유자와 관리자에게 이메일이 발송됩니다. 웹훅 하나당 24시간에 최대 한 통입니다.
  • 도중에 중단되는 경우: 웹훅이 삭제되거나 꺼지거나, 요금제에 더 이상 웹훅이 포함되지 않으면 이후 시도는 전송도 기록도 되지 않습니다.
  • 리다이렉트: 전송은 리다이렉트를 따라가지만, 301, 302, 303은 POST를 본문 없는 GET으로 바꿉니다. 최종 URL을 입력하고, http에서 https로의 리다이렉트에 의존하지 마세요.

수동 재전송

전송 로그에서 다시 보내기를 누르면 웹훅의 현재 URL로 전송을 다시 보냅니다.

  • 본문은 event_id, delivery_id, timestamp를 포함해 동일합니다. 타임스탬프 헤더와 서명은 새로 만들어집니다.
  • 한 번만 시도하며, 실패해도 재시도하지 않습니다. 결과는 로그에 새 행으로 나타납니다.
  • 커스텀 웹훅 전송만 재전송할 수 있으며, 웹훅이 아직 존재하고 켜져 있어야 합니다.

전송 로그

로그는 30일 동안 보관됩니다. 응답 본문은 각각 처음 4KB만 저장됩니다.

신뢰성

수신 측이 가정할 수 있는 것과 없는 것입니다.

  • 최소 한 번 전송(at least once). 같은 이벤트가 두 번 이상 도착할 수 있습니다. event_id로 중복을 제거하세요.
  • 돈과 관련된 처리에는 두 번째 확인을 추가하세요. Mateflow는 같은 결제와 같은 구독의 시작 또는 종료를 이미 중복 제거하지만, 그 확인을 사용할 수 없을 때는 버리지 않고 보내며, 이 경우 두 이벤트의 event_id 값은 서로 다릅니다. invoice_id, subscription_id, order_id도 함께 확인하세요.
  • 순서는 보장되지 않습니다. 전송은 병렬로 실행되고 재시도되므로, 한 멤버의 access_group_removed와 access_group_added가 뒤바뀌어 도착할 수 있습니다. 어느 쪽이 먼저인지 알아내려면 초 단위로 정확한 occurred_at을 사용하세요.
  • 드물게 유실될 수 있습니다. 이벤트는 데이터가 커밋된 뒤에 큐에 들어가므로, 그 두 단계 사이에 프로세스가 종료되면 이벤트가 유실됩니다.
  • 상태를 동기화한다면 정합성을 맞추세요. 이벤트는 무언가 바뀌었다는 신호로 취급하고, 현재 상태는 Open API에서 읽으며(예: members.read 스코프로 /member/get), 주기적으로 전체 비교를 실행하세요. Open API는 이메일 주소를 반환하지 않으므로, 이메일 주소는 이벤트에서 가져오세요.

이벤트 목록

17개의 이벤트를 구독할 수 있습니다.

모든 필드에 공통되는 규칙

  • Mateflow ID는 hashid 문자열이며 API가 쓰는 값과 같습니다. 값이 0이면 인코딩되지 않고 숫자 0으로 나타납니다.
  • member_id와 user_id는 같은 사람을 가리킵니다. member.joined, 게시물 이벤트, 댓글 이벤트는 user_id를 쓰고, 그 밖의 이벤트는 모두 member_id를 씁니다.
  • Stripe ID는 그대로 전달됩니다. 대상은 invoice_id, subscription_id, charge_id, payment_intent_id입니다.
  • 시각은 UTC 기준 RFC 3339입니다.
  • 금액은 해당 통화의 최소 단위 정수(USD는 센트)이며, currency는 소문자 통화 코드입니다.
  • 해당하지 않는 선택 필드는 생략됩니다. null이나 빈 문자열이 아닙니다.
  • 필드와 enum 값은 추가만 되고 변경되지 않습니다. 수신 측은 새로 추가된 값을 허용해야 합니다.

전체 이벤트

이벤트의미사용자명과 이메일
member.joined새 멤버가 가입했습니다예
member.left멤버가 커뮤니티를 떠났습니다예
member.role_changed멤버의 역할이 바뀌었습니다아니요
member.suspended멤버가 정지 또는 차단되었습니다아니요
member.reinstated정지 또는 차단이 해제되었습니다아니요
member.access_group_added멤버가 액세스 그룹을 보유하게 되었습니다예
member.access_group_removed멤버가 액세스 그룹을 더 이상 보유하지 않게 되었습니다예
post.created게시물이 게시되었습니다아니요
post.deleted게시물이 삭제되었습니다아니요
comment.created댓글이 게시되었습니다아니요
comment.deleted댓글이 삭제되었습니다아니요
payment.received결제가 들어왔습니다. 구독 인보이스 또는 일회성 구매입니다예
subscription.renewed구독이 성공적으로 갱신되었습니다예
payment.failed인보이스 결제가 실패했습니다예
subscription.createdStripe에서 구독이 생성되었습니다예
subscription.canceled구독이 종료되었습니다예
refund.issued환불이 처리되었습니다예

멤버

member.joined

새 멤버가 가입했습니다.

필드타입항상 포함설명
user_idhashid예멤버 ID입니다.
usernamestring예사용자명입니다.
emailstring예이메일 주소입니다.

전송되는 경우

  • 소셜 로그인, 첫 SSO 로그인, 초대를 통한 가입을 포함해 가입이 성공했을 때.
  • 유료 가입이 완료되었을 때.
  • 가입 요청이 승인되었을 때.
  • 같은 이메일로 다시 가입해 계정이 복원되었을 때. 이벤트가 다시 전송됩니다.

전송되지 않는 경우

  • CSV 파일로 멤버를 가져왔을 때.
  • 가입 요청을 검토하는 커뮤니티에서 멤버가 아직 승인을 기다리고 있을 때.

member.left

멤버가 커뮤니티를 떠났습니다.

필드타입항상 포함설명
member_idhashid예member.joined의 멤버 user_id와 같은 값입니다.
usernamestring예떠나기 전의 사용자명입니다. 떠나면서 개인 정보가 삭제되더라도 포함됩니다.
emailstring예떠나기 전의 이메일 주소이며, 같은 조건이 적용됩니다.
sourcestring예떠난 방식입니다. 아래 표를 참고하세요.
removed_byhashid아니요조치한 관리자입니다. removed와 anonymized에만 포함됩니다.
reasonstring아니요관리자가 사유를 입력한 removed에만 포함됩니다.
source의미같은 이메일로 다시 가입하면
removed관리자가 멤버를 제거했습니다같은 ID로 복원되고 member.joined가 다시 전송됩니다
account_deleted멤버가 직접 계정을 삭제했습니다. 즉시 삭제했거나 30일의 유예 기간이 끝났을 때입니다즉시 삭제: 위와 같습니다. 유예 기간 후: 새 ID
anonymized관리자가 GDPR에 따라 멤버를 익명화했습니다새 ID
inactivity비활성 계정 정리이며, 매일 밤 일괄로 실행됩니다새 ID

전송되지 않는 경우

  • 계정이 비활성화되었을 때.
  • 계정 삭제를 요청한 멤버가 유예 기간 중일 때. 유예 기간이 끝나면 전송되며, 멤버가 취소하면 전혀 전송되지 않습니다.
  • 멤버가 차단 또는 정지되었을 때. 이 경우에는 member.suspended가 전송됩니다.
  • 가입 요청이 거절되었거나 보류 중인 경우처럼, 애초에 가입하지 않았을 때.
  • 구독이 종료되었을 때.
  • 커뮤니티가 삭제되었을 때.
  • 멤버가 스페이스 하나에서만 나갔을 때.
  • 떠나면서 해제된 액세스 그룹은 member.access_group_removed로 하나씩 전송되지 않습니다. member.left는 해당 멤버가 더 이상 어떤 액세스 그룹도 보유하지 않는다는 뜻으로 취급하세요.

member.role_changed

멤버의 역할이 바뀌었습니다.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
previous_rolestring예member, moderator, admin 중 하나이며 항상 소문자입니다.
rolestring예같은 값을 씁니다.
changed_byhashid예변경한 사람입니다.

전송되지 않는 경우

  • 역할이 실제로는 바뀌지 않았을 때.
  • Billing Admin이나 커스텀 역할만 바뀌었을 때.

member.suspended

멤버가 정지 또는 차단되었습니다. 두 경우 모두 이 이벤트를 쓰며, ban_type으로 구분합니다.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
suspended_byhashid예조치한 관리자입니다.
reasonstring예관리자의 내부 메모입니다. 빈 문자열일 수 있습니다. 멤버에게 보여 주지 마세요.
ban_typestring예suspend 또는 ban입니다.
duration_daysnumber아니요suspend에만 포함됩니다.
suspended_untilRFC 3339아니요suspend에만 포함됩니다.
viastring아니요신고를 처리하는 중에 조치했다면 moderation입니다. 멤버 관리에서 조치했다면 생략됩니다.

전송되는 경우

  • 멤버 관리에서든 신고를 처리하는 중에든, 한 명씩이든 일괄이든 멤버가 정지 또는 차단되었을 때.

전송되지 않는 경우

  • 멤버가 경고만 받았을 때.

member.reinstated

정지 또는 차단이 해제되었습니다.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
reinstated_byhashid아니요조치한 관리자입니다. via가 expired이면 생략됩니다.
fromstring예해제된 대상으로, suspend 또는 ban입니다.
viastring아니요moderation(모더레이션에서 해제), appeal(이의 신청 승인), expired(정지 기간 만료) 중 하나입니다. 멤버 관리에서 해제했다면 생략됩니다.

전송되지 않는 경우

  • 정지 기간이 끝났지만 멤버에게 다른 유효한 차단이 있어 여전히 제한된 상태일 때.
  • 정지 기간이 끝나면 만료 여부를 5분마다 확인하므로, 이벤트는 보통 몇 분 안에 전송됩니다. occurred_at은 만료가 확인된 시각입니다.

member.access_group_added, member.access_group_removed

멤버가 액세스 그룹을 보유하게 되었거나 더 이상 보유하지 않게 되었습니다. 이벤트 하나는 멤버 한 명과 액세스 그룹 하나를 다루므로, 구독을 Silver에서 Gold로 옮기면 removed 하나와 added 하나가 전송됩니다.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
usernamestring예사용자명입니다.
emailstring예이메일 주소입니다.
access_group_idhashid예Open API의 access_groups[].id와 같은 값입니다.
access_group_namestring예당시의 그룹 이름입니다. 삭제된 그룹이라면 삭제되기 전의 이름입니다.
sourcestring예아래 표를 참고하세요.
changed_byhashid아니요manual과 invitation에만 포함됩니다.
sourceEvents의미changed_by
manualadded, removed관리자가 콘솔에서 부여하거나 제거했습니다해당 관리자
subscriptionadded, removed구독이 시작, 종료되었거나 등급이 바뀌었습니다없음
invitationadded멤버가 초대를 수락했습니다초대한 사람
defaultadded멤버가 기본 그룹에 들어갔습니다없음
group_deletedremoved액세스 그룹이 삭제되었습니다없음

전송되지 않는 경우

  • 멤버의 그룹 구성이 실제로는 바뀌지 않았을 때.
  • 가입할 때 멤버가 기본 그룹에 자동으로 들어갈 때.
  • 변경이 아직 승인을 기다리는 멤버에게 해당할 때.
  • 계정이 삭제되어 그룹이 해제될 때.
  • 액세스 그룹을 삭제하거나 기본 그룹을 설정하는 것은 일괄 변경입니다. 이벤트는 멤버별로 전송되어 조치 후 몇 초에서 몇 분 뒤에 도착하며, occurred_at은 조치가 이루어진 시각 그대로입니다. 이때의 event_id는 형식이 다르며, 그래서 불투명한 값으로 취급해야 합니다.

콘텐츠

네 가지 이벤트 모두에서 post_id는 콘텐츠 자체의 ID이고, user_id는 행동한 사람이 아니라 작성자입니다. 콘텐츠가 스페이스에 속하지 않으면 space_id는 생략됩니다. 비공개 스페이스의 콘텐츠도 전송되므로, 수신 측에서 필요하다면 space_id로 걸러 내세요.

post.created

게시물 또는 블로그 게시물이 게시되었습니다.

필드타입항상 포함설명
post_idhashid예게시물 ID입니다.
user_idhashid예작성자입니다.
space_idhashid아니요해당 콘텐츠가 속한 스페이스입니다.
titlestring예제목입니다. 빈 문자열일 수 있습니다.
bodystring예마크다운 원문이며 최대 500자입니다. 단어나 구두점 위치에서 일찍 잘릴 수 있고, 말줄임표는 붙지 않습니다. 블로그 게시물이라면 발췌문입니다.

전송되는 경우

  • 게시물 또는 블로그 게시물이 공개되었을 때: 바로 게시되었거나, 검토 대기열에서 승인되었거나, 예약 시각에 도달했을 때.

전송되지 않는 경우

  • 댓글이 게시되었을 때. 이 경우에는 comment.created가 전송됩니다.
  • 블로그 게시물은 최초 게시 때만 이 이벤트를 보내지만, 게시를 취소했다가 다시 예약한 게시물은 이 이벤트를 다시 보낼 수 있습니다. post_id 기준으로 처리가 멱등하도록 만드세요.

post.deleted

게시물 또는 블로그 게시물이 삭제되었습니다.

필드타입항상 포함설명
post_idhashid예게시물 ID입니다.
user_idhashid예작성자입니다.
space_idhashid아니요해당 콘텐츠가 속한 스페이스입니다.

전송되는 경우

  • 작성자가 삭제했거나, 관리자가 삭제했거나(한 건씩 또는 일괄), 신고를 처리하는 중에 제거되었거나, 차단으로 멤버의 콘텐츠가 지워졌을 때. 멤버의 콘텐츠를 지우면 이벤트가 한꺼번에 여러 건 발생할 수 있습니다.

전송되지 않는 경우

  • 블로그 게시물의 게시를 취소해 초안으로 되돌렸을 때. 삭제가 아닙니다.
  • 한 번도 게시되지 않은 예약 게시물을 삭제해도 이 이벤트가 전송되므로, 이 이벤트를 받았다고 해서 앞서 post.created를 받았다는 뜻은 아닙니다.
  • 콘텐츠 복원에 대한 별도의 이벤트는 없습니다.

comment.created

댓글이 게시되었습니다.

필드타입항상 포함설명
post_idhashid예댓글 자체의 ID입니다.
parent_idhashid예답글 대상인 게시물 또는 댓글입니다.
user_idhashid예작성자입니다.
space_idhashid아니요해당 콘텐츠가 속한 스페이스입니다.
bodystring예post.created의 body와 같은 규칙입니다.

comment.deleted

댓글이 삭제되었습니다. post.deleted와 같은 조건에서 전송됩니다.

필드타입항상 포함설명
post_idhashid예댓글 자체의 ID입니다.
parent_idhashid예답글 대상인 게시물 또는 댓글입니다.
user_idhashid예작성자입니다.
space_idhashid아니요해당 콘텐츠가 속한 스페이스입니다.

결제와 구독

이 이벤트들은 Stripe 구독, 스페이스 일회성 구매, 환불을 다룹니다. 멤버가 더 이상 존재하지 않는 경우를 제외하면 username과 email이 포함됩니다.

인앱 구매와 선물이나 리딤 코드로 부여된 구독은 payment.*나 subscription.*를 보내지 않습니다. 요금제가 액세스 그룹에 연결되어 있으면 source가 subscription인 member.access_group_added를 보내고, 액세스가 끝날 때 member.access_group_removed를 보내므로, 이를 이용해 권한 변경을 추적할 수 있습니다.

유료 가입에서는 결제가 계정보다 먼저 이루어지므로, 첫 결제의 이벤트는 계정이 만들어진 뒤에 전송됩니다. 대상은 member.joined, subscription.created, 그리고 첫 인보이스가 결제된 경우의 payment.received입니다. 이 이벤트들은 어떤 순서로든 도착할 수 있습니다.

payment.received

결제가 들어왔습니다. 페이로드 형태가 두 가지이며, invoice_id가 있는지 여부로 구분할 수 있습니다.

필드타입구독 인보이스일회성 구매설명
member_idhashid예예멤버 ID입니다.
usernamestring대부분대부분멤버가 더 이상 존재하지 않으면 생략됩니다.
emailstring대부분대부분멤버가 더 이상 존재하지 않으면 생략됩니다.
amount_centsnumber예예결제된 금액입니다. 체험이 시작될 때의 0원 인보이스는 0입니다.
currencystring예예통화입니다.
invoice_idstring예아니요Stripe 인보이스 ID입니다.
subscription_idstring예아니요Stripe 구독 ID입니다.
period_startRFC 3339예아니요이 결제로 구매한 기간의 시작입니다.
period_endRFC 3339예아니요이 결제로 구매한 기간의 끝입니다. 액세스 만료 시각으로 사용할 수 있습니다.
order_idhashid아니요예주문 ID입니다.
space_idhashid아니요예구매한 스페이스입니다.
payment_intent_idstring아니요예Stripe PaymentIntent ID입니다.
  • 이 이벤트에는 plan_id가 없습니다.
  • 일회성 구매는 액세스가 부여된 뒤에 주문당 한 번 이 이벤트를 보냅니다.

subscription.renewed

구독이 성공적으로 갱신되었습니다. 필드는 payment.received의 구독 인보이스 형태와 같습니다.

  • 갱신하면 두 이벤트가 모두 전송되며, event_id 값은 서로 다릅니다. 두 이벤트를 모두 구독한다면 매출을 두 번 기록하지 마세요.

payment.failed

인보이스 결제가 실패했습니다. Stripe가 청구를 재시도할 때마다를 포함해, 실패할 때마다 전송됩니다.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
usernamestring아니요멤버가 더 이상 존재하지 않으면 생략됩니다.
emailstring아니요멤버가 더 이상 존재하지 않으면 생략됩니다.
invoice_idstring예Stripe 인보이스 ID입니다.
subscription_idstring예Stripe 구독 ID입니다.
amount_due_centsnumber예청구된 금액입니다.
currencystring예통화입니다.
attempt_countnumber예해당 인보이스에 대한 몇 번째 시도인지를 나타냅니다.

subscription.created

Stripe에서 구독이 생성되었습니다. 이는 결제가 완료되었다는 뜻이 아닙니다. 결제 여부는 payment.received로 확인하세요.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
usernamestring아니요멤버가 더 이상 존재하지 않으면 생략됩니다.
emailstring아니요멤버가 더 이상 존재하지 않으면 생략됩니다.
subscription_idstring예Stripe 구독 ID입니다.
plan_idhashid예멤버 구독 요금제 ID입니다.
statusstring예주요 값은 active, trialing, incomplete(첫 인보이스가 정산되지 않음)입니다.
price_centsnumber예할인 전 요금제의 단가입니다.
currencystring예통화입니다.
billing_periodstring예monthly 또는 annual입니다.
period_startRFC 3339예현재 기간의 시작입니다.
period_endRFC 3339예현재 기간의 끝입니다.

subscription.canceled

구독이 실제로 종료되었습니다. 멤버가 기간 종료 시점에 해지하도록 예약하는 것으로는 전송되지 않으며, 기간이 끝났을 때 전송됩니다.

필드타입항상 포함설명
member_idhashid예멤버 ID입니다.
usernamestring아니요멤버가 더 이상 존재하지 않으면 생략됩니다.
emailstring아니요멤버가 더 이상 존재하지 않으면 생략됩니다.
subscription_idstring예Stripe 구독 ID입니다.
plan_idhashid예멤버 구독 요금제 ID입니다.
previous_statusstring예종료되기 전의 상태이며, active, trialing, past_due 등이 있습니다.
cancelled_atRFC 3339예종료된 시각입니다.
  • 한 번도 결제되지 않은 채 만료된 구독도 previous_status가 incomplete인 상태로 이 이벤트를 보냅니다. 해당 멤버는 액세스를 가진 적이 없으므로 이탈로 집계하지 마세요.
  • 철자가 일관되지 않습니다. 이벤트는 canceled, 필드는 cancelled_at, 구독 상태 값은 cancelled입니다.

refund.issued

환불이 처리되었습니다. 구독 인보이스의 환불과 일회성 구매의 환불은 이 이벤트를 함께 쓰며 필드가 다릅니다. invoice_id가 있는지 여부로 구분할 수 있습니다.

필드타입구독 인보이스일회성 구매설명
member_idhashid예예멤버 ID입니다.
usernamestring대부분대부분멤버가 더 이상 존재하지 않으면 생략됩니다.
emailstring대부분대부분멤버가 더 이상 존재하지 않으면 생략됩니다.
amount_refunded_centsnumber예예지금까지 환불된 누적 금액이며, 이번 환불의 금액이 아닙니다.
currencystring예예통화입니다.
refund_statusstringrefunded 또는 partially_refundedrefunded환불 후의 상태입니다.
is_partialboolean예항상 false부분 환불인지 여부입니다.
previous_statusstring예: paid 또는 partially_refundedcompleted환불 전의 상태입니다.
invoice_idstring예아니요Stripe 인보이스 ID입니다.
charge_idstring예, 빈 문자열일 수 있음Stripe에서 시작한 환불에만 해당Stripe 청구(charge) ID입니다.
order_idhashid아니요예주문 ID입니다.
space_idhashid아니요예구매한 스페이스입니다.
payment_intent_idstring아니요예Stripe PaymentIntent ID입니다.
  • Stripe에서 처리한 일회성 구매의 부분 환불은 이벤트를 보내지 않으며, 주문과 액세스는 그대로 유지됩니다.

테스트 전송

관리자 콘솔의 웹훅 테스트는 요청을 즉시 한 건 보내고 결과를 전송 로그에 기록합니다. 꺼져 있는 웹훅도 테스트할 수 있으며 재시도하지 않습니다. 서명을 포함해 형식은 실제 전송과 같지만, 다음 항목은 다릅니다.

항목테스트 전송실제 전송
testtrue없음
event구독한 이벤트와 관계없이 항상 member.joined구독한 이벤트
data샘플 데이터실제 데이터
event_id, occurred_at없음있음

먼저 test를 확인하고, true이면 200을 반환하세요. 그렇지 않으면 member.joined 테스트가 수신 측 시스템에 실제로 멤버를 만들 수 있습니다.

다른 채널

전송 로그에는 이 소스도 함께 표시됩니다. 커스텀 웹훅과 같은 이벤트를 사용하되 형식이 다릅니다.

채널본문서명그 밖의 차이점
Zapierdata는 같습니다. 엔벨로프에는 불리언 test가 추가되고 webhook_id는 없습니다.연결할 때 서명 시크릿을 설정한 경우에만 서명됩니다. 알고리즘은 같습니다.X-Mateflow-Delivery-Id 헤더가 추가됩니다.
Slack, Discord읽을 수 있는 텍스트만 전송되며, Slack은 text, Discord는 content에 담깁니다.Zapier와 같습니다.X-Mateflow-Delivery-Id 헤더가 추가됩니다. 구조화된 필드는 없습니다.
자동화 규칙: 웹훅 호출규칙에 작성한 템플릿입니다.규칙에 시크릿이 있는 경우에만 서명됩니다. 알고리즘은 같습니다.이 엔벨로프를 사용하지 않습니다. 전송 시점은 규칙이 결정합니다.
Open API로 커뮤니티를 다시 호출하기

만들어 보세요

커뮤니티 관리자 콘솔에서 웹훅을 만들고, 테스트 전송을 보낸 다음, 로그에서 확인하세요.

14일 무료 체험 · 이후 회원 100명까지 Free 플랜 · 신용카드 불필요