Webhook 구현 가이드
HTTPS URL을 등록하고 스마트 머니 신호가 발생할 때 실시간으로 HMAC 서명된 이벤트 푸시를 받으세요 — 폴링 불필요. 이 가이드는 등록, 이벤트 필터링, 서명 검증 및 재시도 동작을 다룹니다.
개요
폴링 대신 /v1/confirm 또는 신호 피드 대신, 웹훅을 등록하면 Smart Money API가 매칭되는 신호가 발생하는 즉시 귀하의 엔드포인트로 이벤트를 POST합니다. 모든 전송은 HMAC-SHA256으로 서명되어 있으므로 당사에서 진짜로 발송되었음을 확인할 수 있습니다.
아웃바운드 웹훅은 Pro 및 Enterprise 플랜에서 이용 가능합니다.
Webhook 등록
POST 요청을 /v1/webhooks 에 API 키를 X-API-Key 헤더에 포함하여 보내세요. 본문에는 네 개의 필드가 필요합니다:
| 필드 | 타입 | 설명 |
| url | string | 이벤트를 수신할 HTTPS 엔드포인트 (반드시 https://) |
| events | array | 수신할 이벤트 이름, 예: ["HIGH","MEDIUM","VETO"] 또는 ["*"] |
| symbols | array | 필터링할 심볼, 예: ["BTC","ETH"] 또는 ["*"] |
| secret | string | 귀하의 서명 시크릿 — 최소 16자 이상. 해시되어 저장되며, 서명 검증을 위해 원본 값을 귀하 측에 보관하세요. |
curl -X POST https://api.smartmoneyapi.com/v1/webhooks \
-H "X-API-Key: sm_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/smartmoney",
"events": ["HIGH", "MEDIUM"],
"symbols": ["BTC", "ETH"],
"secret": "a-long-random-secret-16-plus-chars"
}'
{
"webhook_id": 42,
"url": "https://yourapp.com/webhooks/smartmoney",
"events": ["HIGH", "MEDIUM"],
"symbols": ["BTC", "ETH"],
"message": "Webhook registered. Test with POST /v1/webhooks/test"
}
이벤트 필터
전송은 등록된 이벤트 이름과 심볼이 일치할 때 발생합니다. 일반적인 이벤트 이름은 확인 신뢰도 버킷 — HIGH, MEDIUM, VETO — 및 일반적인 SIGNAL 이벤트입니다. 모든 이벤트 또는 모든 심볼을 수신하려면 ["*"] 를 사용하세요.
전송 및 헤더
각 전송은 HTTP POST 이며 JSON 본문과 다음 헤더를 포함합니다:
| 헤더 | 값 |
| X-SmartMoney-이벤트 | 이벤트 이름 (예: HIGH) |
| X-SmartMoney-서명 | HMAC-SHA256 헥스 다이제스트 (아래 참조) |
| Content-Type | application/json |
| User-Agent | SmartMoneyAPI-Webhook/1.0 |
{
"event": "HIGH",
"ts": "2026-07-01T18:22:05Z",
"symbol": "BTC",
"direction": "long",
"confidence": "HIGH",
"composite": 0.74,
"webhook_id": 42
}
응답은 어떤 것이든 2xx 2xx가 아닌 경우 (또는 타임아웃) 재시도가 트리거됩니다.
서명 검증
서명은 X-SmartMoney-Signature 요청 본문의 HMAC-SHA256 헥스 다이제스트입니다. HMAC 키는 등록한 시크릿의 SHA-256 헥스 다이제스트입니다. (원본 시크릿은 우리 측에서 해시된 상태로만 저장됩니다). 확인하려면: 키를 도출하고, 원본 본문을 HMAC 처리한 후, 상수 시간 비교를 통해 검증하세요. 실패한 요청은 모두 거절하십시오.
import hashlib, hmac
from flask import Flask, request, abort
app = Flask(__name__)
MY_SECRET = "a-long-random-secret-16-plus-chars" # 등록한 값
@app.post("/webhooks/smartmoney")
def receive():
raw = request.get_data() # 본문의 정확한 바이트
sig = request.headers.get("X-SmartMoney-Signature", "")
key = hashlib.sha256(MY_SECRET.encode()).hexdigest() # HMAC 키 = sha256(secret) 헥스
expected = hmac.new(key.encode(), raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
abort(401)
event = request.get_json()
# ... event["event"], event["symbol"], event["composite"]에 따라 동작 ...
return "", 200
import crypto from "crypto";
import express from "express";
const app = express();
const MY_SECRET = "a-long-random-secret-16-plus-chars";
// 원본 바디를 캡처하여 시그니처 검증 시 정확한 바이트를 사용합니다.
app.post("/webhooks/smartmoney", express.raw({ type: "*/*" }), (req, res) => {
const sig = req.get("X-SmartMoney-Signature") || "";
const key = crypto.createHash("sha256").update(MY_SECRET).digest("hex");
const expected = crypto.createHmac("sha256", key).update(req.body).digest("hex");
const ok = expected.length === sig.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body.toString());
// ... 이벤트 처리 ...
res.status(200).end();
});
다음에 대해 검증 원본, 파싱되지 않은 요청 바디 — 파싱된 JSON을 재직렬화하면 바이트 순서나 공백이 변경되어 검증이 실패할 수 있습니다.
재시도
엔드포인트가 응답을 반환하지 않으면 2xx (타임아웃 — 전송 타임아웃은 10초 — 발생 시) Smart Money API는 지수 백오프(약 1초, 4초, 16초)로 최대 3회 재시도합니다. 핸들러를 멱등성(idempotent)으로 설계해 재전송된 이벤트를 중복 처리해도 안전하도록 하세요.
인바운드 웹훅 (TradingView)
별도로, 당사에 인바운드 알림을 보낼 수 있습니다. POST /v1/tradingview/webhook TradingView 알림을 수신하면 이를 /confirm로 전달하여 확인 응답을 반환합니다. TradingView는 사용자 정의 헤더를 보낼 수 없으므로 JSON 본문의 secret 필드로 인증합니다(헤더가 아님). X-API-Key을 전송하고, secret, symbol을 포함하세요. direction (long/short선택적으로 timeframe, strategy를 추가할 수 있습니다. price.