← 全記事
チュートリアル

Devin DesktopにWebhookドキュメントを渡して20分でLINE受信ハンドラーを構築した話

Devin Desktop——2026年初にWindsurfからリブランドされ、SWE-1.6モデルが950トークン/秒を叩き出す——は今、周りの開発者が最も話題にしているAIコーディングツールです。使い方はシンプル:リファレンスドキュメントを渡して、欲しいものを説明し、動くまでイテレーションする。このワークフローがwebhook APIに対してどこまで通用するか試してみました。

タスク:LINEのメッセージをwebhookイベントとして受信し、構造化ログに出力するPythonサーバー——小規模チームがメッセージキューやチケットシステムの前段にデプロイするようなハンドラー。LINE公式アカウント不要、Messaging API認証情報不要、チャネルアクセストークン管理不要。インバウンドメッセージが正規化されたJSONで届けばそれで十分です。

完成するもの

FastAPIサーバー(約40行):

  1. UnifyPort統一webhookのmessage.receivedイベントを受信
  2. signing_secretでHMAC-SHA256署名を検証
  3. 各メッセージを構造化JSONでログ出力——provider、sender、text、timestamp
  4. すべてのイベントに200を返し、署名不正なら401

所要時間:約20分。UnifyPortワークスペースにLINEアカウントを接続(LINEアプリでQRスキャン)し、Python 3.10+が必要です。

なぜLINE Messaging APIを直接使わないのか

公式ルート:

  1. LINE公式アカウントを作成——LINE Business IDが必要、企業または本人確認済み個人の認証が必要
  2. LINE Developersコンソールでmessaging APIを有効化——チャネル設定、チャネルアクセストークン生成、webhook URL設定
  3. LINEのwebhook payloadを解析:イベント構造は{"events": [{"type": "message", "message": {"type": "text", "text": "..."}, "source": {"userId": "U..."}}]}——ネストされたLINE固有のフォーマット
  4. 署名を検証:チャネルシークレットでx-line-signatureを検証——LINEはBase64エンコードのHMAC-SHA256を使用(hexではない)
  5. リプライトークンを処理:各イベントに有効期限30秒のreplyTokenが付属——ウィンドウを逃すと返信はサイレントに失敗

コードは動きますが、LINE専用です。webhook payloadフォーマット、署名形式、リプライトークン機構——どれもWhatsAppやTelegramに流用できません。2つ目のプラットフォームを追加するには、ゼロから別の統合を構築する必要があります。

UnifyPortの非公式インターフェースは個人のLINEアカウントを接続——QRスキャン、公式アカウント不要——すべてのインバウンドメッセージを正規化されたmessage.receivedイベントとして配信します。WhatsApp、Telegram、TikTok、Zalo、Xと完全に同じフォーマット。一つのハンドラーで6プラットフォームをカバーします。

セットアップ:Devin DesktopにAPIリファレンスを渡す

Devin Desktopを開き、新しいセッションを作成。最初のプロンプトの前にコンテキストを提供:

  1. UnifyPort webhookドキュメントからmessage.receivedイベントpayloadを貼り付け:
    {
      "event": "message.received",
      "account_id": "acct_7kQnWx",
      "provider": "line",
      "from": "user_a3f82c",
      "text": "金曜日に変更できますか?",
      "timestamp": 1751270400,
      "message_id": "line_msg_5e9d21"
    }
  2. HMAC-SHA256署名検証セクションを貼り付け——x-unifyport-signatureヘッダー、signing_secretを使って生のリクエストボディからhexダイジェストを計算

Devinはコンテキストとしてペーストされたドキュメントをインデックスします。SWE-1.6 Fastが950 tok/sで処理するため、ドキュメントの消化は数秒。Cursor、Claude Code、Copilotも同じ方法で動きます——ペーストまたは添付。ツールは交換可能、ドキュメントが本質です。

ビルドプロセス

最初のプロンプト——ハンドラーの骨格:

先ほど貼り付けたUnifyPort webhookリファレンスを使って、FastAPIサーバーを書いてください。POST /webhookルート。イベントが”message.received”の場合、Pythonのloggingモジュールで構造化JSONとしてprovider、from、text、timestampをログ出力。すべてのイベントに200を返す。uvicornで起動。

Devinがコンテキストを読み取り生成:

from fastapi import FastAPI, Request
import logging, json

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("inbound")

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    evt = await request.json()
    if evt.get("event") == "message.received":
        logger.info(json.dumps({
            "provider": evt["provider"],
            "from": evt["from"],
            "text": evt["text"],
            "timestamp": evt["timestamp"],
            "message_id": evt["message_id"],
        }))
    return {"status": "ok"}

12行のハンドラー。LINE専用のimportなし、linebot SDKなし、チャネルアクセストークンなし。イベント構造が十分フラットなので、追加のパースは不要です。

2番目のプロンプト——署名検証:

HMAC-SHA256署名検証を追加。ヘッダーはx-unifyport-signature、シークレットは環境変数UNIFYPORT_SIGNING_SECRETから取得、HMACは生のリクエストボディバイトに対して計算——再シリアライズしたJSONではない。検証失敗は401を返す。タイミングセーフな比較を使用。

3番目のプロンプト——統合と整理:

署名検証をwebhookハンドラーに統合。検証通過後にbodyをJSONとしてパース。GET /healthヘルスチェックを追加。uvicorn.runブロック、ポート8000。

完成版のserver.py

from fastapi import FastAPI, Request, HTTPException
import hmac, hashlib, os, json, logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("inbound")

SIGNING_SECRET = os.environ["UNIFYPORT_SIGNING_SECRET"]

app = FastAPI()

@app.get("/health")
async def health():
    return {"status": "ok"}

@app.post("/webhook")
async def webhook(request: Request):
    body = await request.body()
    sig = request.headers.get("x-unifyport-signature", "")
    expected = hmac.new(
        SIGNING_SECRET.encode(), body, hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(sig, expected):
        raise HTTPException(status_code=401, detail="invalid signature")

    evt = json.loads(body)
    if evt.get("event") == "message.received":
        logger.info(json.dumps({
            "provider": evt["provider"],
            "from": evt["from"],
            "text": evt["text"],
            "timestamp": evt["timestamp"],
            "message_id": evt["message_id"],
        }))
    return {"status": "ok"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

40行未満。署名検証済み、構造化ログ出力、任意のリバースプロキシの背後にデプロイ可能。

起動してLINEメッセージの着信を確認

UnifyPortワークスペースでLINEアカウントを接続——LINEはQRコード認証に対応しているので、LINEアプリでスキャンすれば数秒で接続完了。LINE公式アカウント不要、Business ID不要、Messaging APIのチャネル設定不要。

サーバーに向けたwebhookエンドポイントを登録し、subscribed_events: ["message.received"]signing_secretを設定。サーバーを起動:

UNIFYPORT_SIGNING_SECRET=your_secret python server.py

友人からLINEにテストメッセージを送ってもらいます。イベントが到着:

{
  "event": "message.received",
  "account_id": "acct_3Xk1wL",
  "provider": "line",
  "from": "user_a3f82c",
  "text": "金曜日に変更できますか?",
  "timestamp": 1751270400,
  "message_id": "line_msg_5e9d21"
}

サーバーが署名を検証し、構造化JSONをログ出力。署名検証が失敗して401が返る場合——UNIFYPORT_SIGNING_SECRETがwebhookエンドポイントに設定した値と一致しているか確認してください。

WhatsAppを追加——コード変更ゼロ

同じワークスペースでWhatsAppアカウントを接続し、同じwebhookエンドポイントを購読。WhatsAppメッセージが同じフォーマットで到着:

{
  "event": "message.received",
  "account_id": "acct_7kQnWx",
  "provider": "whatsapp",
  "from": "user_d4f29a",
  "text": "注文確認、月曜日に出荷します",
  "timestamp": 1751270460,
  "message_id": "wa_msg_8b3e71"
}

同じハンドラー。同じ構造化ログ。providerフィールドが"line"から"whatsapp"に変わるだけで、コードパスは完全に同一。LINE Messaging APIのハンドラーにWhatsAppを追加するなら、まったく別の統合——異なるwebhook payload、異なる署名形式、異なるSDK——をゼロから構築する必要があります。このハンドラーは6プラットフォームに対応します。イベント形状が変わらないからです。

日本市場ではLINEが圧倒的ですが、インバウンドのカスタマーサポートや越境ECではWhatsAppやTelegramからのメッセージも届きます。プラットフォームごとに別の統合を維持するのではなく、一つのハンドラーですべてカバーする——Devin DesktopにUnifyPort APIリファレンスを渡して、ハンドラーを記述し、動くまでイテレーション。ツールはDevinでもCursorでもClaude Codeでも——リファレンスが同じなら、ハンドラーは40行以内で完成します。