イベント
Webhookとは、イベントが発生するとすぐに当社がそのイベントを送信する、お使いのサーバー上のアドレスです。イベントは次の六種類です。
form.response.created Eine neue Antwort auf eines deiner Formulare
form.response.updated Jemand hat seine Antwort über den Bearbeitungslink geändert
order.paid Eine Bestellung ist bezahlt
order.fulfilled Eine Bestellung ist versendet oder übergeben
order.canceled Eine bestätigte Bestellung wurde storniert
order.refunded Eine Bestellung wurde erstattetorder.refunded は、最初の一部返金時と全額返金時に送信されます。一度も支払われずに期限切れになったチェックアウトはキャンセルではないため、イベントは送信されません。
Webhookを設定する
プログラムが自らWebhookを登録することもできます。その場合は、webhooks:manage を持つキーで POST /v1/webhooks を呼び出します。さらに、Webhookが送信するデータの読み取り権限(フォームイベントには forms:read、注文には orders:read)も必要です。
- ダッシュボードで開発者エリアを開いてください。
- 「Webhook」に受信先のアドレスを入力してください。アドレスはhttpsで始まります。
- 「どのイベント?」で受け取りたいものを選択し、「Webhookを作成」をクリックしてください。
- その後表示されるシークレットをコピーし、受信先に設定してください。
- 「テストを送信」をクリックしてください。結果はすぐ下に表示されます。
届く内容
各イベントはJSONを含む POST です。data.object には、APIと同じ形式でオブジェクトが入ります。注文であれば GET /v1/orders/{id} と同じ形式、回答であれば回答一覧と同じ形式です。フォームイベントでは、data.form に、その回答がどのフォームに属するかも含まれます。
{
"id": "evt_Q2x9kLmP4vT7aB1cD3eF5gH7",
"object": "event",
"type": "order.paid",
"api_version": "2026-09-23",
"created_at": "2026-09-23T14:05:12Z",
"data": {
"object": {
"id": 5531,
"object": "order",
"number": 42,
"status": "confirmed",
"payment_status": "paid",
"currency": "EUR",
"total": 3890,
"customer": { "email": "mara@example.com", "name": "Mara Sol" },
"items": [
{ "name": "Kaffeebohnen, 500 g", "sku": "KAF-500", "quantity": 2, "unit_price": 1690 }
]
}
}
}署名を検証する
すべての呼び出しには、ヘッダー KENJTEC-Signature: t=<時刻>,v1=<署名> が付きます。署名は、時刻、ドット、変更されていないリクエスト本文を対象に、Webhookのシークレットをキーとして計算したHMAC-SHA256です。時刻が五分より前のものはすべて破棄してください。
JSONを読み込む前に、生の本文に対して検証してください。スペースが一つ違うだけでも、署名は異なるものになります。シークレットは「シークレットを表示」でいつでも確認できます。「シークレットを更新」を実行すると、古いシークレットは直ちに無効になります。
import crypto from "node:crypto";
// raw: the unparsed request body (Buffer), header: the KENJTEC-Signature header
export function verify(raw, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret)
.update(`${parts.t}.`).update(raw).digest("hex");
const given = String(parts.v1 ?? "");
return given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(given));
}import hashlib, hmac, time
def verify(raw: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))同じイベントが二回届くことがあります。たとえば、サーバーが応答したものの、その応答が当社に届かなかった場合です。その場合も id の値(ヘッダー KENJTEC-Event-Id にも含まれます)は変わりません。この値を記録し、各イベントを一度だけ処理してください。
届かない場合
十秒以内に 200~299 のステータスで応答があれば、配信済みとみなされます。リダイレクトには従いません。
イベントが届かない場合は、再試行します。1分後、5分後、30分後、2時間後、6時間後、12時間後、さらに24時間後に二回と、三日余りにわたって再試行します。五件のイベントが連続して最終的に届かなかった場合は、Webhookをオフにします。その際はダッシュボードに理由が表示され、「再びオンにする」でリセットできます。
「ログ」では、過去30日間のすべての配信をステータスと内容付きで確認できます。そこから「再送信」で、どのイベントでももう一度送信できます。

Webhookの送信先は、公開されたhttpsアドレスに限られます。内部ネットワーク内のアドレスや、KENJTEC自身のアドレスは受け付けません。