본문으로 건너뛰기

Webhook

폴링을 통해 같은 엔드포인트를 여러 번 요청하지 않고, webhook을 설정하여 여러분의 웹 서버 엔드포인트로 알림을 받아볼 수 있습니다.

info

Webhook 기능을 사용하시려면 Shakr의 고객성공팀에 연락하셔서 사용하시고자 하는 엔드포인트를 알려주세요. 등록이 완료되면 해당 엔드포인트로 POST 요청이 전달됩니다.

사용 가능한 webhook 목록

이벤트 이름설명
batch_show.status.changedBatchShow의 status가 변경됨
feed.status.changedFeed의 status가 변경됨
render_session.status.changedRenderSession의 status가 변경됨
render_session.external_upload.completeRenderSession에 지정한 external_upload (Automated Video Delivery)가 완료됨
template_style_version.status.changedTemplateStyleVersion의 status가 변경됨

예제

render_session.status.changed

RenderSession의 status가 변경되면 발생하는 이벤트입니다. RenderSession status의 목록은 다음과 같습니다.

Status설명
draftRenderSession이 생성되었고 편집을 할 수 있는 상태 (초기 상태)
preview_renderingRenderSession 렌더가 진행중인 상태
preview_renderedRenderSession 렌더가 완료된 상태

preview_rendered status는 RenderSession의 렌더가 완료되었다는 것을 의미하고, 해당 status인 경우 video_url을 통해 비디오 파일을 다운로드 받을 수 있습니다.

Webhook으로 전달되는 예시 페이로드는 다음과 같습니다.

{
"render_session": {
"id": "deJIKN",
"title": "나의 RenderSession",
"status": "preview_rendered",
...
"render_progress": 100,
"thumbnails": [...],
"video_url": "...",
...
}
}

feed.status.changed

Feed의 status가 변경되면 발생하는 이벤트입니다. Feed status의 목록은 다음과 같습니다.

StatusDescription
pendingFeed가 생성되고 처리중인 상태 (초기 상태)
postprocessingFeed에 추가적인 처리가 필요한 경우, 이를 진행중인 상태
readyFeed 처리를 완료하여 사용 가능한 상태
failedFeed 처리를 실패한 상태

Webhook으로 전달되는 예시 페이로드는 다음과 같습니다.

{
"feed": {
"id": "zknqQo",
"status": "ready",
"upload": { ... },
...
}
}

render_session.external_upload.complete

RenderSession을 생성할 때 external_uploads 파라미터를 지정하여 Automated Video Delivery 기능을 사용한 이후, 렌더가 완료되어 external_uploads 중 하나의 업로드가 완료되었을 때 발생하는 이벤트입니다. Automated Video Delivery에서 지원하는 모든 채널에서 webhook을 지원합니다.

각 채널별 예시 페이로드는 다음과 같습니다.

{
"render_session_id": "RENDER_SESSION_ID",
"to": "fbadact://act_123",
"remote_id": "CREATED_FACEBOOK_ADVIDEO_ID"
}

지정한 Facebook Ad Account에 새로 추가된 AdVideo ID를 remote_id 키를 통해 반환합니다.

batch_show.status.changed

BatchShow의 status가 변경되면 발생하는 이벤트입니다. BatchShow status의 목록은 다음과 같습니다.

Status설명
draftBatchShow가 생성되었고 편집을 할 수 있는 상태 (초기 상태)
preview_renderingBatchShow의 일부 결과물 렌더가 진행중인 상태 (프리뷰 렌더중)
preview_renderedBatchShow의 일부 결과물 렌더가 완료된 상태 (프리뷰 렌더 완료)
renderingBatchShow의 전체 결과물 렌더가 진행중인 상태
renderedBatchShow의 전체 결과물 렌더가 완료된 상태

Webhook으로 전달되는 예시 페이로드는 다음과 같습니다.

{
"batch_show": {
"id": "zbkqwA",
"title": "나의 BatchShow",
"status": "preview_rendered",
"rendered_outputs_count": 3,
"failed_outputs_count": 0,
"feed_items_count": 10,
...
}
}

template_style_version.status.changed

TemplateStyleVersion의 status가 변경되었을 때 발생하는 이벤트입니다. TemplateStyleVersion의 status 목록은 다음과 같습니다.

Status설명
createdTemplateStyleVersion이 생성된 상태 (초기 상태)
processingShakr에서 업로드된 프로젝트 파일을 분석하고 있는 상태
demo_renderingShakr에서 템플릿의 테스트 렌더링을 진행중인 상태
edit_readyTemplate Wizard에서 사용이 가능한 상태

서명 검증하기

여러분의 웹 서버의 webhook 엔드포인트는 외부에 노출되어 있기 때문에, 엔드포인트의 존재를 알게 된 외부 공격자가 악의적인 요청을 보낼 가능성도 있습니다. 이러한 공격을 방지하기 위하여, 모든 webhook 요청에는 SHA-256 서명이 포함되어 있어 요청이 Shakr에서 온 것인지를 직접 검증할 수 있습니다.

Webhook 요청의 X-Shakr-Signature 헤더를 통해 서명이 전달되고, 이 서명은 API 액세스 토큰을 발급받을 때 사용하는 client_id으로 다음과 같이 검증할 수 있습니다.

require "json"
require "rack"

digest = OpenSSL::Digest.new("sha256")

# Get your client_id, used when retrieving an access token
client_id = "YOUR_SHAKR_CLIENT_ID"

# Get "X-Shakr-Signature" header from Shakr API response
# Example: request.env["HTTP_X_SHAKR_SIGNATURE"] when using Sinatra
returned_signature = "HEADER_RETURNED_FROM_SHAKR_API"

# Get raw body returned from Shakr API response
# Example: request.body when using Sinatra
body = "BODY_RETURNED_FROM_SHAKR_API"

calculated_signature = OpenSSL::HMAC.hexdigest(digest, client_id, body)
calculated_signature_str = "sha256=#{calculated_signature}"

# Use secure comparison method to prevent timing attack
# Returns true if matches
Rack::Utils.secure_compare(returned_signature, calculated_signature_str)

서명을 비교하기 전에 sha256= 텍스트를 앞에 추가하는 것을 잊지 마세요.

타이밍 공격 방지하기

많은 프로그래밍 언어의 == 비교 연산자는 타이밍 공격(Timing Attack)을 방지해주지 않습니다. Ruby Rack의 secure_compare, PHP의 hash_equals, Python의 hmac.compare_digest와 같은 상수 시간 문자열 비교(Constant time string comparison) 연산을 사용하시기 바랍니다.