本文へスキップ

メインメニュー

無料で始めるログイン
外部ツールとの連携ボット開発者Webhook

Webhookボットサーバーを作成

Tabpleが送るリクエストの構造と署名の検証、8秒以内の同期応答とコールバックによる非同期応答を実装します。

約8分 2026年10月13日に更新

準備するもの

  • HTTPSでアクセスできるパブリックなアドレスのサーバー — localhost・プライベートIPはブロックされます。
  • ボットの登録時に作成したSecret — 署名の検証に同じ値を使います。
  • 8秒以内に応答するか、それ以上かかりそうな場合は自ら非同期コールバックに切り替えるハンドラー — 8秒を超えると、切り替えではなくタイムアウトによる失敗として処理されます。

リクエストの構造を理解する

1

受信するリクエスト

メンションが発生すると、Tabpleは登録されたWebhook URLにJSON本文をPOSTします。text はメンションを除いたユーザーのメッセージで、channel.kind はプロジェクトチャンネルなら project、DMなら dm(このとき project_id はnull)です。

POST <your-webhook-url>json
{
  "event": "message.created",
  "invocation_id": 482,
  "bot": { "id": 3, "name": "assistant" },
  "channel": { "id": 17, "kind": "project", "project_id": 12 },
  "invoker": { "id": 7, "name": "Geun" },
  "parent_message_id": 90412,
  "text": "이번 주 회의록 요약해줘",
  "callback_url": "https://tabple.com/api/bots/3/invocations/482/callback",
  "callback_token": "f3a9c1…(48자 hex)",
  "timestamp": "2026-06-10T02:14:33.120Z"
}
2

リクエストヘッダー

署名の検証に必要な値が、ヘッダーで一緒に渡されます。

http
Content-Type: application/json
X-Tabple-Bot-ID: 3
X-Tabple-Timestamp: 1781057673
X-Tabple-Signature: v1=2f8a41…(hex 64자)
User-Agent: Tabple-Bot/1.0
参考X-Tabple-Timestamp はUnix秒単位です。サーバーの時刻と5分以上ずれているリクエストはリプレイ攻撃の可能性があるため、拒否してください。

署名の検証(HMAC-SHA256)

1

署名の方式

署名の対象は <X-Tabple-Timestampの値>.<元のリクエスト本文> という文字列で、登録したSecretをキーとしてHMAC-SHA256を計算したhex値に v1= プレフィックスを付け、X-Tabple-Signature ヘッダーで送ります。

注意JSONをパースしてから再シリアライズした文字列ではなく、受信した元の本文そのままで署名を計算する必要があります。キーの順序や空白が変わると、検証に失敗します。
2

Node.jsでの検証例

Expressを前提とした全体の流れです。元の本文を保持するために、rawパーサーを使います。

server.mjsjavascript
import crypto from "node:crypto";
import express from "express";

const SECRET = process.env.TABPLE_BOT_SECRET;
const app = express();

function isValidSignature(rawBody, ts, signature) {
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const expected = "v1=" + crypto
    .createHmac("sha256", SECRET)
    .update(`${ts}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(signature ?? "");
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const rawBody = req.body.toString("utf8");
  const ts = req.header("X-Tabple-Timestamp");
  const signature = req.header("X-Tabple-Signature");
  if (!isValidSignature(rawBody, ts, signature)) {
    return res.status(401).json({ error: "invalid signature" });
  }

  const event = JSON.parse(rawBody);
  res.json({ reply: `${event.invoker.name}님, "${event.text}" 잘 받았어요!` });
});

app.listen(3000);

同期応答 — 8秒以内に返す

1

200 + reply

8秒以内に応答できる場合は、HTTP 200で reply 文字列を返してください。そのままボットのメッセージとして投稿されます。

HTTP 200 응답 본문json
{
  "reply": "이번 주 회의록 요약입니다. …",
  "attachments": [
    { "name": "summary.png", "url": "https://cdn.example.com/summary.png",
      "mime": "image/png", "size": 48211, "kind": "image" }
  ]
}
注意8秒以内に応答が届かない場合、呼び出しはタイムアウトとして処理され、チャットに「ボット応答失敗」のメッセージが投稿されます。

非同期応答 — コールバックPUT

1

まず202で受け付ける

LLMの呼び出しのように時間のかかる処理では、HTTP 202(または200 + { "pending": true })をすぐに返し、処理が終わったら、リクエストに含まれていた callback_url に結果を送ります。

2

コールバックPUTを送る

callback_url にPUTリクエストを送ってください。認証には、リクエストに含まれていた callback_token を X-Tabple-Callback-Token ヘッダーにそのまま入れます。本文は同期応答と同じ形式です。

async-reply.mjsjavascript
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const rawBody = req.body.toString("utf8");
  if (!isValidSignature(rawBody, req.header("X-Tabple-Timestamp"),
      req.header("X-Tabple-Signature"))) {
    return res.status(401).end();
  }

  const event = JSON.parse(rawBody);
  res.status(202).end();

  runLongTask(event.text).then(async (answer) => {
    if (!event.callback_url) return;
    await fetch(event.callback_url, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        "X-Tabple-Callback-Token": event.callback_token,
      },
      body: JSON.stringify({ reply: answer }),
    });
  });
});
3

コールバックの有効期間

コールバックトークンは、呼び出し時点から5分間、1回だけ有効です。期限切れのトークンや、すでに使用されたトークンで送ると404が返されます。

参考callback_url と callback_token は、サーバーの設定によってはnullになる場合があります。nullの場合は、同期応答のみを使ってください。

応答の制限とトラブルシューティング

  • reply にも通常のメッセージと同じ4,000文字の制限があり、超過分は切り捨てられます。
  • attachments は最大8件で、url はHTTPSのみ許可されます。kind は image または file で、それ以外の値は file として処理されます。
  • ボットサーバーが4xx/5xxを返すか8秒を超えると、チャットに「ボット応答失敗」のメッセージが投稿されます — エラー本文の冒頭も一緒に表示されるので、デバッグに活用してください。
  • reply のない200応答はエラーとして処理されます。返す内容がない場合も、短い案内文を返してください。
  • 登録したWebhook URLがリダイレクト(3xx)を返した場合は、追従せずにエラーとして扱います — 最終的なアドレスをWebhook URLとして直接登録してください。
  • 呼び出しの上限(ボット・呼び出し元・プロジェクト別)については、「チャットボットの接続」ガイドを参照してください。