웹훅 만들기
POST
/api/v1/stations/{station_id}/webhooks
const url = 'https://dev.api.endueos.com/api/v1/stations/stn_01J9ZK3Q8M4XW7D2N5R6T0V1BC/webhooks';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"url":"https://example.com/hooks/endue-os","events":["device.status_changed","command.completed"]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://dev.api.endueos.com/api/v1/stations/stn_01J9ZK3Q8M4XW7D2N5R6T0V1BC/webhooks \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://example.com/hooks/endue-os", "events": [ "device.status_changed", "command.completed" ] }'서명 비밀은 이 응답에서만 나온다. Station 당 10개.
가이드 — 이벤트와 웹훅
파라미터
섹션 제목: “파라미터”경로 파라미터
섹션 제목: “경로 파라미터”station_id
필수
string
예시
stn_01J9ZK3Q8M4XW7D2N5R6T0V1BC요청 본문필수
섹션 제목: “요청 본문필수”Media typeapplication/json
object
url
필수
Https 만. 공인 호스트만 (사설·루프백 주소는 422 WEBHOOK_URL_NOT_ALLOWED).
string
events
필수
Array<string>
resource_ids
Array<string>
예시
{ "url": "https://example.com/hooks/endue-os", "events": [ "device.status_changed", "command.completed" ]}201
섹션 제목: “201”비밀값이 든 응답 — 캐시하지 않는다.
Media typeapplication/json
object
data
필수
object
webhook
필수
object
webhook_id
필수
string
station_id
필수
string
url
필수
string
events
필수
EventType 값 또는 command.* 같은 접두사 와일드카드. command.progress 는 웹훅으로 가지 않는다.
Array<string>
resource_ids
필수
Any of:
Array<string>
null
status
필수
string
consecutive_failures
필수
integer format: int32
created_at
필수
string format: date-time
updated_at
필수
string format: date-time
secret
필수
oswh_ + hex 64. 이 응답에서만 볼 수 있다.
전송마다 Endue-Signature: t={unix},v1={hex(HMAC-SHA256(secret, t + "." + body))} 와 Endue-Event-Id 를 싣는다.
수신자는 5분 넘은 t 를 거절하고 Endue-Event-Id 로 중복을 거른다 (최소 한 번 전달).
string
meta
필수
object
request_id
필수
string
timestamp
필수
string format: date-time
duration
서버 처리 시간(ms).
integer format: int32
예시
{ "data": { "webhook": { "webhook_id": "whk_01J9ZK3Q8M4XW7D2N5R6T0V1BC", "station_id": "stn_01J9ZK3Q8M4XW7D2N5R6T0V1BC", "resource_ids": [ "res_01J9ZK3Q8M4XW7D2N5R6T0V1BC" ], "status": "active" } }}응답 헤더
섹션 제목: “응답 헤더”Cache-Control
필수
string
400
섹션 제목: “400”요청 형식이 틀렸다 — VALIDATION_ERROR(details.errors[]) 또는 BAD_REQUEST. 본문은 오류 봉투.
401
섹션 제목: “401”인증이 없거나 무효다 — UNAUTHORIZED. 본문은 오류 봉투.
404
섹션 제목: “404”없거나 내 것이 아니다 — *_NOT_FOUND. 본문은 오류 봉투.
409
섹션 제목: “409”지금 상태와 충돌한다 — 코드는 위 설명을 본다. 본문은 오류 봉투.
422
섹션 제목: “422”형식은 맞지만 할 수 없다 — 코드는 위 설명을 본다. 본문은 오류 봉투.
429
섹션 제목: “429”한도를 넘었다 — Retry-After 초 뒤에 다시. 본문은 오류 봉투.
500
섹션 제목: “500”서버 오류 — INTERNAL_ERROR. 본문은 오류 봉투.
503
섹션 제목: “503”의존 서비스에 닿지 못했다 — 잠시 뒤 다시. 본문은 오류 봉투.