변경 수신과 서명 검증
웹훅을 받는 프로그램인 수신기를 준비하고 변경 알림을 연결하는 가이드입니다. 먼저 연결 준비의 연동 계정과 대상 업무를 확인하세요. 이벤트·웹훅·상태 조회의 관계를 살펴본 뒤 아래 순서로 진행합니다.
연결 순서
섹션 제목: “연결 순서”- 고객사 · 수신기 준비: 서명 검증, 이벤트 저장과 중복 제거를 구현합니다. 초기 동기화·누락 복구에 사용할 저장소와 커서 처리도 준비합니다.
- 고객사·Maybox · 연결 요청: 공용 HTTPS 수신 URL을 준비한 뒤 담당자에게 등록을 요청합니다. 등록하면 전송이 활성화됩니다.
- 고객사 · 동기화: 초기 동기화와 누락 복구 순서로 현재 상태와 동기화 중 변경을 함께 반영합니다.
- 고객사 · 운영: 이벤트를 저장한 뒤 응답하고, 최신 상태 조회와 주기적인 누락 복구·재고 대조를 계속합니다.
알림 대상
섹션 제목: “알림 대상”arrival.changed · 도착
섹션 제목: “arrival.changed · 도착”도착 기록의 조회 권한 부여·회수, 상세, 입고 연결, 사진 변경을 알립니다. 최신 정보는 GET /api/v2/arrivals/{id}로 조회합니다. 조회 권한이 회수되면 resource.apiPath가 null이며 상세를 읽을 수 없을 수 있습니다.
inbound.changed · 입고
섹션 제목: “inbound.changed · 입고”입고 생성, 수량 등록, 확인 요청·확인, 입고 완료 수량 반영, 종료, 사진 변경을 알립니다. 최신 정보는 GET /api/v2/inbounds/{id}로 조회합니다. 확인 요청을 받아도 고객사가 최신 등록 내용을 조회·검토한 뒤 같은 버전 번호로 확인 API를 호출해야 합니다.
outbound.changed · 출고
섹션 제목: “outbound.changed · 출고”출고 생성, 포장, 인계 준비·계획, 출고 완료 수량 반영, 취소, 국내 운송장과 사진 변경을 알립니다. 최신 정보는 GET /api/v1/outbounds/{id}로 조회합니다.
입고·출고 변경 이벤트 자체를 요청 전체의 완료로 해석하지 않습니다. 최신 상세에서 입고 완료 수량(completedQuantity) 또는 출고 완료 수량(dispatchedQuantity)과 요청 상태를 함께 조회합니다. 부분 완료도 같은 변경 이벤트로 알려지며, 별도의 부분 완료 이벤트 종류를 가정하지 않습니다.
이 가이드에서 다루는 입고·출고 흐름의 이벤트입니다. 연결할 업무와 추가로 필요한 이벤트는 도입 시 담당자와 확인합니다. 본문은 최대 128KiB이며 상품 목록 등의 일부가 생략될 수 있습니다.
수신 URL 등록
섹션 제목: “수신 URL 등록”공용 인터넷에서 접근할 수 있는 HTTPS 443 포트의 수신 URL을 준비해 Maybox 담당자에게 등록을 요청합니다. 리다이렉트와 사설 주소는 지원하지 않습니다. 수신 URL 등록·변경·중지·재개는 담당자가 진행하며, 고객사가 직접 설정하는 공개 API는 없습니다.
수신 URL은 협의한 접근 범위마다 하나를 등록합니다. 대상 고객사와 연동 계정의 범위를 담당자와 확인하세요. 여러 시스템에 전달하려면 고객사에서 수신 후 분배합니다. API 인증 정보와 웹훅 서명 키는 서로 다른 값입니다. 등록하면 전송이 활성화되므로 서명 검증·이벤트 저장·초기 동기화를 준비한 뒤 연결을 요청합니다.
연결을 요청할 때는 대상 환경·고객사 연동 계정, 수신 URL, 수신기 준비 여부와 기술 담당자를 전달합니다. 서명 키의 전달 방법은 담당자와 별도로 맞추고 공개 문서·로그·공유 양식에 키를 넣지 않습니다.
서명 검증
섹션 제목: “서명 검증”Content-Type: application/jsonMaybox-Webhook-Id: <eventId>Maybox-Webhook-Key-Id: <서명 키 ID>Maybox-Webhook-Timestamp: <이번 전송 시각의 Unix 초>Maybox-Webhook-Signature: <HMAC SHA256의 base64 값>서명 계산 방식은 다음과 같습니다.
key = base64_decode(secret)message = utf8(timestamp + "." + eventId + ".") || original_body_bytessignature = base64(HMAC_SHA256(key, message))- JSON 파싱 전에 원래 HTTP body 바이트를 보관합니다.
- 알려진 키 ID와 헤더 형식을 확인하고 전송 시각의 허용 차이를 검사합니다. 허용 차이는 수신 측에서 정하며, 예를 들어 과거·미래 5분입니다.
- 계산한 값과 수신 서명을 상수 시간 비교합니다. 서명에
v1=접두사는 없습니다. - JSON을 파싱하고 헤더와 본문의
eventId가 같은지 확인합니다.
JSON을 다시 직렬화한 본문이나 base64로 디코딩하지 않은 secret으로 계산하면 서명이 맞지 않습니다. 수신한 이벤트가 어느 고객사의 데이터인지는 검증한 서명 키와 수신 서버의 설정으로 판단합니다. 웹훅 본문에는 tenantId가 포함되지 않습니다.
저장과 중복 제거
섹션 제목: “저장과 중복 제거”이벤트와 후속 처리의 대기 상태는 데이터베이스 등 서버가 재시작돼도 유지되는 저장소에 보관합니다. 다음 순서로 구현합니다.
수신 응답과 후속 처리의 경계
eventId를 환경·연동 계정과 묶어 고유하게 저장합니다. API 복구와 웹훅에서 같은 중복 제거 기록을 사용합니다.- 이미 저장한 이벤트라면 상태 갱신이나 알림 발송을 중복 실행하지 않고 수신 성공을 반환합니다.
- 저장 전이나 메모리 큐에만 넣고 성공을 반환하면 재시작할 때 유실될 수 있습니다.
- 같은 이벤트가 중복되거나 발생 순서와 다르게 도착할 수 있습니다. 과거 이벤트 본문으로 최신 상태를 덮어쓰지 않습니다.
sequence는 같은 고객사의 이벤트 순서를 나타내는 10진수 문자열로 보존하고, 비교에는BigInt등을 사용합니다. - 재시도마다 timestamp·서명이 바뀔 수 있으므로 이 둘은 중복 제거 키로 사용하지 않습니다.
- 알 수 없는 추가 필드는 허용합니다. 모르는 이벤트 종류·변화 코드는 기록한 뒤 상태 갱신이나 알림 발송을 실행하지 않고 지원 여부를 확인합니다.
연결 요청 전에 확인할 동작
섹션 제목: “연결 요청 전에 확인할 동작”- 같은 이벤트를 두 번 수신해도 이벤트를 중복 저장하거나 같은 후속 처리를 두 번 실행하지 않는다.
- 서명이 맞지 않는 요청을 업무에 반영하지 않는다.
- 저장 후 응답이 끊기거나 처리 프로그램이 재시작돼도 저장한 이벤트를 이어서 처리할 수 있다.
- 역순 이벤트를 받아도 과거 본문으로 최신 상태를 덮어쓰지 않는다.
아래 본문 예제로 수신 파싱과 후속 처리를 준비할 수 있습니다. 서명 검증을 함께 시험할 때는 테스트용 키·timestamp·원문 바이트로 위 계산식에 따라 서명을 만들어 요청합니다. 실제 연결의 전달 확인 방법과 창고 작업은 Maybox 담당자와 협의합니다.
이벤트 본문 예제
섹션 제목: “이벤트 본문 예제”다음은 입고 생성 시 전달되는 웹훅 본문 예제입니다. payload의 정보는 이벤트 발생 시점의 요약이며, 이후 변경까지 반영한 최신 상태가 아닙니다.
{ "schemaVersion": 1, "eventId": "c7c1f25d-cc59-4f1c-9b32-0634ecab22b6", "sequence": "42", "type": "inbound.changed", "changes": ["created"], "occurredAt": "2026-11-13T01:00:00.000Z", "resource": { "kind": "inbound", "id": "2001", "apiPath": "/api/v2/inbounds/2001" }, "actionRequired": false, "summary": "입고 2001 생성", "payload": { "inboundId": "2001", "externalReference": "DEMO-INBOUND-20261113-001", "status": "OPEN", "registrationRevision": 0, "registrationConfirmationRequest": null, "registrationConfirmation": null, "closedAt": null, "note": "테스트 입고 연동 확인", "lineCount": 1, "linesTruncated": false, "lines": [ { "sku": "DEMO-SERUM-001", "skuName": "데모 세럼 30ml", "brandName": "데모 브랜드", "expectedQuantity": 100, "registeredQuantity": 0, "completedQuantity": 0, "autoReservedQuantity": 0, "pendingAutoReservationQuantity": 0, "unitSpecification": null, "cartonSpecification": null, "unitMeasurement": null, "cartonMeasurement": null, "requestsUnitMeasurement": false, "requestsCartonMeasurement": false } ], "truncatedFields": [] }}eventId는 이벤트의 고유 식별자입니다. 값의 형식을 해석하거나 UUID라고 가정하지 않습니다. changes는 바뀐 항목을 나타냅니다. 입고 이벤트의 actionRequired: true는 현재 버전의 등록 내용에 고객사 확인 요청이 남아 있다는 뜻입니다. 이 경우에도 최신 입고 상세를 조회·검토한 뒤 같은 버전 번호로 확인 API를 호출합니다.
재시도와 중지 기간 복구
섹션 제목: “재시도와 중지 기간 복구”Maybox는 2xx 이외 응답과 네트워크 오류를 재시도합니다. 기본 간격은 5초→30초→2분→10분→30분→1시간이며 ±20% 변동을 적용합니다. Retry-After가 더 늦은 시각이면 그 값을 사용하고, 이벤트 발생 후 72시간 또는 실제 자동 시도 256회가 상한입니다.
전송 전체 타임아웃은 DNS를 포함해 5초입니다. 수신 측은 저장까지 짧게 처리하고 오래 걸리는 처리는 응답 뒤 수행합니다. 수신 중지 뒤 재개하면 재개 시점 이후의 이벤트를 전송합니다. 중지 기간의 이벤트는 API로 복구합니다.
다음 단계: 초기 동기화와 누락 복구를 연결한 뒤 운영 연결 준비에서 양측 확인 항목을 점검합니다.