コンテンツにスキップ
コンソール →
公式サイト →
AI に聞くなら、この URL を貼る https://keelson.dev/ja/llms.txt

外部システムとの連携

OpenAI などの API を呼ぶ、外部サービスの通知を受け取る、アプリ宛てのメールを処理するなど、用途に応じて連携方法を選べます。

やりたいこと必要な対応
外部の API を呼ぶ接続情報や API キーを設定する
外部サービスから通知を受けるWebhook の受信先と、送信元を確認する方法を設定する
社内の別システムからアプリの API を呼ぶアプリトークンと呼び出せるパスを設定する
アプリ宛てのメールを受け取るメール受信を有効にし、受信処理を実装する
外部 DB やストレージを使う接続情報を設定する

アプリから外部サービスへの通信に制限はありません。必要な API キーをシークレットに設定し、アプリから呼び出します。外部サービス側の契約や呼び出し回数の制限は、そのサービスの条件に従います。

一方、外部のシステムからアプリを呼ぶ場合は、既定で Keelson のログインが必要です。ブラウザでログインできないシステムから呼び出すには、以下の設定を行います。

Webhook は、外部サービスで起きた出来事をアプリに通知する仕組みです。たとえば決済完了の通知を受けて、注文の状態を更新できます。

keelson.yamlauth.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 を呼ぶ場合は、アプリトークンを発行します。

Terminal window
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 で宣言したパスを呼びます。

Terminal window
curl -H "Authorization: Bearer keelson_xxx" https://acme--myapp.keelson.run/api/external/status
auth:
endpoints:
- path: /api/external/status
methods: [GET]
- /api/external/import # メソッド指定なし = すべて

Keelson は、トークンが有効で対象アプリ・用途に合っていること、送信元 IP が許可されていること、パスとメソッドが宣言に合っていることを確認してから、アプリにリクエストを渡します。

アプリ宛てのメールを受け取り、問い合わせの登録や添付ファイルの処理に使えます。Keelson がメールを受信し、内容をアプリの受信処理に渡します。

  1. 次の設定でメール受信を有効にします。
  2. アプリに POST /api/webhooks/email の受信処理を実装します。
  3. デプロイ後、コンソールに表示された受信アドレスへメールを送って確認します。

メール受信にはサーバー側の処理が必要です。静的サイトだけの構成では利用できません。

email:
inbound:
enabled: true

既定は無効です。メールを送るだけ、または扱わないアプリでは設定しません。

デプロイに成功すると、アプリには <slug>@inbound.keelson.run というアドレスが割り当てられます。<slug> はアプリの slug です。現在の環境で実際に割り当てられたアドレスはコンソールに表示されます。

アプリに POST /api/webhooks/email を実装してください。このパスはプラットフォーム予約なので auth.endpoints には宣言しません。メールが届くと Keelson が直接呼び出します。

項目内容
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"
}
]
}

受信処理では、Keelson から届いた通知かどうかを SDK で確認してから、本文や添付ファイルを処理します。確認に使う署名シークレットは、KEELSON_EMAIL_WEBHOOK_SECRET としてアプリに渡されます。

署名の検証には、JSON に変換する前の、変更を加えていないリクエスト本文を使ってください。以下の例は検証成功後に 200、検証失敗時に 401 を返します。二重処理を防ぐ方法は次の節で説明します。

Python の実装例
import os
from fastapi import FastAPI, HTTPException, Request, Response
from 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 / VerifyEventWebhookOnceIdempotencyStore を渡します。Node.js / Python は setIdempotencyStore / set_idempotency_store で永続ストアを登録できますが、これは SDK の組み込みサーバーに効くもので、上の例のように verifyWebhook を自分で呼ぶ場合には適用されません。組み込みサーバーの既定の重複抑止はプロセス内メモリなので、再起動や複数インスタンスをまたいでは効きません。

PostgreSQL、MySQL、自前の libSQL に接続する場合は db.mode: none にし、接続情報をシークレットで渡します。詳しくはデータベースを参照してください。

Files / Media SDK の上限を超えるファイルは、S3 互換のストレージにアプリから直接保存します。認証情報はシークレットで渡します。