Webhook 엔드포인트가 뭔가요?
Webhook 엔드포인트(endpoint)는 노트, 요약, 문서가 바뀌는 순간 Tiro가 HTTP POST 요청을 보내는 여러분 애플리케이션의 URL이에요. Tiro Platform 에 엔드포인트를 한 번 등록해 두면, Tiro가 모든 이벤트를 실시간으로 그 주소로 보내줘요. API를 폴링할 필요가 없어요.
Webhook이란?
Webhook을 사용하면 워크스페이스에서 이벤트가 발생할 때 애플리케이션이 실시간 알림을 받을 수 있어요. API를 폴링하는 대신, 무언가 발생할 때마다 Tiro가 여러분의 endpoint로 HTTP POST 요청을 보내요.
Webhook 동작 방식
설정 : 애플리케이션에 webhook endpoint를 마련해요
등록 : 받고 싶은 이벤트를 위해 endpoint를 추가해요
수신 : 이벤트가 발생하면 즉시 알림을 받아요
처리 : 애플리케이션에서 이벤트 데이터를 처리해요
이벤트 & 리소스 구조
Webhook은 이벤트(Event) 와 리소스(Resource) 를 중심으로 구성돼요.
이벤트(Events)
이벤트는 워크스페이스에서 발생하는 동작을 나타내요. 자세한 내용은 Note Events , Note Document Events , Note Summary Events , FolderNote Events 를 참고하세요.
Webhook 이벤트는 메타데이터만 포함하도록 설계되어 보통 수백 KB 이하로 유지돼요. transcript나 스크립트 같은 대용량 콘텐츠는 webhook의 안정성과 성능을 보장하기 위해 별도의 API를 통해 접근해요.
리소스(Resources)
리소스는 이벤트가 작용할 수 있는 주요 엔터티를 나타내요. 현재 지원되는 항목은 다음과 같아요.
Note: 개별 노트 리소스
NoteDocument: 노트에서 생성된 템플릿 기반 문서
NoteSummary: 노트에 대한 AI 생성 요약
FolderNoteRelation: 폴더와 노트 사이의 관계
Webhook Payload 구조
모든 webhook 이벤트는 표준 Event Structure 구조를 따라요.
Webhook 요청은 Authorization 헤더의 secret key로 인증돼요.
Authorization: Bearer {secret_key}
secret key는 webhook endpoint를 설정할 때 제공되며, 들어오는 요청의 진위 여부를 검증하는 데 사용돼요. 제공된 secret을 설정해 둔 secret과 비교하기만 하면 진위를 확인할 수 있어요.
검증 예시
Node.js
Python
Go
Kotlin + Spring
function verifyWebhookAuth ( authHeader , expectedSecret ) {
const token = authHeader . replace ( 'Bearer ' , '' );
return token === expectedSecret ;
}
// Usage in your webhook endpoint
app . post ( '/en/developers/webhooks/tiro' , ( req , res ) => {
const authHeader = req . headers . authorization ;
if ( ! verifyWebhookAuth ( authHeader , process . env . WEBHOOK_SECRET )) {
return res . status ( 401 ). send ( 'Unauthorized' );
}
// Process webhook event
const event = req . body ;
handleWebhookEvent ( event );
res . status ( 200 ). send ( 'OK' );
});
import os
from flask import Flask, request
def verify_webhook_auth ( auth_header , expected_secret ):
if not auth_header or not auth_header.startswith( 'Bearer ' ):
return False
token = auth_header.replace( 'Bearer ' , '' )
return token == expected_secret
# Usage in your webhook endpoint
@app.route ( '/en/developers/webhooks/tiro' , methods = [ 'POST' ])
def handle_webhook ():
auth_header = request.headers.get( 'Authorization' )
if not verify_webhook_auth(auth_header, os.getenv( 'WEBHOOK_SECRET' )):
return 'Unauthorized' , 401
# Process webhook event
event = request.get_json()
handle_webhook_event(event)
return 'OK' , 200
import (
" encoding/json "
" net/http "
" os "
" strings "
)
func verifyWebhookAuth ( authHeader , expectedSecret string ) bool {
if ! strings . HasPrefix ( authHeader , "Bearer " ) {
return false
}
token := strings . TrimPrefix ( authHeader , "Bearer " )
return token == expectedSecret
}
// Usage in your webhook endpoint
func handleWebhook ( w http . ResponseWriter , r * http . Request ) {
authHeader := r . Header . Get ( "Authorization" )
if ! verifyWebhookAuth ( authHeader , os . Getenv ( "WEBHOOK_SECRET" )) {
http . Error ( w , "Unauthorized" , http . StatusUnauthorized )
return
}
// Process webhook event
var event WebhookEvent
json . NewDecoder ( r . Body ). Decode ( & event )
handleWebhookEvent ( event )
w . WriteHeader ( http . StatusOK )
}
@RestController
class WebhookController {
@Value ( " \$ {webhook.secret}" )
private lateinit var webhookSecret: String
private fun verifyWebhookAuth (authHeader: String ?, expectedSecret: String ): Boolean {
if (authHeader?. startsWith ( "Bearer " ) != true ) {
return false
}
val token = authHeader. removePrefix ( "Bearer " )
return token == expectedSecret
}
// Usage in your webhook endpoint
@PostMapping ( "/en/developers/webhooks/tiro" )
fun handleWebhook (
@RequestHeader ( "Authorization" ) authHeader: String ?,
@RequestBody event: WebhookEvent
): ResponseEntity < String > {
if ( ! verifyWebhookAuth (authHeader, webhookSecret)) {
return ResponseEntity. status (HttpStatus.UNAUTHORIZED). body ( "Unauthorized" )
}
// Process webhook event
handleWebhookEvent (event)
return ResponseEntity. ok ( "OK" )
}
}
전달 & 재시도
Method : HTTP POST
Content-Type : application/json
Timeout : 60초
Retries : exponential backoff로 최대 5회 재시도 (총 6회 시도)
Success : 모든 2xx HTTP 상태 코드
재시도 스케줄
15초
30초
5분
30분
2시간
시작하기
endpoint 마련 : POST 요청을 받을 수 있는 HTTP endpoint를 만들어요
webhook 설정 : Tiro Platform 에서 webhook endpoint를 등록해요
이벤트 처리 : 애플리케이션에서 들어오는 webhook payload를 처리해요
Tiro Platform 에서 webhook endpoint를 설정하세요.
Webhook endpoint는 워크스페이스 단위로 등록/관리돼요. Platform에서 endpoint를 추가하면 현재 워크스페이스의 이벤트가 그 endpoint로 전달돼요. 여러 워크스페이스를 운영한다면 워크스페이스마다 endpoint를 등록하고, payload 최상위의 workspaceGuid로 이벤트 출처를 구분하세요.
endpoint를 등록했다면, 받게 될 Event Structure 를 먼저 익혀 보세요. 그런 다음 Best Practices 를 읽고, 운영에 들어가기 전에 재시도, idempotency, 서명 검증을 처리하세요.