Webhooks API
Webhook を使用すると、Mercozy ストアでイベントが発生した際にリアルタイムで HTTP 通知を受け取れます。API をポーリングする代わりに、URL を登録すれば Mercozy が自動的にイベントデータを POST します。
クイックスタート
3 ステップで始められます:
ステップ 1:エンドポイントを登録
API キーを取得してください 設定 > 連携 > API キー. 管理者権限が必要です。Webhook 署名シークレット(whsec_xxx)はエンドポイント作成時に自動生成され、一度のみ表示されます。
curl -X POST "https://api.mercozy.com/api/v1/external/webhooks" \
-H "X-API-Key: mk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "My ERP Integration",
"url": "https://your-app.com/webhooks/mercozy",
"events": ["order.created", "order.cancelled", "payment.received"]
}'ステップ 2:イベントを処理
app.post('/webhooks/mercozy', (req, res) => {
// 1. Verify signature (see below)
const signature = req.headers['x-webhook-signature'];
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// 2. Process the event
const { type, data } = req.body;
switch (type) {
case 'order.created':
syncOrderToERP(data);
break;
case 'payment.received':
updateAccountingSystem(data);
break;
}
// 3. Respond quickly with 200
res.status(200).json({ received: true });
});ステップ 3:エンドポイントをテスト
curl -X POST "https://api.mercozy.com/api/v1/external/webhooks/{id}/test" \
-H "X-API-Key: mk_live_your_key_here"エンドポイント
/webhooks
登録済みのすべてのWebhookエンドポイントを一覧表示します。
/webhooks
イベント通知を受信するための新しいWebhookエンドポイントを登録します。
/webhooks/:id
既存のWebhookエンドポイントを更新します(URL、イベント、またはアクティブステータス)。
/webhooks/:id
Webhookエンドポイントを削除します。イベントは配信されなくなります。
フィールドリファレンス
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
id | string | いいえ | 一意のWebhook識別子(読み取り専用) |
name | string | はい | エンドポイントの説明的な名前 |
url | string | はい | Webhookイベントを受信するHTTPSエンドポイントURL |
events | string[] | はい | 購読するイベントタイプの配列 |
secret | string | いいえ | ペイロード検証用の署名シークレット(読み取り専用、作成時に生成) |
isActive | boolean | いいえ | Webhookがアクティブかどうか(デフォルト:true) |
createdAt | datetime | いいえ | ISO 8601タイムスタンプ(読み取り専用) |
利用可能なイベントタイプ
Mercozy は 6 カテゴリ、18 種類の Webhook イベントに対応しています。必要なイベントのみ購読してください。
注文
- order.created
新しい注文が作成された時に発火
- order.updated
注文内容が変更された時に発火
- order.cancelled
注文がキャンセルされた時に発火
- order.completed
注文が完了した時に発火
- order.status_changed
注文ステータスが変更された時に発火
決済
- payment.received
決済が完了または代引き回収時に発火
- payment.refunded
返金または決済キャンセル時に発火
商品
- product.created
新しい商品が追加された時に発火
- product.updated
商品情報が変更された時に発火
- product.deleted
商品が削除された時に発火
在庫
- stock.low
在庫がしきい値を下回った時に発火
- stock.updated
在庫数が変更された時に発火
顧客
- customer.created
新しい顧客が登録された時に発火
- customer.updated
顧客情報が更新された時に発火
配送
- delivery.batch_started
配送バッチが出発した時に発火
- delivery.batch_completed
バッチ内の全配送が完了した時に発火
- delivery.completed
個別の配送が完了した時に発火
- delivery.failed
配送の試行が失敗した時に発火
Webhookペイロード
各Webhook配信にはイベントタイプ、タイムスタンプ、関連するリソースデータが含まれます。
注文イベントペイロード
{
"id": "evt_clx1abc2def3",
"type": "order.created",
"timestamp": "2026-03-19T10:30:00Z",
"data": {
"id": "clx9order123",
"orderNumber": "ORD-1042",
"status": "PENDING",
"total": 4999,
"currency": "USD"
}
}決済イベントペイロード
{
"id": "evt_clx2pay4ghi5",
"type": "payment.received",
"timestamp": "2026-03-19T10:35:00Z",
"data": {
"orderId": "clx9order123",
"orderNumber": "ORD-1042",
"amount": 4999,
"currency": "USD",
"paymentMethod": "CARD",
"status": "CAPTURED"
}
}商品イベントペイロード
{
"id": "evt_clx3prod6jkl",
"type": "product.created",
"timestamp": "2026-03-19T11:00:00Z",
"data": {
"id": "clx9prod789",
"name": "Organic Coffee Beans",
"sku": "COF-001",
"price": 1299,
"status": "ACTIVE"
}
}配送イベントペイロード
{
"id": "evt_clx4del7mno",
"type": "delivery.completed",
"timestamp": "2026-03-19T14:22:00Z",
"data": {
"orderId": "clx9order123",
"orderNumber": "ORD-1042"
}
}HTTP ヘッダー
すべての Webhook リクエストには以下のヘッダーが含まれます:
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
Content-Type | string | はい | application/json |
X-Webhook-Signature | string | はい | HMAC-SHA256 署名:sha256={16進数ダイジェスト} |
X-Webhook-Timestamp | string | はい | Unix ミリ秒タイムスタンプ |
X-Webhook-ID | string | はい | 重複排除用のユニークなイベント ID |
User-Agent | string | はい | Mercozy-Webhook/1.0 (+https://www.mercozy.com/docs/webhooks) |
署名検証
すべての Webhook には HMAC-SHA256 署名を含む X-Webhook-Signature ヘッダーが付与されます。イベント処理前に必ずこの署名を検証し、リクエストが Mercozy からのものであることを確認してください。
Node.js
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, timestamp, secret) {
// Signature is computed over "timestamp.body"
const message = timestamp + '.' + rawBody;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware
app.post('/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-webhook-signature'];
const ts = req.headers['x-webhook-timestamp'];
if (!verifyWebhook(req.body, sig, ts, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process event...
res.status(200).json({ received: true });
});Python
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
# Signature is computed over "timestamp.body"
message = (timestamp + '.').encode() + payload
expected = 'sha256=' + hmac.new(
secret.encode(),
message,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
sig = request.headers.get('X-Webhook-Signature')
ts = request.headers.get('X-Webhook-Timestamp')
if not verify_webhook(request.data, sig, ts, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
# Process event...
return {'received': True}, 200PHP
<?php
function verifyWebhook(string $payload, string $signature, string $timestamp, string $secret): bool {
// Signature is computed over "timestamp.body"
$message = $timestamp . '.' . $payload;
$expected = 'sha256=' . hash_hmac('sha256', $message, $secret);
return hash_equals($expected, $signature);
}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
if (!verifyWebhook($payload, $signature, $timestamp, $webhookSecret)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process event...
http_response_code(200);
echo json_encode(['received' => true]);リトライポリシー
エンドポイントが 2xx 以外のステータスを返すかタイムアウト(10 秒)した場合、Mercozy は指数バックオフで再試行します:
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
試行 1 | いいえ | 即時 | |
試行 2 | いいえ | 遅延 ~1s | |
試行 3 | いいえ | 遅延 ~4s |
3 回の再試行が失敗すると、配信は失敗としてマークされます。ダッシュボードまたは API から手動で再試行できます。
ベストプラクティス
5 秒以内に応答 — 200 ステータスを即座に返し、イベントは非同期で処理してください。Mercozy のタイムアウトは 10 秒です。
署名を常に検証 — イベント処理前に X-Webhook-Signature ヘッダーを確認し、なりすましリクエストを防いでください。
重複処理(冪等性) — X-Webhook-ID ヘッダーで重複を排除してください。処理済みのイベント ID を保存し、既に処理したものはスキップしてください。
HTTPS エンドポイントを使用 — 本番環境では Webhook データの安全な転送のため HTTPS URL が必須です。
ログ記録と監視 — すべての受信 Webhook をログに記録し、失敗を監視してください。配信ログ API で配信状況を確認できます。