20_API仕様
本章では、filix-shadowwork-api が提供する Application Programming Interface(API)の外部仕様を定義する。
各エンドポイントの目的、入力、出力、認証、およびエラー形式を示し、フロントエンドとの契約(contract)とする。
共通仕様
ベースURL
- 環境ごとに異なる(例: Cloudflare Workers のデプロイ先)
- 以降のパスはすべてベースURL配下の相対パスとして記載する
Content-Type
- リクエストボディがある場合は原則
application/json - レスポンスは原則
application/json
認証(Authentication)
本APIは、JWT による認証が必須です(/api/auth/exchange を除く)。
クライアントは以下の流れで認証を行います:
POST /api/auth/exchangeで Memberstack トークンを渡し、JWT(Cookie)を取得- 以後の API リクエストでは、Cookie に含まれる JWT が自動的に送信される
- バックエンドは JWT の署名を検証し、
subクレーム(= memberId)を用いてユーザーを特定
重要: すべてのエンドポイントで、クライアントは user_id パラメータを送信してはいけません。
ユーザーIDはすべて JWT から確定されます(認証なしでは処理されません)。
詳細は 50_セキュリティ の「認証方式(JWT + Cookie)」を参照。
注: 本章では API契約として「JWT で認証が必須」「user_id は送信不要」を記載する。
検証手順の詳細は50_セキュリティを参照。
利用権限(Authorization / 利用権限)
有料状態(paid)などの利用権限は、各機能のアクセス可否に影響する。
paid判定や webhook 反映の詳細は 40_課金と利用権限 に記載する。
クエリパラメータ
GETの取得系ではクエリパラメータを使用する場合がある(詳細は各エンドポイント)
共通エラー形式(Error response)
すべてのエンドポイントは、失敗時に共通の JSON 形式でエラーを返す。
{
"ok": false,
"error": {
"code": "BAD_REQUEST",
"message": "説明文(人間向け)"
}
}
ok: 成否フラグ(失敗時はfalse)error.code: エラー種別(例:BAD_REQUEST,UNAUTHORIZED,FORBIDDEN,NOT_FOUND,INTERNAL_ERROR)error.message: 画面表示やログのための説明文
HTTPステータスはエラー種別に応じて設定する(例: 400/401/403/404/500)。
※実装上のステータス割当は変更され得るが、成功時に ok: true を返すこと、失敗時に上記形式を返すことを契約とする。
代表的なエラー例
CORS 許可外 Origin(403)
{
"ok": false,
"error": {
"code": "FORBIDDEN",
"message": "origin not allowed"
}
}
外部APIタイムアウト等の上流失敗(502)
{
"ok": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "OpenAI fetch failed"
}
}
上記 502 は OpenAI/Stripe/Memberstack などの外部依存に起因する失敗を含む。
外部API呼び出しのタイムアウト制御は EXTERNAL_API_TIMEOUT_MS に従う。
エンドポイント一覧
認証
POST /api/auth/exchange
Memberstack トークンから JWT を発行。
ヘルスチェック
GET /
サービス稼働確認。
課金・利用権限
GET /api/paid
指定ユーザーの paid 状態を返す。POST /api/admin/set_paid
管理用途。指定ユーザーの paid 状態を更新する。
スレッド(thread)
POST /api/thread/start
スレッド開始。POST /api/thread/chat
LLM中継(平文を受け取り応答を返す。保存はしない)。POST /api/thread/message
暗号化済みメッセージを保存する。GET /api/thread/state
スレッド状態取得。POST /api/thread/close
スレッド終了。GET /api/thread/messages
スレッドのメッセージ一覧取得。
実行(run)
POST /api/run/start
実行開始。POST /api/run/restart
実行再開(続きから進める想定)。GET /api/runs/list
実行一覧取得。
スレッド一覧
GET /api/threads/list
スレッド一覧取得。
LLM(管理・検証用途)
POST /api/llm/ping
LLM疎通確認。POST /api/llm/respond
LLM応答生成(検証・開発用途を想定)。
エンドポイント仕様(詳細)
以下は「最低限の契約」を示す。
リクエスト/レスポンスの厳密なフィールド定義(JSON schema)は必要になった段階で追加する。
GET /
目的: 稼働確認。
認証: 不要。
成功レスポンス例:
{ "ok": true }
GET /api/paid
目的: 認証ユーザーの paid 状態を返す。
認証: 必要(方式は 50_セキュリティ)。
入力: なし(ユーザーは JWT から確定)
成功レスポンス例:
{ "ok": true, "paid": true }
POST /api/admin/set_paid
目的: 管理用途で paid 状態を更新する。
認証: 強い制限が必須(詳細は 50_セキュリティ)。
- JWT(Cookie)で管理者(memberId)を特定し、ADMIN_MEMBER_IDS allowlist に含まれること
- 管理トークンが一致すること(X-PAID-ADMIN-TOKEN: <token>)
入力: JSON(例)
{ "user_id": "xxx", "paid": true }
成功レスポンス例:
{ "ok": true }
POST /api/thread/start
目的: シャドウワークのスレッドを開始する。
認証: 必要。
入力: なし(ユーザーは JWT から確定)
成功レスポンス(例):
{ "ok": true, "thread_id": "t_xxx", "run_id": "r_xxx" }
POST /api/thread/chat
目的: AI応答生成のための中継。平文を受け取り、LLMに送信して応答を返す。
認証: 必要。
入力: JSON(例)
{ "message": "text" }
成功レスポンス(例):
{ "ok": true, "reply": "text", "thread_state": { } }
補足:
- 本エンドポイントは平文を永続化しない。
- LLM利用料金の抑制を目的とした入力文字数制限は、本エンドポイントの平文入力(message)に対して適用する。
- action: "next" 指定時はLLM呼び出しを行わず、次アクション用の文面を返す。
POST /api/thread/message
目的: 暗号化済みメッセージ(暗号文+メタ情報)を保存する。
認証: 必要。
入力: JSON(例)
{
"thread_id": "t_xxx",
"role": "user",
"client_message_id": "cm_xxx",
"ciphertext": "base64...",
"iv": "base64...",
"alg": "AES-256-GCM",
"v": 1,
"kid": "k1"
}
成功レスポンス(例):
{
"ok": true,
"thread_id": "t_xxx",
"message": {
"role": "user",
"client_message_id": "cm_xxx"
}
}
補足:
- サーバは平文を受け付けない。
- バックエンドは復号鍵を保持しないため、暗号文を復号できない(復号はフロントエンド側の責務)。
- client_message_id により保存リトライ時の重複登録を防ぐ(冪等)。
- 本エンドポイントの暗号文入力(ciphertext/iv/alg)は、LLM利用料金の制御対象ではない。
- 暗号文入力の検証は、保存API保護(異常入力・過大リクエスト対策)を目的として実施する。
client_message_id 採番規約(フロント実装)
- 1メッセージ(1回の保存対象)につき、クライアントが一意なIDを生成する。
- 形式は UUIDv4 などの十分なランダム性を持つ文字列を推奨する。
- 同じメッセージを再送する場合は、同じ
client_message_idを再利用する。 - 新しいメッセージを保存する場合は、必ず新しい
client_message_idを採番する。 client_message_idはメッセージ本文そのものや個人情報を含めない。
保存失敗時のUI状態(クライアント指針)
フロントは保存状態を明示し、少なくとも以下の状態を表現する。
saved: 保存済み(サーバがok: trueを返却)pending_retry: 保存失敗。再試行可能な状態not_saved: ユーザー離脱や再試行上限到達等で未保存が確定した状態
推奨フロー:
1. POST /api/thread/chat でAI応答を取得
2. 端末で暗号化し、POST /api/thread/message で保存
3. 保存失敗時は pending_retry を表示し、同じ client_message_id で再送
4. 最終的に保存できなかった場合は not_saved を表示
GET /api/thread/state
目的: スレッドの状態を取得する。
認証: 必要。
入力: なし(ユーザーは JWT から確定)
成功レスポンス(例):
{ "ok": true, "state": { } }
POST /api/thread/close
目的: スレッドを終了する。
認証: 必要。
入力: なし(ユーザーは JWT から確定)
成功レスポンス(例):
{ "ok": true }
GET /api/thread/messages
目的: スレッドに紐づくメッセージ一覧を取得する。
認証: 必要。
入力: クエリ(例)
- thread_id(必須)
- limit(任意)
成功レスポンス(例):
{
"ok": true,
"messages": [
{
"role": "user",
"client_message_id": "cm_xxx",
"ciphertext": "base64...",
"iv": "base64...",
"alg": "AES-256-GCM",
"v": 1,
"kid": "k1"
}
]
}
補足: - 返却は暗号文とメタ情報のみ(平文本文は返却しない)。
クライアント実装上の注意(メッセージ保存)
POST /api/thread/chatとPOST /api/thread/messageは分離された責務である。- AI応答成功は保存成功を意味しないため、保存結果は別途判定する。
- バックエンドは復号鍵を保持しないため、履歴暗号文の復号は行えない。
- 「直近 n 回 + 要約」をAIに送る場合、フロントは履歴暗号文を復号して組み立てる。
- 履歴復号に必要な鍵が無い/不正な場合は、AI送信前にユーザーへ再設定を促す。
POST /api/run/start
目的: 実行(run)を開始する。
認証: 必要。
入力: なし(ユーザーは JWT から確定)
成功レスポンス(例):
{ "ok": true, "run_id": "r_xxx" }
POST /api/run/restart
目的: 実行(run)を再開する。
認証: 必要。
入力: なし(ユーザーは JWT から確定)
成功レスポンス(例):
{ "ok": true, "run_id": "r_xxx" }
GET /api/runs/list
目的: 認証ユーザーの実行(run)一覧を返す。
認証: 必要。
入力: なし(ユーザーは JWT から確定)
成功レスポンス(例):
{ "ok": true, "runs": [ { } ] }
GET /api/threads/list
目的: 認証ユーザーのスレッド一覧を返す。
認証: 必要。
入力: クエリ(例)
- run_no(任意)
成功レスポンス(例):
{ "ok": true, "threads": [ { } ] }
POST /api/llm/ping
目的: LLM疎通確認。
認証: 原則必要(開発用途のため制限推奨)。
入力: JSON(必要なら)
{ "ok": true }
POST /api/llm/respond
目的: LLM応答生成(検証用途)。
認証: 原則必要(開発用途のため制限推奨)。
入力: JSON(例)
{ "input": "text" }
認証エンドポイント詳細
POST /api/auth/exchange
目的: Memberstack トークンから JWT(Access Token)を発行し、Cookie として返す。
認証: 不要(このエンドポイントのみ認証なし)
入力(JSON)
{
"token": "memberstack-login-token-here"
}
token(必須):Memberstack が発行したログイン証明トークン
処理 1. Memberstack API でトークンを検証し memberId を取得 2. 検証失敗時は 401 を返す 3. JWT を以下の仕様で生成 4. Cookie に設定して返す
出力(JSON)
{
"ok": true,
"member_id": "mem_xxxxx",
"token_type": "Bearer",
"expires_in": 900
}
Cookie設定
Set-Cookie: access_token=<JWT>; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=900
エラーレスポンス(例)
{
"ok": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid Memberstack token"
}
}
ステータスコード - 200:成功 - 400:リクエスト形式エラー(token がない等) - 401:認証失敗(Memberstack トークンが無効) - 500:サーバーエラー
````
成功レスポンス(例):
json
{ "ok": true, "reply": "text" }
備考(実装との整合性)
- 各リクエスト/レスポンスの詳細フィールドは、実装の進展に合わせて追記する。
- 本章は「詳細設計」ではなく、フロントとバックの契約として必要な事項に限定して記載する。