Webhook実装ガイド
HTTPS URLを登録すると、スマートマネーシグナル発火時にHMAC署名付きのリアルタイムイベントプッシュを受信できます(ポーリング不要)。このガイドでは登録、イベントフィルタリング、署名検証、リトライ動作について説明します。
概要
ポーリングや /v1/confirm シグナルフィードの代わりに、Webhookを登録すると、一致するシグナルが発火した瞬間にSmart Money APIがあなたのエンドポイントにイベントをPOSTします。各配信にはHMAC-SHA256署名が付与されているため、当社からの正当な配信であることを検証できます。
アウトバウンドWebhookは Pro およびEnterpriseプランで利用可能です。
Webhookの登録
POSTリクエストを /v1/webhooks に送信し、APIキーを X-API-Key ヘッダーに含めてください。リクエストボディには4つのフィールドが必要です:
| フィールド | タイプ | 説明 |
| url | 文字列 | イベントを受信する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登録完了。テストは POST /v1/webhooks/test で実行できます"
}
イベントフィルタ
登録内容とイベント名・シンボルが一致した場合に配信されます。典型的なイベント名は確認信頼度バケット — HIGH, MEDIUM, VETO — に加えて汎用 SIGNAL events。 ["*"] を使用すると全てのイベントまたはシンボルを受信できます。
配信とヘッダー
各配信はHTTP POST で、JSONボディと以下のヘッダーを含みます:
| ヘッダー | 値 |
| X-SmartMoney-Event | イベント名(例: HIGH) |
| X-SmartMoney-Signature | リクエストボディの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) hex
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());
// ... eventに基づいて処理 ...
res.status(200).end();
});
に対して検証 生の、未解析の リクエストボディ — 解析済みJSONを再シリアライズするとバイト順序やスペースが変わり、チェックが失敗する可能性があります。
リトライ
エンドポイントが 2xx を返さない場合(またはタイムアウト — 配信タイムアウトは10秒)、Smart Money APIは最大3回まで指数バックオフ(約1秒、4秒、16秒)でリトライします。ハンドラーを冪等にし、再配信されたイベントを安全に二重処理できるようにしてください。
インバウンドWebhook(TradingView)
別途、 インバウンド アラートを送信できます。 POST /v1/tradingview/webhook TradingViewアラートを受信し、 /confirmを通じて処理し、確認を返します。TradingViewはカスタムヘッダーを送信できないため、JSONボディ内の secret フィールドで認証されます( X-API-Keyではありません)。 secret, symbolを送信し、 direction (long/short);オプションで timeframe, strategy、および price.