外部システムとの連携
OpenAI などの API を呼ぶ、外部サービスの通知を受け取る、アプリ宛てのメールを処理するなど、用途に応じて連携方法を選べます。
| やりたいこと | 必要な対応 |
|---|---|
| 外部の API を呼ぶ | 接続情報や API キーを設定する |
| 外部サービスから通知を受ける | Webhook の受信先と、送信元を確認する方法を設定する |
| 社内の別システムからアプリの API を呼ぶ | アプリトークンと呼び出せるパスを設定する |
| アプリ宛てのメールを受け取る | メール受信を有効にし、受信処理を実装する |
| 外部 DB やストレージを使う | 接続情報を設定する |
外部の API を呼ぶ
Section titled “外部の API を呼ぶ”アプリから外部サービスへの通信に制限はありません。必要な API キーをシークレットに設定し、アプリから呼び出します。外部サービス側の契約や呼び出し回数の制限は、そのサービスの条件に従います。
一方、外部のシステムからアプリを呼ぶ場合は、既定で Keelson のログインが必要です。ブラウザでログインできないシステムから呼び出すには、以下の設定を行います。
Webhook を受ける
Section titled “Webhook を受ける”Webhook は、外部サービスで起きた出来事をアプリに通知する仕組みです。たとえば決済完了の通知を受けて、注文の状態を更新できます。
keelson.yaml の auth.endpoints に受信先のパスとメソッドを設定します。この設定は、指定したパスをブラウザのログインなしで呼べるようにするものです。誰からの呼び出しかは、署名やアプリトークンで確認します。
パスは /api/webhooks/ または /api/external/ で始まる必要があります。
auth: endpoints: - path: /api/webhooks/stripe methods: [POST]指定したパスの受信処理もアプリ側に実装し、その URL を送信元のサービスに登録します。これらのリクエストには、ログインユーザーの情報(X-Keelson-User-Id)は付きません。
- 送信元が署名を付ける場合(Stripe、GitHub、Slack など): 署名シークレットを
secretsで受け取り、署名を検証します - 送信元が署名を付けない場合: アプリトークン(次の節)を発行し、
X-Webhook-Secretヘッダーまたは URL/api/webhooks/<token>/...で受け取ります
宣言していないパスは、これまでどおりログイン必須のままです。Webhook のために必要なパスだけを開けてください。
なお /api/webhooks/email と /api/webhooks/email-events はプラットフォーム予約のため、独自のアプリ Webhook として宣言できません。メール受信を有効にすると、下記のとおりプラットフォームが /api/webhooks/email を直接呼びます。auth.endpoints への追加は不要です。
別のシステムからアプリの API を呼ぶ
Section titled “別のシステムからアプリの API を呼ぶ”社内の別システムやスクリプトからアプリの API を呼ぶ場合は、アプリトークンを発行します。
keelson apps tokens create --name batch-client --scope api --allowed-ip 203.0.113.0/24- トークンは
keelson_...の形式で、作成時に 1 回だけ表示されます --scope apiは/api/external/配下、--scope webhookは/api/webhooks/配下に対応します--allowed-ipで送信元 IP を絞れます(CIDR、複数可)。ワークスペースやアプリの IP 制御とは別に、トークン単位で適用されます- 同じ設定はコンソールのアプリ画面からも行えます。
keelson apps tokens list / rotate / deleteでローテーションと失効ができます
呼び出し側は Authorization: Bearer keelson_... を付けて、auth.endpoints で宣言したパスを呼びます。
curl -H "Authorization: Bearer keelson_xxx" https://acme--myapp.keelson.run/api/external/statusauth: endpoints: - path: /api/external/status methods: [GET] - /api/external/import # メソッド指定なし = すべてKeelson は、トークンが有効で対象アプリ・用途に合っていること、送信元 IP が許可されていること、パスとメソッドが宣言に合っていることを確認してから、アプリにリクエストを渡します。
メールを受け取る
Section titled “メールを受け取る”アプリ宛てのメールを受け取り、問い合わせの登録や添付ファイルの処理に使えます。Keelson がメールを受信し、内容をアプリの受信処理に渡します。
- 次の設定でメール受信を有効にします。
- アプリに
POST /api/webhooks/emailの受信処理を実装します。 - デプロイ後、コンソールに表示された受信アドレスへメールを送って確認します。
メール受信にはサーバー側の処理が必要です。静的サイトだけの構成では利用できません。
email: inbound: enabled: true既定は無効です。メールを送るだけ、または扱わないアプリでは設定しません。
デプロイに成功すると、アプリには <slug>@inbound.keelson.run というアドレスが割り当てられます。<slug> はアプリの slug です。現在の環境で実際に割り当てられたアドレスはコンソールに表示されます。
アプリに POST /api/webhooks/email を実装してください。このパスはプラットフォーム予約なので auth.endpoints には宣言しません。メールが届くと Keelson が直接呼び出します。
受信処理に渡される情報
Section titled “受信処理に渡される情報”| 項目 | 内容 |
|---|---|
delivery_id | 配信を識別する ID。同じメールの再配信を判別するために使います |
from / to | 差出人と宛先 |
subject | 件名 |
text / html | 本文 |
attachments | 添付ファイルの ID、ファイル名、サイズなど |
受信データの JSON 例
代表的なキーの抜粋です。追加のキーが届いても処理できるように実装してください。
{ "delivery_id": "del_01JEXAMPLE", "attempt": 1, "received_at": "2026-09-04T10:15:30Z", "from": { "name": "Ada Lovelace", "address": "ada@example.com" }, "to": [ { "name": "Support", "address": "my-app@inbound.keelson.run" } ], "cc": [], "reply_to": { "name": "Ada Lovelace", "address": "replies@example.com" }, "subject": "アカウントについての質問", "text": "プレーンテキスト部分の本文です。", "html": "<p>HTML 部分の本文です。</p>", "envelope_to": "my-app@inbound.keelson.run", "references": ["<earlier-message@example.com>"], "attachments": [ { "id": "att_01JEXAMPLE", "filename": "question.pdf", "content_type": "application/pdf", "size_bytes": 48231, "download_url": "https://example.invalid/temporary-download" } ]}受信メールの署名を検証する
Section titled “受信メールの署名を検証する”受信処理では、Keelson から届いた通知かどうかを SDK で確認してから、本文や添付ファイルを処理します。確認に使う署名シークレットは、KEELSON_EMAIL_WEBHOOK_SECRET としてアプリに渡されます。
署名の検証には、JSON に変換する前の、変更を加えていないリクエスト本文を使ってください。以下の例は検証成功後に 200、検証失敗時に 401 を返します。二重処理を防ぐ方法は次の節で説明します。
Python の実装例
import os
from fastapi import FastAPI, HTTPException, Request, Responsefrom keelson_email import EmailError, verify_webhook
app = FastAPI()secret = os.environ["KEELSON_EMAIL_WEBHOOK_SECRET"]
@app.post("/api/webhooks/email")async def receive_email(request: Request) -> Response: try: message = verify_webhook(await request.body(), request.headers, secret) except EmailError as exc: raise HTTPException(status_code=401, detail="invalid signature") from exc
# ここで message を処理します。 return Response(status_code=200)Node.js の実装例
import { createServer } from "node:http";import { verifyWebhook } from "@keelsonhq/email";
const secret = process.env.KEELSON_EMAIL_WEBHOOK_SECRET;if (!secret) throw new Error("KEELSON_EMAIL_WEBHOOK_SECRET is required");
createServer(async (request, response) => { if (request.method !== "POST" || request.url !== "/api/webhooks/email") { response.writeHead(404).end(); return; }
let message; try { message = await verifyWebhook(request, secret); } catch { response.writeHead(401).end(); return; }
// ここで message を処理します。 response.writeHead(200).end();}).listen(process.env.PORT ?? 3000);Go の実装例
package main
import ( "net/http" "os"
"github.com/keelsonhq/go-sdk/email")
func main() { secret := os.Getenv("KEELSON_EMAIL_WEBHOOK_SECRET") if secret == "" { panic("KEELSON_EMAIL_WEBHOOK_SECRET is required") }
http.HandleFunc("/api/webhooks/email", func(w http.ResponseWriter, r *http.Request) { message, err := email.VerifyWebhook(r, secret) if err != nil { http.Error(w, "invalid signature", http.StatusUnauthorized) return }
// ここで message を処理します。 _ = message w.WriteHeader(http.StatusOK) })
if err := http.ListenAndServe(":"+os.Getenv("PORT"), nil); err != nil { panic(err) }}同じメールが 2 回届くことに備える
Section titled “同じメールが 2 回届くことに備える”アプリが 200 を返す前に停止したり、応答が遅かったりすると、同じメールが再配信されることがあります(at-least-once 配信)。署名が正しくても、初めて届いたメールとは限りません。注文の登録や返信メールの送信などが二重に行われないようにしてください。
- 受信データの
delivery_id(配信イベントではevent_id)と処理状況を DB に保存し、処理済みなら再処理せず200を返す - ID の保存と業務データの更新は同じトランザクションで行う
- 処理中に同じ ID が来たら
503を返して再配信に回す
SDK の重複処理防止機能を使う場合
SDK には重複抑止の補助があります。Go は VerifyWebhookOnce / VerifyEventWebhookOnce に IdempotencyStore を渡します。Node.js / Python は setIdempotencyStore / set_idempotency_store で永続ストアを登録できますが、これは SDK の組み込みサーバーに効くもので、上の例のように verifyWebhook を自分で呼ぶ場合には適用されません。組み込みサーバーの既定の重複抑止はプロセス内メモリなので、再起動や複数インスタンスをまたいでは効きません。
外部データベース
Section titled “外部データベース”PostgreSQL、MySQL、自前の libSQL に接続する場合は db.mode: none にし、接続情報をシークレットで渡します。詳しくはデータベースを参照してください。
外部オブジェクトストレージ
Section titled “外部オブジェクトストレージ”Files / Media SDK の上限を超えるファイルは、S3 互換のストレージにアプリから直接保存します。認証情報はシークレットで渡します。