Webhook

분석된 결과를 원하는 callback URL로 전달해주는 기능으로, Session API에 Create Session API에서 파라미터에 적어주면, 아래와 같은 포맷으로 분석 결과를 전달합니다.

📘

안전한 처리를 위한 권장 사항 (멱등성 및 순서 보장)

네트워크 지연이나 재시도로 인해 이미 처리된 웹훅 요청이 중복 전달되거나, 이벤트 순서가 뒤바뀐 상태로 도착할 수 있습니다.시스템의 데이터 정합성을 보장하기 위해, 아래 가이드에 따라 이벤트의 멱등성과 순서 검증 로직을 구현해 주세요.

1. 중복 처리 방지 (이벤트 별 멱등성 키 설정)

웹훅의 중복 수신을 안전하게 처리하기 위한 고유 키(Unique Key) 기준은 이벤트 타입에 따라 다릅니다.

  • SESSION_COMPLETE
    • 수면 세션 종료 시, 세션당 한 번만 발생하는 이벤트입니다.
    • 고유 키 조합: session_id + event
  • INFERENCE_COMPLETE (실시간 수면 분석 완료 이벤트)
    • 한 세션 내에서 AI 모델의 수면 예측 결과가 갱신될 때마다 반복적으로 발생하는 이벤트입니다. 따라서 이벤트의 고유성을 보장하기 위해 시퀀스 번호까지 함께 관리해야 합니다.
    • 고유 키 조합: session_id + event + inference_seq_num

2. 이벤트 순서 보장 (Order Guarantee)

네트워크 환경에 따라 후속 이벤트가 선행 이벤트보다 먼저 도착할 수 있습니다. 특히 INFERENCE_COMPLETE 이벤트는 순서가 중요한 이벤트이므로, 수신 페이로드 내 inference_seq_num 값을 기준으로 이벤트의 선후 관계를 검증해야 합니다.

재시도 정책

웹훅 요청은 수신 서버가 2xx 상태 코드를 반환한 경우 성공으로 간주됩니다. 다음의 경우 Asleep은 자동으로 재시도합니다.

  • 응답 상태 코드가 408, 429 또는 5xx인 경우
  • 네트워크 연결이 끊기거나 타임아웃이 발생한 경우

재시도는 최대 15회까지 수행되며, 이후에는 웹훅 전송은 최종 실패 처리됩니다. 재시도 간격은 아래 시퀀스를 기반으로 동작합니다.

(10, 10, 30, 10, 10, 480, 10, 10, 2430, 10, 10, 7680, 10, 10) 

Request

Method

POST

Header

Field

Type

Description

x-api-key

String

데이터 업로드 또는 세션 종료 시 사용한 API Key

x-user-id

String

수면 세션을 생성한 user id

X-Asleep-Timestamp

String

웹훅 발송 시각 (Unix timestamp, 초) - 웹훅 시크릿 발급 시에만 포함

X-Asleep-Signautre

String

HMAC-SHA256 서명. 웹훅 시크릿 발급 시에만 포함 자세한 내용은 Webhook HMAC 서명 검증하기 참고

Body

Field

Type

Description

event

String (INFERENCE_COMPLETE,SESSION_COMPLETE)

Webhook 이벤트 타입
INFERENCE_COMPLETE: 5분 또는 40분 단위로 분석이 완료된 경우
SESSION_COMPLETE: 전체 세션 분석이 완료된 경우

version

String
(V1,V2,V3)

Webhook 버전
V1: v1.0 문서의 webhook 포맷을 따름
V2: v2.0 문서의 webhook 포맷을 따름
V3: 현재 버전의 webhook 포맷을 따름

data

Webhook Data Object

웹훅 데이터

INFERENCE_COMPLETE일 경우 Webhook Data Object

Field

Type

Description

user_id

String

user id

session_id

String

session id

seq_num

Int

오디오 데이터 업로드 순서 번호

inference_seq_num

Int

seq_num을 5분 단위로 변환한 번호
예) 사운드 데이터 업로드 시 seq_num 39이면 inference_seq_num은 3

sleep_stages

[Int]

직전 5분 단위의 수면 단계 결과. 가장 최근 분석된 10개의 값을 전달받음

snoring_stages

[Int]

직전 5분 단위의 코골이 결과. 가장 최근 분석된 10개의 값을 전달받음


{
    "event": "INFERENCE_COMPLETE",
    "version": "V3",
    "data": {
        "user_id": "G-20250115025029-vLErWBfQNtnfvgDccFOQ",
        "session_id": "20250115025029_fvivn",
        "seq_num": 39,
        "inference_seq_num": 3,
        "sleep_stages": [0], // omitted
        "breath_stages": null,
        "snoring_stages": [0] // omitted
    }
}

SESSION_COMPLETE일 경우 Webhook Data Object

FieldTypeDescription
dataSleep Data ObjectData API의 Get Session의 응답과 같은 형태 (user_id 만 추가되어 있음)

{
    "event": "SESSION_COMPLETE",
    "version": "V3",
    "data": {
        "timezone": "UTC",
        "peculiarities": ["NO_BREATHING_STABILITY"],
        "missing_data_ratio": 0.0,
        "user_id": "G-20250115025029-vLErWBfQNtnfvgDccFOQ",
        "session": {
            "id": "20250115025029_fvivn",
            "state": "COMPLETE",
            "start_time": "2025-01-15T02:50:29+00:00",
            "end_time": "2025-01-15T03:50:29+00:00",
            "unexpected_end_time": null,
            "created_timezone": "UTC",
            "sleep_stages": [0], // omitted
            "breath_stages": null, 
            "snoring_stages": [0] // omitted
        },
        "stat": {
            "sleep_time": "2025-01-15T03:05:29+00:00",
            "wake_time": "2025-01-15T03:26:29+00:00",
            "sleep_index": 50,
            "sleep_latency": 900,
            "wakeup_latency": 1440,
            "light_latency": 0,
            "deep_latency": null,
            "rem_latency": null,
            "time_in_bed": 3600,
            "time_in_sleep_period": 1260,
            "time_in_sleep": 1080,
            "time_in_wake": 180,
            "time_in_light": 1080,
            "time_in_deep": 0,
            "time_in_rem": 0,
            "time_in_stable_breath": null,
            "time_in_unstable_breath": null,
            "time_in_snoring": 0,
            "time_in_no_snoring": 1260,
            "sleep_efficiency": 0.3,
            "sleep_ratio": 0.86,
            "wake_ratio": 0.14,
            "light_ratio": 0.86,
            "deep_ratio": 0.0,
            "rem_ratio": 0.0,
            "stable_breath_ratio": null,
            "unstable_breath_ratio": null,
            "snoring_ratio": 0.0,
            "no_snoring_ratio": 1.0,
            "breathing_index": null,
            "breathing_pattern": null,
            "waso_count": 1,
            "longest_waso": 180,
            "sleep_cycle_count": 0,
            "sleep_cycle": null,
            "sleep_cycle_time": [],
            "unstable_breath_count": null,
            "snoring_count": 0
        }
    }
}