インタビューアプリ API
WebRTC インタビュールーム機能を独立させた新基盤の公開 APIです。minedia-www のサーバーが、Session・参加トークン・録画・チャット履歴などを基盤と連携するためのサーバー間 REST API を定義します。
GitHub Issue #5317 の設計成果物 F(API 契約素案)を可視化したものです。シングルテナント想定(tenant 0 = minedia-www)。本書確定後に OpenAPI 3.1 (YAML) へ落とします。
対象スコープ
| プレーン | 通信 | 認証 | 状態 |
|---|---|---|---|
| コントロールプレーン | minedia-www サーバー → 基盤(サーバー間 REST) | API key | 本書で定義 |
| イベント通知 | 基盤 → minedia-www(Webhook) | HMAC 署名 | 本書で定義 |
ベース URL
すべてのエンドポイントは /api/v1 配下です。
https://interview.minedia.com/api/v1
エンティティ × 公開 API マトリクス
コア 16 エンティティが公開 API でどう露出するかの全体マップです。書き込み系は「調査 → 基盤」の連携点に限定し、ルーム内で発生する事実は読み取り専用 + Webhook で通知します。
| エンティティ | 公開 API での扱い | 主なエンドポイント | Webhook |
|---|---|---|---|
Session | ✅ CRUD(作成・逆引き・取得・更新・解放) | POSTGETPATCHDEL /sessions | W4 session.ended |
Room | ✅ 取得 + 一括失効(作成は Session に内包) | GET /rooms/{room_id}、POST …/revocations | — |
| 参加トークン | ✅ 発行・個別失効 | POST /room-access-tokens | — |
Participant | ✅ 読み取り + 個別 ban | GET …/participants、POST …/ban | W1 / W2 |
EntryHistory | ✅ 読み取り(純ログ) | GET …/entry-histories | W1 / W2 |
ChatMessage | ✅ 読み取り + 削除(運用 purge) | GET/DELETE …/chat-messages | — |
RoomEvent | ✅ 読み取り(純ログ) | GET …/room-events | — |
Recording | ✅ 読み取り + 署名 URL + 削除 | GET …/recordings、GET …/download-url | W3 recording.status_changed |
Transcription | ✅ 読み取り + 起動 | POST/GET …/transcriptions | W6 transcription.completed |
Handout | ✅ 登録・一覧・更新・削除 | POST/GET …/handouts | — |
ConnectionTest | ✅ 開始・履歴・結果取得 | POST/GET /connection-tests | W5 connection_test.completed |
Tenant / User | ❌ 非公開(基盤内部) | — | — |
認証
API key はテナント(サーバー)を認証する長命・回転式の資格情報です。ブラウザには絶対に露出させないでください。
すべてのリクエストの Authorization ヘッダに API key を付与します。形式は <key_id>.<secret> です。
認証ヘッダ
PoC では tenant 0 の鍵 1 本のみ。scope はなく「有効 / 無効」の二値です。失効・未指定・不正な鍵はすべて 401 UNAUTHORIZED を返します。
| ヘッダ | 説明 |
|---|---|
| Authorization必須 | ApiKey <key_id>.<secret> |
curl https://interview.minedia.com/api/v1/sessions \ -H "Authorization: ApiKey key_live_a1b2.sk_9f8e7d…"
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "API キーが無効です"
}
}
リソース識別子 / external_ref
基盤の全リソースは 単一のフィールド名 id で識別します。値の形式はリソース種別により UUID 文字列 または 整数 の 2 通りです。テナント側 PK との対応は external_type / external_id のペアで双方向解決します。
id値が UUID でもフィールド名は id とします(内部 DB カラムは uuid のまま)。関連参照も session_id / room_id のように _id 接尾辞で揃えます。
id の値形式(2 種類)
| 形式 | 対象リソース | 例 |
|---|---|---|
| ① UUID 文字列 | 通常リソース(Session / Room / Participant / RtcSession / Recording / Transcription / ChatChannel / ChatPost / Handout / ConnectionTest / User / Tenant) | "id": "0190a1b2-…" |
| ② 整数 | 高ボリュームのログ系(EntryHistory / ChatMessage / RoomEvent) | "id": 1001 |
① UUID 形式について
基盤採番の不透明文字列で、内部キーは UUIDv7(時刻ソート可能)。推測不能なため列挙攻撃の二次防御になり、テナント側はこの文字列をそのまま保持して逆引きに使います。
高ボリュームなログ系リソース(EntryHistory / ChatMessage / RoomEvent)は UUID を持たず、BIGINT の整数 を id として露出します(カーソルの内部キーにも使用)。それ以外のリソースが UUID 形式という原則に対する明示的な例外です。
external_ref(テナント側 PK との対応)
external_type / external_id のペアで、物理 FK を張らずにテナント側リソースと双方向解決します。
| リソース | external_type | 例 |
|---|---|---|
| Session | Slot / ExtemporarySlot | {"Slot","123"} |
| Participant | User / Employee / Operator | {"User","999"} |
| Handout | ProjectHandout | {"ProjectHandout","12"} |
テナント分離(L0)
他テナント所有のリソースと、存在しないリソースは、どちらも 404 NOT_FOUND を返します(存在秘匿。403 は返しません)。
PoC は 1 テナントですが、突合コードは最初から実装します。これにより、将来のマルチテナント化時にアクセス制御の挙動が変わらないことを保証します。
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "リソースが見つかりません"
}
}
レスポンス形式
成功・エラーともに success フラグで包むエンベロープ形式です(truffle-survey-v2 準拠)。
成功レスポンス
success: true と data を返します。本書のリソース表現・レスポンス例は原則として data の中身を示します(実レスポンスはエンベロープで包まれます)。
エラーレスポンス
success: false と error を返します。code は UPPER_CASE の機械可読コード、details[] はフィールド単位のエラー(details[].code も UPPER_CASE)。
/.well-known/jwks.json(RFC 7517 標準形)と /health / /health/ready(素の liveness / readiness)はエンベロープで包みません。204 No Content はボディなし。
日時フォーマット
ISO 8601 / UTC(例 2026-07-01T06:00:00Z)。
{
"success": true,
"data": { "id": "0190a1b2-…" }
}
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "入力値が不正です",
"details": [
{
"field": "ends_at",
"code": "BEFORE_STARTS_AT",
"message": "終了時刻は開始時刻より後である必要があります"
}
]
}
}
ページネーション
一覧系エンドポイントはカーソルページングです(truffle 準拠)。カーソルは採番時刻順の安定ソートを表す不透明 Base64 文字列で、クライアントは中身を解釈しません。
リクエストパラメータ
| パラメータ | 説明 |
|---|---|
| cursor任意 | 前ページレスポンスの next_cursor をそのまま渡す Base64 トークン。初回は省略 |
| limit任意 | 1〜100(default 20) |
レスポンスフィールド
| フィールド | 説明 |
|---|---|
| items[] | リソース表現の配列 |
| next_cursor | 次の cursor に渡す不透明 Base64。終端では null |
| has_more | 次ページの有無(boolean) |
内部キーは UUID リソースでは UUIDv7(時刻ソート可能)、ログ系では整数 id を用います。各一覧の既定ソート順は各エンティティの節に明記します。
{
"success": true,
"data": {
"items": [ { "id": "0190a1b2-…" } ],
"next_cursor": "eyJrIjoxMDQyfQ",
"has_more": true
}
}
curl "…/api/v1/sessions?cursor=eyJrIjoxMDQyfQ&limit=20" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
ステータスコード規約
400(スキーマ違反)と 422(業務ルール違反)を分離します。真の競合は 409 を維持します。
| HTTP | 用途 |
|---|---|
| 200 / 201 / 204 | 取得 / 作成 / 削除・失効(冪等) |
| 400 | スキーマ / パース違反(型・必須・enum・単一フィールドの範囲・ペア指定 / JSON parse 失敗)。INVALID_REQUEST / INVALID_JSON |
| 401 | API key 不正(UNAUTHORIZED) |
| 404 | 不存在 or 他テナント(L0 存在秘匿・NOT_FOUND) |
| 409 | 真の状態競合(解放済み・入室実績あり・ban・external_ref 重複など) |
| 413 | ファイルサイズ超過(FILE_TOO_LARGE) |
| 422 | スキーマ通過後の業務ルール違反(時間窓・録画未完・拡張子など。VALIDATION_ERROR 系) |
| 429 | レート制限(RATE_LIMIT_EXCEEDED・PoC では未実装・予約) |
型・必須・enum・単一フィールドの数値範囲・ペア指定など スキーマで弾けるもの = 400。スキーマは通るが時間窓・状態・録画進捗などの 意味的 / 横断的ルール違反 = 422。409 は「既に存在 / 既にその状態」という競合に限定します。
冪等性(Idempotency-Key)
ネットワーク再送による二重作成を防ぐため、すべての作成系 POST は Idempotency-Key ヘッダを標準採用します(Stripe 型)。
挙動
同一キーの再送は初回レスポンス(ステータス + ボディ)をそのまま返します(処理は 1 回だけ)。キーは Redis に保存(TTL = 24h 仮値)。
| ケース | 挙動 |
|---|---|
| 同一キー + 同一ボディ | 初回レスポンスを再生 |
| 同一キー + 異なるボディ | 422 IDEMPOTENCY_KEY_CONFLICT |
対象エンドポイント
すべての作成系 POST(/sessions、/room-access-tokens、/connection-tests、/handouts、/transcriptions、ban、revocations)。external_ref 一意などの自然キーは二次防御として併存します。
PoC では未指定リクエストも受理します(移行期互換)。将来必須化の余地あり。
curl -X POST …/api/v1/sessions \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-7425-40de-944b-…" \ -H "Content-Type: application/json" \ -d '{ … }'
Webhook(基盤 → minedia-www)
ルーム内で発生した事実は、基盤から minedia-www へ Webhook で push 通知します。全イベント共通のエンベロープと HMAC 署名を持ちます。
署名検証
| ヘッダ | 説明 |
|---|---|
| X-Interview-Signature | sha256=<HMAC-SHA256(webhook_secret, timestamp + "." + body)> |
| X-Interview-Timestamp | unix 秒。受信側は署名検証 + 許容差 ±5 分を確認 |
配信仕様
| 観点 | 仕様 |
|---|---|
| 冪等性 | id で重複排除(リトライで同一イベント再送あり) |
| リトライ | 2xx 以外で指数バックオフ・最大 5 回(1m / 5m / 30m / 2h / 12h) |
| 順序 | 順序保証なし。occurred_at で並べ替えること |
| スキーマ版数 | version で破壊的変更を吸収。未知 version は安全に無視 / 隔離 |
イベント一覧
| ID | type | 説明 |
|---|---|---|
| W1 | participant.entered | 入室(出欠判定材料) |
| W2 | participant.left | 退室 |
| W3 | recording.status_changed | 録画状態変化 |
| W4 | session.ended | セッション終了 / 解放 |
| W5 | connection_test.completed | 接続テスト完了 |
| W6 | transcription.completed | 文字起こし完了 |
{
"id": "evt_0190a210-…",
"version": "1",
"type": "participant.left",
"occurred_at": "2026-07-01T06:01:12Z",
"data": { }
}
エラーコード
機械可読な code(UPPER_CASE)と HTTP ステータスの一覧です。各エンドポイント固有のエラーは、それぞれのエンティティ節にも記載します。
| HTTP | code | 意味 |
|---|---|---|
| 400 | INVALID_REQUEST | 型・必須・enum・範囲・ペア指定などのスキーマ違反 |
| 400 | INVALID_JSON | JSON パース失敗 |
| 400 | UNKNOWN_ROLE | 未知 / 不正なロール値(基盤がゲートキープ) |
| 401 | UNAUTHORIZED | API key 不正・未指定・失効 |
| 404 | NOT_FOUND | 不存在 or 他テナント(存在秘匿) |
| 409 | DUPLICATE_EXTERNAL_REF | 同一 external_ref の重複作成 |
| 409 | SESSION_ALREADY_STARTED | 入室実績ありの Session の時間枠変更 |
| 409 | ACTIVE_TOKENS_EXIST | 有効な参加トークンが存在 |
| 409 | SESSION_RELEASED | 解放済み Session への操作 |
| 409 | PARTICIPANT_BANNED | ban 済み参加者へのトークン再発行 |
| 409 | RECORDING_NOT_AVAILABLE | 録画が利用可能状態でない |
| 413 | FILE_TOO_LARGE | ファイルサイズ超過 |
| 422 | VALIDATION_ERROR | 業務ルール違反(汎用) |
| 422 | INVALID_TIME_WINDOW | 開始 ≧ 終了、過去終了、上限超過などの時間窓違反 |
| 422 | OUTSIDE_TIME_WINDOW | 入室可能時間外 |
| 422 | UNSUPPORTED_FILE_TYPE | 許可されない拡張子 |
| 422 | IDEMPOTENCY_KEY_CONFLICT | 同一キーで異なるボディ |
| 429 | RATE_LIMIT_EXCEEDED | レート制限(PoC 未実装・予約) |
Session
現 Slot / ExtemporarySlot の「時間枠 + アクセストークン + RTC 構成」を汎用化したリソースです。1 Session = 1 Room のため、Room は Session 作成時に同時生成され、レスポンスに内包されます。
RTC 構成・文字起こし構成は「呼び出し側テナントが作成時に渡す入力」です。Project / Slot 設定からの解決は minedia-www 側の責務であり、基盤は受け取った値を保存します。
旧エンティティとの差分(slots / extemporary_slots → sessions)クリックで展開
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(sessions) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記(1 つの識別子ではない)。id=内部 BIGINT 主キー(非公開・コア内 FK 用)/ tenant_id=テナントスコープ(PoC は 0 固定)/ uuid=公開識別子(グローバル一意・API では id として露出)。※DB の id と API の id は別物 |
| slots.id / extemporary_slots.id | → 移行 | external_type / external_id | 出所 PK を external_ref 化。例 {"Slot","123"}。FK ではない |
| room_access_token | → 移行 | access_token | グローバル一意・ランダム |
| start_time | → 移行 | starts_at | — |
| end_time | → 移行 | ends_at | — |
| extemporary_slots.name | → 移行 | name | 任意 |
| — (As-Is は中止を調査側 AnswerSlot.status のみで保持) | 🆕 新設 | status | scheduled / active / cancelled / closed。Room 解放・入室可否・締切を基盤が単一根拠で判定 |
| — | 🆕 新設 | rtc_engine | opentok / livekit / zoom。新基盤の既定は LiveKit |
| extemporary_slots.archive_mode / Project 設定 | → 移行 | archive_mode | 呼び出し側が作成時に渡す入力に統一 |
| archive_resolution(Project / 即席) | → 移行 | archive_resolution | 640x480 / 1280x720 |
| stream_resolution(Project / 即席) | → 移行 | stream_resolution | — |
| stream_frame_rate(Project / 即席) | → 移行 | stream_frame_rate | 30 / 15 / 7 / 1 |
| slots / extemporary_slots.transcription_language | → 移行 | transcription_language | 当初「調査残置」→ G5 で文字起こしコア化に伴いコアへ(default ja-JP) |
| Project.transcription_service | → 移行 | transcription_service | 自動文字起こしの既定エンジン(openai 等) |
| slots.project_id / limit / segment / memo、extemporary_folder_id / memo | ❌ 対象外 | — (調査に残置) | パネル募集・定員・セグメント・即席フォルダ・メモは調査ドメインの語彙 |
| created_at / updated_at | → 移行 | 同左 | — |
Session オブジェクト
Slot / ExtemporarySlotnull)opentok / livekit / zoom。新基盤の既定は livekitalways / manual(既定 manual)640x480 / 1280x720(既定 640x480)320x240 / 640x480 / 1280x720(既定 320x240)30 / 15 / 7 / 1(既定 15)ja-JP)。録画完了後の自動文字起こしの既定言語eleven_labs / openai / azure / anyenv / rimo(既定 openai)scheduled(開始前)/ active(時間窓内)/ ended(時間窓終了)/ released(解放済み)id / session_id / token_epoch / connect_url)。1 Session = 1 Room で常に同時生成{
"id": "0190a1b2-…",
"external_type": "Slot",
"external_id": "123",
"name": null,
"access_token": "k3jH9x…",
"starts_at": "2026-07-01T05:00:00Z",
"ends_at": "2026-07-01T06:00:00Z",
"rtc_engine": "livekit",
"archive_mode": "always",
"archive_resolution": "640x480",
"stream_resolution": "640x480",
"stream_frame_rate": 15,
"transcription_language": "ja-JP",
"transcription_service": "openai",
"status": "scheduled",
"room": {
"id": "0190a1b3-…",
"session_id": "0190a1b2-…",
"token_epoch": 0,
"connect_url": "https://interview.minedia.com/rooms/0190a1b3-…"
},
"created_at": "2026-06-10T09:00:00Z"
}
Session を作成する
minedia-www の Project / Slot 作成時(webrtc_engine = 新App 選択時)と即席調査(ExtemporarySlot)作成時に呼びます。Room が同時生成され、レスポンスに内包されます。
ボディパラメータ
| パラメータ | 説明 |
|---|---|
| external_type必須 string(≤64) | tenant 0: Slot(本番)/ ExtemporarySlot(即席) |
| external_id必須 string(≤64) | テナント側 PK(整数も文字列化) |
| starts_at必須 datetime | 枠の開始時刻 |
| ends_at必須 datetime | 枠の終了時刻 |
| name任意 string(≤255) | 表示名 |
| rtc_engine任意 enum | opentok / livekit / zoom(default livekit) |
| archive_mode任意 enum | always / manual(default manual) |
| archive_resolution任意 enum | 640x480 / 1280x720(default 640x480) |
| stream_resolution任意 enum | 320x240 / 640x480 / 1280x720(default 320x240) |
| stream_frame_rate任意 enum(int) | 30 / 15 / 7 / 1(default 15) |
| transcription_language任意 string(≤16) | 文字起こし言語(BCP 47・default ja-JP) |
| transcription_service任意 enum | 文字起こしエンジン(default openai) |
エラー
| 条件 | レスポンス |
|---|---|
| external_type / external_id 必須・ペア指定 / enum 不正 | 400 INVALID_REQUEST |
[tenant_id, external_type, external_id] 重複 | 409 DUPLICATE_EXTERNAL_REF |
| starts_at ≧ ends_at / ends_at が過去 / 枠が 24h 超 | 422 INVALID_TIME_WINDOW |
成功時は 201 Created と Location: /api/v1/sessions/{session_id} を返します。
curl -X POST …/api/v1/sessions \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…" \ -H "Content-Type: application/json" \ -d '{ "external_type": "Slot", "external_id": "123", "starts_at": "2026-07-01T05:00:00Z", "ends_at": "2026-07-01T06:00:00Z", "rtc_engine": "livekit", "archive_mode": "always" }'
{
"id": "0190a1b2-…",
"external_type": "Slot",
"external_id": "123",
"access_token": "k3jH9x…",
"starts_at": "2026-07-01T05:00:00Z",
"ends_at": "2026-07-01T06:00:00Z",
"rtc_engine": "livekit",
"archive_mode": "always",
"status": "scheduled",
"room": {
"id": "0190a1b3-…",
"token_epoch": 0,
"connect_url": "https://interview.minedia.com/rooms/0190a1b3-…"
},
"created_at": "2026-06-10T09:00:00Z"
}
Session を一覧 / 逆引きする
テナント側 PK(Slot.id 等)から基盤の Session を逆引きします(「テナント → 基盤」方向の双方向解決)。既定ソートは created_at 降順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| external_type任意 | 逆引きキー(external_id とペア指定) |
| external_id任意 | 逆引きキー(external_type とペア指定) |
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
エラー
| 条件 | レスポンス |
|---|---|
| external_type / external_id の片方のみ指定 | 400 INVALID_REQUEST |
curl "…/api/v1/sessions?external_type=Slot&external_id=123" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{ "id": "0190a1b2-…", "external_type": "Slot", "external_id": "123", "status": "scheduled" }
],
"next_cursor": null,
"has_more": false
}
Session を取得する
id(UUID 形式)で 1 件取得します。Room を内包したリソース表現を返します。
エラー
| 条件 | レスポンス |
|---|---|
| 不存在 or 他テナント | 404 NOT_FOUND |
curl …/api/v1/sessions/0190a1b2-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"id": "0190a1b2-…",
"external_type": "Slot",
"external_id": "123",
"status": "active",
"room": { "id": "0190a1b3-…", "token_epoch": 0 }
}
Session を更新する
作成時のフィールドを部分更新します(external_type / external_id は変更不可)。JSON Merge Patch(RFC 7396)準拠で、リクエストにキーが存在するフィールドだけ更新します。
更新セマンティクス
| リクエストでの現れ方 | 挙動 |
|---|---|
| キーを省略 | そのフィールドは変更しない |
キーあり・値 null | クリア(null 化)。nullable なのは name のみ |
| キーあり・値あり | その値に更新(作成時のバリデーション適用) |
エラー
| 条件 | レスポンス |
|---|---|
| starts_at / ends_at / rtc_engine 変更時、入室実績(EntryHistory)が 1 件以上 | 409 SESSION_ALREADY_STARTED |
| 同上の変更時、有効な参加トークンが存在 | 409 ACTIVE_TOKENS_EXIST |
| 解放済み(released)Session | 409 SESSION_RELEASED |
| 時間窓 / enum 制約違反 | 400 / 422 |
割付後リスケ不可などは調査ドメイン側の業務ルールです。基盤はトークン・入室実績との整合のみ強制します。
curl -X PATCH …/api/v1/sessions/0190a1b2-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Content-Type: application/merge-patch+json" \ -d '{ "ends_at": "2026-07-01T06:30:00Z", "name": "コンセプトA 追加ヒアリング" }'
{
"id": "0190a1b2-…",
"name": "コンセプトA 追加ヒアリング",
"ends_at": "2026-07-01T06:30:00Z",
"status": "scheduled"
}
Session を解放する
パネルのキャンセル・欠席時に、先行作成した Session / Room を解放します。
副作用
| 対象 | 挙動 |
|---|---|
| 参加トークン | Room の token_epoch を +1 し、発行済み参加トークンを全失効 |
| 接続中の参加者 | メディア切断 |
| 既発生の事実データ | 録画・チャット・入退室履歴は削除しない(事後参照可能) |
| Webhook | W4 session.ended(reason: "released")を送信 |
成功時は 204 No Content。解放済みへの再実行は冪等で 204 を返します。
curl -X DELETE …/api/v1/sessions/0190a1b2-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
204 No Content
Webhook: session.ended
セッションの時間窓終了、またはテナントによる解放時に送信されます。振り返りフェーズ開始の起点として利用できます。
| reason | 意味 |
|---|---|
expired | 時間窓の自然終了(entity 側 closed) |
released | テナントによる解放・キャンセル(entity 側 cancelled) |
API 露出名 ended / released と entity-design 側の closed / cancelled の語彙対応は、OpenAPI 化時に一本化予定です。
{
"id": "evt_0190a210-…",
"version": "1",
"type": "session.ended",
"occurred_at": "2026-07-01T06:00:00Z",
"data": {
"session_id": "0190a1b2-…",
"session_external_ref": { "external_type": "Slot", "external_id": "123" },
"room_id": "0190a1b3-…",
"reason": "expired",
"ended_at": "2026-07-01T06:00:00Z"
}
}
Room
WebRTC の接続先となる RTC ルーム(集約ルート)です。1 Session = 1 Room で、Session 作成時に同時生成され Session に従属します。単体の作成 / 削除 API はなく、取得とトークンの一括失効のみを公開します。
Room の生成は Session 作成に、解放は Session 解放に内包されます。本リソースは、生成済みの Room を id で取得すること、および発行済み参加トークンを世代単位で一括失効させること(強制退室)に責務を限定します。
旧エンティティとの差分(rooms → rooms)クリックで展開
| 旧フィールド | 種別 | 新フィールド | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記。id=内部 BIGINT 主キー(非公開)/ tenant_id=テナントスコープ/ uuid=公開識別子(API では id として露出) |
| slot_id / extemporary_slot_id(二股FK) | → 移行 | session_id | 二股 FK を sessions.id へ一本化。出所の多態は Session.external_type が吸収(コア内部 FK) |
| stream_resolution | → 移行 | stream_resolution | default 320x240 |
| stream_frame_rate | → 移行 | stream_frame_rate | default 15 |
| — | 🆕 新設 | token_epoch | Room 単位の参加トークン世代。一括失効(revocations)/ Session 解放で +1 し旧世代を全失効。JWT epoch クレームと突合 |
| created_at / updated_at | → 移行 | 同左 | — |
Room オブジェクト
id。1 Session = 1 Room320x240 / 640x480 / 1280x720(既定 320x240)。Session 作成時の入力を継承30 / 15 / 7 / 1(既定 15)0){
"id": "0190a1b3-…",
"session_id": "0190a1b2-…",
"stream_resolution": "640x480",
"stream_frame_rate": 15,
"token_epoch": 0,
"connect_url": "https://interview.minedia.com/rooms/0190a1b3-…",
"created_at": "2026-06-10T09:00:00Z"
}
Room を取得する
id(UUID 形式)で Room を 1 件取得します。現在の token_epoch や接続先 URL を参照する用途に使います。
エラー
| 条件 | レスポンス |
|---|---|
| 不存在 or 他テナント | 404 NOT_FOUND |
curl …/api/v1/rooms/0190a1b3-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"id": "0190a1b3-…",
"session_id": "0190a1b2-…",
"stream_resolution": "640x480",
"stream_frame_rate": 15,
"token_epoch": 0,
"connect_url": "https://interview.minedia.com/rooms/0190a1b3-…",
"created_at": "2026-06-10T09:00:00Z"
}
トークンを一括失効する
「このルームの全員を蹴る」操作です。Room の token_epoch を +1 し、発行済みの全参加トークンを一括で無効化します。接続中の参加者はメディアを切断されます。Session は解放しません(解放を伴う場合は Session 解放を使います)。
revocation は名詞リソースとしてモデル化しています(token_epoch の世代を 1 つ進める作成操作)。単体取得エンドポイントは持たないため、Location は影響先の Room を指します。再送防止のため Idempotency-Key ヘッダに対応します。
ボディパラメータ
| パラメータ | 説明 |
|---|---|
| reason必須 string(1..255) | 失効理由。RoomEvent(room.tokens_revoked)として監査記録されます |
エラー
| 条件 | レスポンス |
|---|---|
| reason 必須 / 長さ違反 | 400 INVALID_REQUEST |
| 不存在 or 他テナント | 404 NOT_FOUND |
成功時は 201 Created と Location: /api/v1/rooms/{room_id} を返します。ボディには更新後の token_epoch を含みます。
curl -X POST …/api/v1/rooms/0190a1b3-…/revocations \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…" \ -H "Content-Type: application/json" \ -d '{ "reason": "進行終了のため全員を退室" }'
{
"room_id": "0190a1b3-…",
"token_epoch": 3
}
参加トークン
永続エンティティではなく発行物です。発行の副作用として Participant が生成・紐付けされます。現行の room_access_token(Slot 単位)と force_access_token(緊急バイパス)は本 API に収斂します(DEC-05)。
テナントのサーバーが、自テナントで認証済みの参加者を participant に変換して発行を依頼します(委譲信頼・Model A)。基盤はテナントの認証結果を信頼し、participant トークン(JWT)を発行します。署名は EdDSA(kid 付き)で、公開鍵は JWKS(/.well-known/jwks.json)から取得します。
トークン発行レスポンス
kid 付き)。ルーム接続時にこのトークンで認可DELETE /room-access-tokens/{jti})の単体アドレスnbf に対応・ISO 8601 / UTC)exp に対応)id / room_id / role / display_name / external_type / external_id を内包(匿名参加者は external_type / external_id が null)JWT クレーム
発行される token に含まれるクレームです。署名は EdDSA(kid 付き・公開鍵は JWKS)。
| クレーム | 説明 |
|---|---|
| iss | 発行者(基盤) |
| aud | 受信者(ルームランタイム) |
| sub | 主体(participant) |
| tenant | テナント(PoC は 0 固定) |
| room | 対象 Room の id |
| participant | Participant の id |
| role | panelist / moderator / observer / minedia_observer |
| name | 表示名(発行時に封入・以後変更不可。なりすまし防止) |
| exp | 失効時刻(expires_at) |
| nbf | 有効開始時刻(not_before) |
| iat | 発行時刻 |
| jti | トークン一意 ID(個別失効の検証キー) |
| epoch | 発行時点の Room 世代(token_epoch)。一括失効の検証に使用 |
| pepoch | 発行時点の Participant 世代。ban の再入室阻止検証に使用(G1) |
{
"token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImludGVydmlldy0yMDI2LTA2In0...",
"jti": "8f14e45f-...",
"not_before": "2026-07-01T04:30:00Z",
"expires_at": "2026-07-01T05:15:00Z",
"room_connect_url": "https://interview.minedia.com/rooms/0190a1b3-...",
"participant": {
"id": "0190a1d0-...",
"room_id": "0190a1b3-...",
"role": "panelist",
"display_name": "山田太郎",
"external_type": "User",
"external_id": "999"
}
}
トークンを発行する
テナントのサーバーが、委譲信頼で自テナントの認証済み参加者の発行を依頼します(Model A)。Idempotency-Key ヘッダに対応します。緊急発行は force: true で時間窓チェックをスキップします。
リクエストボディ
| フィールド | 説明 |
|---|---|
| room_id必須 uuid | 対象ルーム |
| role必須 enum | panelist / moderator / observer / minedia_observer(DEC-03 正典) |
| display_name必須 string · 1..64 | 表示名。発行時に JWT の name クレームへ封入(以後変更不可・なりすまし防止) |
| participant.external_type任意 string · ≤64 | 参加者のテナント側 identity。tenant 0: User / Employee / Operator |
| participant.external_id任意 string · ≤64 | 同上。両方 null =匿名参加者(即席調査の匿名入室を許容) |
| ttl_seconds任意 integer | 60〜3600(既定 900) |
| force任意 boolean | 緊急発行(既定 false)。時間窓チェックをスキップ |
クレーム導出規則
| クレーム | 通常 | force: true |
|---|---|---|
| nbf | max(now, starts_at − 30min) | now |
| exp | min(now + ttl_seconds, ends_at + 30min) | now + ttl_seconds |
| epoch | 発行時点の Room token_epoch | 同左 |
| pepoch | 発行時点の Participant token_epoch(ban 検証用・G1) | 同左 |
エラー
| 条件 | レスポンス |
|---|---|
自テナント外の room_id(不存在含む・存在秘匿) | 404 NOT_FOUND |
未知 / 不正な role(基盤がゲートキープ) | 400 UNKNOWN_ROLE |
display_name / ペア指定 / ttl_seconds 範囲などのスキーマ違反 | 400 INVALID_REQUEST |
時間窓外(force: true のみスキップ) | 422 OUTSIDE_TIME_WINDOW |
| 解放済み Session の Room(force でも不可) | 409 SESSION_RELEASED |
| ban 済み Participant への再発行(force でも不可・G1) | 409 PARTICIPANT_BANNED |
レスポンスは 201 Created(Location: /api/v1/room-access-tokens/{jti})。
curl -X POST "https://interview.minedia.com/api/v1/room-access-tokens" \ -H "Authorization: ApiKey key_live_a1b2.sk_9f8e7d…" \ -H "Idempotency-Key: 7c9e6679-7425-40de-944b-…" \ -H "Content-Type: application/json" \ -d '{ "room_id": "0190a1b3-…", "role": "panelist", "display_name": "山田太郎", "participant": { "external_type": "User", "external_id": "999" }, "ttl_seconds": 900 }'
{
"token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImludGVydmlldy0yMDI2LTA2In0...",
"jti": "8f14e45f-...",
"not_before": "2026-07-01T04:30:00Z",
"expires_at": "2026-07-01T05:15:00Z",
"room_connect_url": "https://interview.minedia.com/rooms/0190a1b3-...",
"participant": {
"id": "0190a1d0-...",
"room_id": "0190a1b3-...",
"role": "panelist",
"display_name": "山田太郎",
"external_type": "User",
"external_id": "999"
}
}
トークンを個別失効する
jti をブラックリスト(Redis・TTL =残存 exp)に登録し、その 1 トークンだけを取り消します(誤発行の撤回・一時的な切断)。当該トークンで接続中なら切断します。
jti 失効は再発行で素通りします(発行は新しい jti を返すため)=再入室は阻止しません。恒久的に締め出すには Participant の ban(token_epoch++ + status: banned)を使ってください。jti 失効=一時的取り消し、ban=恒久的締め出し、という役割分担です。
エラー
| 条件 | レスポンス |
|---|---|
他テナント発行 / 不存在の jti(存在秘匿) | 404 NOT_FOUND |
レスポンスは 204 No Content(期限切れ済みへの実行も冪等で 204)。自テナント発行の jti のみ対象です。
curl -X DELETE "https://interview.minedia.com/api/v1/room-access-tokens/8f14e45f-…" \ -H "Authorization: ApiKey key_live_a1b2.sk_9f8e7d…"
204 No Content
Participant
「参加者 × 役割」を Room 単位で束縛したリソースです。生成は参加トークン発行(§4.3.1)の副作用であり、直接の作成 API はありません。公開 API は読み取り+個別 banのみで、リアルタイムの参加者一覧はランタイム側が担います。
参加者の身元はテナントが保証し、基盤は per-room の役割束縛・external_ref・表示名スナップショットだけを持ちます。テナント委譲の参加者は external_type / external_id、基盤運用者(minedia_observer)は内部 user_id で表現し、両系統は排他です(どちらも null=匿名入室を許容)。
旧エンティティとの差分(現行の entry_role / chat 由来 → participants)クリックで展開
| 旧フィールド | 種別 | 新フィールド | 備考 |
|---|---|---|---|
| —(専用テーブルなし。参加者 × ルームを初めてテーブル化) | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記(1 つの識別子ではない)。id=内部 BIGINT 主キー(非公開・コア内 FK 用)/ tenant_id=テナントスコープ(PoC は 0 固定)/ uuid=公開識別子(グローバル一意・API では id として露出) |
| — | 🆕 新設 | room_id | コア内部 FK → rooms.id(NOT NULL)。参加者を所属 Room に束縛 |
| room_entry_histories.entry_role / URL param interview_role | → 移行 | role | moderator / observer / panelist / minedia_observer。クライアント申告 param → 署名トークン由来へ(DEC-03) |
| — | 🆕 新設 | user_id | コア内部 FK → users.id(nullable)。基盤運用者(minedia_observer)の入室時のみ台帳に紐づく |
| room_chat_messages.user_id / employee_id(物理 FK・identity の出所) | → 移行 | external_type / external_id | テナント側 identity。FK ではない(identity FK を external_ref に置換)。匿名は null |
| room_entry_histories.name / room_chat_messages.sender_name | → 移行 | display_name | 表示名スナップショット |
| —(As-Is は強制退出を OpenTok 直叩き・記録なし) | 🆕 新設 | status | active / kicked / banned。再入室阻止の単一根拠(§7-1 P5・G1) |
| — | 🆕 新設 | kicked_at | 強制退出 / BAN の発生時刻(nullable・監査用) |
| — | 🆕 新設 | token_epoch | participant JWT の世代。強制退出時に +1 → 旧 epoch のトークンを失効させ再入室を物理阻止(G1) |
Participant オブジェクト
id。参加者は Room 単位で束縛されるmoderator / observer / panelist / minedia_observer。署名トークン由来User / Employee)。匿名入室時は nullnullactive / kicked / banned。再入室阻止の単一根拠null{
"id": "0190a1d0-…",
"room_id": "0190a1b3-…",
"role": "panelist",
"display_name": "山田太郎",
"external_type": "User",
"external_id": "999",
"status": "active",
"token_epoch": 0,
"kicked_at": null,
"created_at": "2026-07-01T04:58:01Z"
}
Participant を一覧する
参加者 × 役割の束縛の事後参照です。見学者一覧・参加実績の突合に使います(リアルタイム一覧はランタイム側)。既定ソートは created_at 昇順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| role任意 | 役割で絞り込み(moderator / observer / panelist / minedia_observer) |
| status任意 | 状態で絞り込み(active / kicked / banned) |
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
エラー
| 条件 | レスポンス |
|---|---|
| room_id が不存在 or 他テナント所有 | 404 NOT_FOUND |
| role / status の enum 不正 | 400 INVALID_REQUEST |
curl "…/api/v1/rooms/0190a1b3-…/participants?role=panelist&status=active" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{
"id": "0190a1d0-…",
"room_id": "0190a1b3-…",
"role": "panelist",
"display_name": "山田太郎",
"external_type": "User",
"external_id": "999",
"status": "active",
"token_epoch": 0,
"kicked_at": null,
"created_at": "2026-07-01T04:58:01Z"
}
],
"next_cursor": null,
"has_more": false
}
Participant を ban する
特定参加者を再入室不可にします。Room 一括失効が「全員を蹴る」のに対し、これは1 人だけを恒久的に締め出す操作です(観察者会議・進行中ルームで他者を巻き込まない)。Idempotency-Key に対応します。
副作用
| 対象 | 挙動 |
|---|---|
| 当該 Participant | token_epoch を +1 し、status を banned に更新 |
| 参加トークン | 発行済みの当該 participant トークンを全失効(再発行されても無効) |
| 接続中の参加者 | メディア切断 |
| 監査 | RoomEvent(participant.banned・payload に participant_id / 理由)として監査記録 |
ボディパラメータ
| パラメータ | 説明 |
|---|---|
| reason必須 string(1..255) | ban 理由(監査用) |
エラー
| 条件 | レスポンス |
|---|---|
| room_id / participant_id が不存在 or 他テナント所有 | 404 NOT_FOUND |
| reason 欠落 / 範囲外(1..255) | 400 INVALID_REQUEST |
成功時は 200 OK。ban 済みへの再実行は冪等で 200 を返します。
JWT は Room の epoch に加え participant 単位の pepoch クレームを持ち、connect 時に runtime が「Room.token_epoch == jwt.epoch かつ Participant.token_epoch == jwt.pepoch かつ status != banned」を検証します。これにより、ban 後に §4.3.1 で再発行されたトークン(新 jti)でも入室を拒否できます(jti ブラックリスト単独では再発行で素通りしてしまう問題の解消)。
curl -X POST …/api/v1/rooms/0190a1b3-…/participants/0190a1d0-…/ban \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…" \ -H "Content-Type: application/json" \ -d '{ "reason": "規約違反のため強制退出" }'
{
"participant_id": "0190a1d0-…",
"status": "banned",
"token_epoch": 1
}
EntryHistory
ルームへの入退室+デバイス計測の純ログです(書き込みはランタイム側のみ)。出欠判定(is_attend)の材料となりますが、判定ロジックは調査ドメイン側の責務であり、基盤は事実のみを返します。
EntryHistory は高ボリュームのログ系リソースのため、id はUUID ではなく BIGINT の整数です(コア共通の UUID 規約に対する明示的な例外。カーソルの内部キーにも使用)。フィールド名は他リソースと同じく id ですが、値の形式のみ整数である点に注意してください。
旧エンティティとの差分(room_entry_histories → entry_histories)クリックで展開
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(entry_histories) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id | ログ系は uuid を持たない純ログ。id=BIGINT 主キー(API でも整数 id として露出・C4 例外)/ tenant_id=テナントスコープ(PoC は 0 固定) |
| room_id | 🆕 新設 | room_id | 旧 integer FK → 新 bigint FK(rooms.id) |
| — | 🆕 新設 | participant_id | FK(participants.id)。匿名入室を許容するため nullable |
| name | → 移行 | name | 表示名スナップショット |
| entry_role | → 移行 | role | integer enum → 同語彙の文字列化(moderator / observer / panelist / minedia_observer) |
| browser_name / browser_version / platform_name / platform_version / ip_address | → 移行 | 同左 | デバイス計測。現状踏襲 |
— (As-Is は退室時刻列なし=created_at のみ) | 🆕 新設 | exited_at | 退室時刻(disconnect webhook で更新・nullable)。入退室をペアで記録。API では left_at として露出 |
| — | 🆕 新設 | duration_sec | 滞在秒数(exited_at − created_at)。課金計測・出欠(is_attend)・障害(録画欠落)判定の根拠 |
| created_at / updated_at | → 移行 | 同左 | entered_at = created_at(現 entry_time メソッド踏襲) |
EntryHistory オブジェクト
rooms への参照)null ありmoderator / observer / panelist / minedia_observerChrome)137)iOS)19.1)created_at と同値)null)。entity 側 exited_atleft_at − entered_at)。課金計測・出欠・障害判定の根拠{
"id": 1001,
"room_id": "0190a1b3-…",
"participant_id": "0190a1d0-…",
"name": "山田太郎",
"role": "panelist",
"browser_name": "Chrome",
"browser_version": "137",
"platform_name": "iOS",
"platform_version": "19.1",
"ip_address": "203.0.113.10",
"entered_at": "2026-07-01T04:58:01Z",
"left_at": "2026-07-01T06:01:12Z",
"duration_sec": 3791
}
EntryHistory を一覧する
指定 Room の入退室履歴を一覧します。出欠判定(is_attend)の材料として調査ドメイン側が pull します。既定ソートは entered_at 昇順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| role任意 | 役割で絞り込み(moderator / observer / panelist / minedia_observer) |
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
curl "…/api/v1/rooms/0190a1b3-…/entry-histories?role=panelist" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{
"id": 1001,
"room_id": "0190a1b3-…",
"participant_id": "0190a1d0-…",
"name": "山田太郎",
"role": "panelist",
"browser_name": "Chrome",
"browser_version": "137",
"platform_name": "iOS",
"platform_version": "19.1",
"ip_address": "203.0.113.10",
"entered_at": "2026-07-01T04:58:01Z",
"left_at": "2026-07-01T06:01:12Z"
}
],
"next_cursor": null,
"has_more": false
}
W1 / W2 participant.entered / participant.left
ルーム内の入退室の事実を push 通知します。Participant 単体の入退室 Webhook はなく、入退室の事実は EntryHistory の W1 / W2 が担い、data に Participant を内包します。
| type | 用途 |
|---|---|
participant.entered W1 | 入室。出欠判定の材料(現行 OpenTok callback の touch_interview_attendance 後継) |
participant.left W2 | 退室。W1 の data に left_at と duration_sec を追加。インタビューコメント(感想投稿)導線・オペ評価工程の起点。謝礼確定のトリガーではない(謝礼は調査側のオペ評価 is_reward で確定) |
退室理由 leave_reason の取り扱いは後日意思決定です。現行は connectionDestroyed を noop で捨てており退室理由を一切保持していません。採用時は W2・EntryHistory に leave_reason(voluntary / kicked / disconnected / session_ended)を追加し、ban は kicked として表現します。
{
"id": "evt_0190a210-…",
"version": "1",
"type": "participant.left",
"occurred_at": "2026-07-01T06:01:12Z",
"data": {
"room_id": "0190a1b3-…",
"session_id": "0190a1b2-…",
"session_external_ref": { "external_type": "Slot", "external_id": "123" },
"participant": {
"id": "0190a1d0-…",
"role": "panelist",
"display_name": "山田太郎",
"external_type": "User",
"external_id": "999"
},
"entry_history_id": 1001,
"entered_at": "2026-07-01T04:58:01Z",
"left_at": "2026-07-01T06:01:12Z",
"duration_sec": 3791
}
}
Chat
チャットの送受信はランタイム側の責務です。公開 API は振り返りフェーズの事後参照のみを提供します(チャット履歴の持ち手=基盤)。チャンネル単位の権限(visibility)は ChatMessage に内包され、クエリ条件として露出します。
ChatChannel は単体エンドポイントを持たず、ChatMessage に visibility(チャンネルの権限軸)として内包・露出します。ChatPost(添付ファイル)の事後参照は v2 候補です(現行の事後閲覧ユースケースの有無を確認してから追補)。
ChatMessage オブジェクト
id(UUID 形式)と visibility(public / private / observer_only)を内包id。system メッセージは nulltext / stamp / archive_statusstamp 等の非テキストでは null{
"id": 501,
"chat_channel": {
"id": "0190a1e0-…",
"visibility": "public"
},
"participant_id": "0190a1d0-…",
"sender_name": "山田太郎",
"content_type": "text",
"text": "よろしくお願いします",
"created_at": "2026-07-01T05:00:30Z"
}
旧エンティティとの差分(room_chat_channels / room_chat_messages / room_chat_posts → chat_*)クリックで展開
chat_channels(現 RoomChatChannel)
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(chat_channels) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の 3 列。uuid は API では id(UUID 形式)として露出 |
| usage (integer enum) | → 移行 | visibility | 文字列化。public / private / observer_only。unique [room_id, visibility](現 [room_id, usage] を踏襲) |
chat_messages(現 RoomChatMessage)
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(chat_messages) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 共通 3 列。ただしログ系のため API は整数 id を露出(uuid は内部一意制約用) |
| room_chat_channel_id | → 移行 | chat_channel_id | FK → chat_channels.id |
| user_id / employee_id (物理FK) | → 移行 | participant_id | identity 物理FK をコア内参加者 FK に置換(nullable・system メッセージ用) |
| content_type | → 移行 | content_type | text / stamp / archive_status |
| text | → 移行 | text | — |
| sender_id (global_id 相当) | → 移行 | sender_ref | テナント不透明文字列の表示用送信者 ID |
| sender_name | → 移行 | sender_name | 送信時点のスナップショット |
chat_posts(現 RoomChatPost・添付)
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(chat_posts) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 共通 3 列 |
| room_chat_channel_id | → 移行 | chat_channel_id | FK → chat_channels.id |
| employee_id (物理FK) | → 移行 | participant_id | identity FK を置換(nullable) |
| file | → 移行 | file | アップローダ。保存先はテナント設定 |
チャット履歴を取得する
振り返りフェーズの事後参照用に、ルームのチャット履歴を取得します。サーバー間参照のため全 visibility を返せます。エンドユーザー(クライアント / オペレーター)への表示可否(private / observer_only の閲覧制限)はテナント側の認可責務です。既定ソートは created_at 昇順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| visibility任意 | 絞り込み。public / private / observer_only。未指定は全チャンネル |
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
エラー
| 条件 | レスポンス |
|---|---|
| 不存在 or 他テナントの room | 404 NOT_FOUND |
| visibility が enum 外 | 400 INVALID_REQUEST |
curl "…/api/v1/rooms/0190a1b3-…/chat-messages?visibility=public&limit=20" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{
"id": 501,
"chat_channel": { "id": "0190a1e0-…", "visibility": "public" },
"participant_id": "0190a1d0-…",
"sender_name": "山田太郎",
"content_type": "text",
"text": "よろしくお願いします",
"created_at": "2026-07-01T05:00:30Z"
}
],
"next_cursor": null,
"has_more": false
}
チャット履歴を削除する(運用 purge)
ルームのチャット履歴をまとめて削除します(現行 operator 画面の destroy_messages 後継。誤投稿・個人情報の消去等の運用操作)。チャット履歴を基盤が所有するため、削除も基盤側 API で行います。Idempotency-Key に対応します。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| visibility任意 | 絞り込み。public / private / observer_only。未指定はルームの全チャンネルを対象 |
副作用
| 対象 | 挙動 |
|---|---|
| 対象メッセージ | 対象範囲の ChatMessage を削除 |
| 添付 | 紐づく ChatPost も併せて削除 |
| 監査 | RoomEvent chat.purged(payload に件数・要求元)を記録 |
成功時は 204 No Content。対象 0 件でも冪等で 204 を返します。
個別削除は PoC では非対応です(必要なら v2 で DELETE …/chat-messages/{id} を追補する余地があります)。
curl -X DELETE "…/api/v1/rooms/0190a1b3-…/chat-messages?visibility=public" \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…"
204 No Content
RoomEvent
Room 内で発生した事実を記録する汎用イベントログです。書き込み(提示物の show / hide 等)はランタイム側の能力であり、公開 API は読み取りのみを提供します(URL レポート・再生・監査用)。
RoomEvent は高ボリュームなログ系リソース(C4)のため、UUID を持たず BIGINT の整数 を id として露出します(カーソルの内部キーにも使用)。UUID 文字列を id とする通常リソースに対する明示的な例外です。
RoomEvent オブジェクト
idhandout.show)に加え、未知の値はテナント拡張として不透明文字列で保持(enum 化しない)handout.show の handout_id)。未設定時は null{
"id": 9001,
"room_id": "0190a1b3-…",
"event_type": "handout.show",
"payload": {
"handout_id": "0190a1c0-…"
},
"created_at": "2026-07-01T05:10:00Z"
}
旧エンティティとの差分(room_events → room_events)クリックで展開
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(room_events) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id | 共通規約(uuid なし・純ログ)。id はログ系のため BIGINT の整数(カーソルの内部キーにも使用)/ tenant_id=テナントスコープ(PoC は 0 固定) |
| room_id | → 移行 | room_id | FK → rooms.id |
| type | → 移行 | event_type | STI 無効化用の type をリネーム(現 inheritance_column = :_type_disabled)。SHOW_PROJECT_HANDOUT 等の調査固有語彙は不透明文字列のまま |
| payload | → 移行 | payload | text (JSON serialize) → json / text |
| created_at / updated_at | → 移行 | 同左 | — |
RoomEvent を一覧する
Room 配下のイベントを時系列で取得します。再生ロジック(SHOW / HIDE 圧縮)の帰属は未決のため、本 API は生イベントの時系列のみ返します。既定ソートは created_at 昇順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| event_type任意 | イベント種別での絞り込み(例 handout.show) |
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
エラー
| 条件 | レスポンス |
|---|---|
| Room が不存在 or 他テナント | 404 NOT_FOUND |
コア語彙の例: handout.show / handout.hide / token.force_issued / room.tokens_revoked / recording.failed / chat.purged。未知の値はテナント拡張として不透明文字列で保持します。
curl "…/api/v1/rooms/0190a1b3-…/room-events?event_type=handout.show" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{
"id": 9001,
"room_id": "0190a1b3-…",
"event_type": "handout.show",
"payload": { "handout_id": "0190a1c0-…" },
"created_at": "2026-07-01T05:10:00Z"
}
],
"next_cursor": null,
"has_more": false
}
Recording
録画の事実を表すリソースです。録画の開始 / 停止はランタイム側の能力(recording.control=moderator)または archive_mode: always の自動録画で行われ、公開 API は録画の事実の読み取り+署名 URL の払い出し+運用削除のみを提供します。
RtcSession(エンジン抽象)は単体エンドポイントを持たず、Recording に rtc_session_id として内包されます。文字起こしはコア化されており(G5)、録画完了後の自動 / 手動文字起こしは Transcription ページを参照してください。
Recording オブジェクト
rtc_session_id として現れるnull(ConnectionTest の recordings から参照され、/rooms/{room_id}/recordings には現れない)opentok / livekit / zoom。RtcSession から非正規化(クエリ容易化)started / paused / stopped / uploaded / available / expired / failed。署名 URL 払い出しは available のみcomposed(合成)/ individual(個別){
"id": "0190a1f0-…",
"rtc_session_id": "0190a1e5-…",
"room_id": "0190a1b3-…",
"engine": "livekit",
"provider_archive_id": "EG_xxxx",
"status": "available",
"output_mode": "composed",
"size": 734003200,
"duration_sec": 3612.5,
"created_at": "2026-07-01T05:00:05Z",
"updated_at": "2026-07-01T06:05:00Z"
}
旧エンティティとの差分(ot_archives → recordings)クリックで展開
| 旧フィールド | 種別 | 新フィールド | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記。id=内部 BIGINT 主キー(非公開)/ tenant_id=テナントスコープ(PoC は 0 固定)/ uuid=公開識別子(API では id として露出・調査側の録画参照キー) |
| ot_session_id | → 移行 | rtc_session_id | コア内部 FK → rtc_sessions.id。OpenTok 直結から汎用エンジン抽象へ |
| — | 🆕 新設 | engine | RtcSession から非正規化(クエリ容易化・任意) |
| archive_id(OpenTok) | → 移行 | provider_archive_id | 汎用名へ(LiveKit=Egress ID / Zoom=recording ID) |
| status | → 移行 | status | started / paused / stopped / uploaded / available / expired / failed。DEC-02 確定時に「正規化 status+provider_status」2 層化候補 |
| output_mode | → 移行 | output_mode | composed / individual |
| size | → 移行 | size | bytes |
| file_duration_sec | → 移行 | duration_sec | decimal(10,4) |
| duration_from_tokbox | ❌ 対象外 | — | ベンダ固有の生値・持ち込まない |
| #video_s3_key(メソッド+Settings.opentok) | → 移行 | storage_key | 生成ロジックはコアへ移植、bucket / apikey はテナント設定経由 |
| created_at / updated_at | → 移行 | 同左 | — |
Room の録画を一覧する
Room 配下の全 RtcSession に紐づく Recording を返します。既定ソートは created_at 昇順です。接続テスト用録画(room_id が null)はここには現れません。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
エラー
| 条件 | レスポンス |
|---|---|
| Room が不存在 or 他テナント | 404 NOT_FOUND |
curl "…/api/v1/rooms/0190a1b3-…/recordings" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{
"id": "0190a1f0-…",
"rtc_session_id": "0190a1e5-…",
"room_id": "0190a1b3-…",
"engine": "livekit",
"status": "available",
"output_mode": "composed",
"duration_sec": 3612.5
}
],
"next_cursor": null,
"has_more": false
}
Recording を取得する
id(UUID 形式)で録画の事実を 1 件取得します。録画の実体ファイルへのアクセスは署名付き URL の払い出し(次節)を使います。
エラー
| 条件 | レスポンス |
|---|---|
| 不存在 or 他テナント | 404 NOT_FOUND |
curl …/api/v1/recordings/0190a1f0-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"id": "0190a1f0-…",
"rtc_session_id": "0190a1e5-…",
"room_id": "0190a1b3-…",
"engine": "livekit",
"provider_archive_id": "EG_xxxx",
"status": "available",
"output_mode": "composed",
"size": 734003200,
"duration_sec": 3612.5,
"created_at": "2026-07-01T05:00:05Z",
"updated_at": "2026-07-01T06:05:00Z"
}
署名付き URL を払い出す
録画ファイル(S3・非公開バケット)への短期署名 URL を払い出します。文字起こし処理・録画閲覧画面の双方で使います。
基盤は「API key 認証済みテナント」にのみ署名 URL を払い出します。エンドユーザー(クラ / オペ)への閲覧可否はテナント側が判断します。録画=個人情報のため、払い出しは基盤側でアクセスログを記録します(recording_id / 払い出し時刻 / expires_at / 要求元テナント)。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| expires_in_seconds任意 integer | 署名 URL の有効秒数。60〜10800(default 3600) |
エラー
| 条件 | レスポンス |
|---|---|
status が available 以外 | 409 RECORDING_NOT_AVAILABLE |
expires_in_seconds が範囲外 | 400 INVALID_REQUEST |
| 不存在 or 他テナント | 404 NOT_FOUND |
curl "…/api/v1/recordings/0190a1f0-…/download-url?expires_in_seconds=3600" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"url": "https://s3.../signed...",
"expires_at": "2026-07-01T07:05:00Z"
}
Recording を削除する(運用・個人情報消去)
録画の実体(S3 オブジェクト)と事実レコードを削除します(現行 operator 画面の DELETE ot_archives/:id 後継)。録画=個人情報のため、保持期間超過・誤録画・削除依頼への対応として基盤側で物理削除します。Idempotency-Key ヘッダに対応します。
副作用
| 対象 | 挙動 |
|---|---|
| 録画ファイル / レコード | S3 の録画ファイルと Recording レコードを削除 |
| Transcription | 当該録画にぶら下がる Transcription も連鎖削除 |
| 監査ログ | 削除の事実を基盤側で記録(recording_id / 削除時刻 / 要求元テナント) |
エラー
| 条件 | レスポンス |
|---|---|
録画中(status が started / paused)は削除不可(停止・完了後に削除) | 409 RECORDING_NOT_AVAILABLE |
成功時は 204 No Content。削除済みへの再実行は冪等で 204 を返します。
curl -X DELETE …/api/v1/recordings/0190a1f0-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…"
204 No Content
W3 recording.status_changed
録画の状態が変化したときに送信されます。available への遷移は文字起こし・事後処理の起点として、failed への遷移は録画障害アラート・オペ対応の起点として利用できます(現行 app_main の録画監視は基盤側へ移り、通知だけ調査側に飛びます)。
| status | 用途 |
|---|---|
available | 文字起こし・事後処理の起点 |
failed | 録画障害アラート・オペ対応(failure_reason に原因) |
{
"id": "evt_0190a210-…",
"version": "1",
"type": "recording.status_changed",
"occurred_at": "2026-07-01T06:05:00Z",
"data": {
"recording_id": "0190a1f0-…",
"room_id": "0190a1b3-…",
"session_id": "0190a1b2-…",
"session_external_ref": { "external_type": "Slot", "external_id": "123" },
"status": "available",
"previous_status": "uploaded",
"failure_reason": null
}
}
Transcription
2026-06-18 の G5 決定でコア化した、録画の子リソースです。基盤は文字起こしの事実+結果を持ち、要約・集計は調査ドメインが Transcription.id を起点に行います。公開 API は読み取り+完了 Webhookを基本としつつ、加えて手動起動を持ちます。
録画完了時に基盤が自動で文字起こしを実行します。手動起動は、別言語・再実行・翻訳をオペレーション起点でオンデマンド要求するための後継機能です(read-only 原則に対する運用例外)。
旧エンティティとの差分(transcription_requests → transcriptions)クリックで展開
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(transcriptions) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記(1 つの識別子ではない)。id=内部 BIGINT 主キー(非公開・コア内 FK 用)/ tenant_id=テナントスコープ(PoC は 0 固定)/ uuid=公開識別子(API では id として露出・調査側の要約/集計の参照キー)。※DB の id と API の id は別物 |
| ot_archive_id | → 移行 | recording_id | コア内部 FK → recordings.id(OtArchive → Recording に追随) |
| transcription_service(5 ベンダ integer enum) | → 移行 | provider | eleven_labs / openai / azure / anyenv / rimo。engine と同じくベンダ中立の文字列化 |
| transcription_id(ベンダのジョブ ID) | → 移行 | provider_transcription_id | 汎用名へ。unique [provider, provider_transcription_id] |
| status(6 種 integer enum) | → 移行 | status | sending_request / processing / retrying / done / error / request_error(文字列化) |
| result_json | → 移行 | result_json | VTT/JSON の文字起こし結果(ベンダ生形式はここに閉じる)。API は result_url で払い出す(本体は基盤内部) |
| duration | → 移行 | duration_sec | decimal(10,4) |
| language | → 移行 | language | G5 副作用でコアに入る。default ja-JP |
| local(true=自前 / false=翻訳) | → 移行 | local | 自前文字起こし vs 翻訳の区別(現状踏襲・default true) |
| error_message | → 移行 | error_message | — |
| created_at / updated_at | → 移行 | 同左 | — |
Transcription オブジェクト
Recording.id)。1 録画に複数言語/翻訳がぶら下がるRoom.id)eleven_labs / openai / azure / anyenv / rimonull)sending_request / processing / retrying / done / error / request_errorja-JP)true=自前文字起こし / false=翻訳null)。本体(result_json)は基盤内部に閉じ、録画 download-url と同じ非公開バケット方針null{
"id": "0190a210-…",
"recording_id": "0190a1f0-…",
"room_id": "0190a1b3-…",
"provider": "openai",
"provider_transcription_id": "tr_xxxx",
"status": "done",
"language": "ja-JP",
"local": true,
"result_url": "https://s3.../signed-vtt…",
"duration_sec": 3612.5,
"created_at": "2026-07-01T06:10:00Z",
"updated_at": "2026-07-01T06:14:30Z"
}
録画の文字起こしを一覧する
1 録画にぶら下がる文字起こし(複数言語 / 翻訳)を一覧します。既定ソートは created_at 昇順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
エラー
| 条件 | レスポンス |
|---|---|
| 録画が不存在 or 他テナント | 404 NOT_FOUND |
curl …/api/v1/recordings/0190a1f0-…/transcriptions \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{ "id": "0190a210-…", "recording_id": "0190a1f0-…", "language": "ja-JP", "local": true, "status": "done" }
],
"next_cursor": null,
"has_more": false
}
Transcription を取得する
id(UUID 形式)で 1 件取得します。done の場合は result_url に短期署名 URL を含みます。
エラー
| 条件 | レスポンス |
|---|---|
| 不存在 or 他テナント | 404 NOT_FOUND |
curl …/api/v1/transcriptions/0190a210-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"id": "0190a210-…",
"recording_id": "0190a1f0-…",
"room_id": "0190a1b3-…",
"provider": "openai",
"status": "done",
"language": "ja-JP",
"local": true,
"result_url": "https://s3.../signed-vtt…",
"duration_sec": 3612.5
}
文字起こし/翻訳を起動する
録画に対し文字起こし / 翻訳をオンデマンド起動します(現行 operator 画面の transcribe / translate 後継)。録画完了時の自動文字起こしに加え、別言語・再実行・翻訳をオペ起点で要求する書き込み系です。Idempotency-Key に対応します。
ボディパラメータ
| パラメータ | 説明 |
|---|---|
| language任意 string(≤16) | 出力言語(BCP 47)。未指定は Session の transcription_language |
| translate任意 boolean | true=翻訳(local=false)/ false=自前文字起こし(default false) |
| provider任意 enum | エンジン上書き(eleven_labs / openai / azure / anyenv / rimo)。未指定は Session の transcription_service |
エラー
| 条件 | レスポンス |
|---|---|
録画 status が available 以外 | 409 RECORDING_NOT_AVAILABLE |
| language / provider が不正 | 400 INVALID_REQUEST |
同一 (recording, language, translate) が処理中 | 409 TRANSCRIPTION_IN_PROGRESS |
成功時は 201 Created(Location: /api/v1/transcriptions/{transcription_id}・status: sending_request)を返します。完了は W6 transcription.completed で push 通知します。
curl -X POST …/api/v1/recordings/0190a1f0-…/transcriptions \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…" \ -H "Content-Type: application/json" \ -d '{ "language": "en-US", "translate": true, "provider": "openai" }'
{
"id": "0190a211-…",
"recording_id": "0190a1f0-…",
"room_id": "0190a1b3-…",
"provider": "openai",
"status": "sending_request",
"language": "en-US",
"local": false,
"result_url": null,
"duration_sec": null,
"created_at": "2026-07-01T06:20:00Z",
"updated_at": "2026-07-01T06:20:00Z"
}
W6 transcription.completed
文字起こしは録画完了(W3 available)より遅れて非同期に付くため、push 通知が必要です。minedia-www はこれを受けて Transcription を取得し、要約・集計を調査側で生成します。status: error / request_error は文字起こし失敗としてオペ / 調査側に通知します。
{
"id": "evt_0190a212-…",
"version": "1",
"type": "transcription.completed",
"occurred_at": "2026-07-01T06:14:30Z",
"data": {
"transcription_id": "0190a210-…",
"recording_id": "0190a1f0-…",
"room_id": "0190a1b3-…",
"session_id": "0190a1b2-…",
"session_external_ref": { "external_type": "Slot", "external_id": "123" },
"status": "done",
"language": "ja-JP",
"failure_reason": null
}
}
Handout
画質保持のため、メディアストリームではなく事前アップロード+ブラウザ直表示で提示する資料(image / video)です。割付ロジック(target_slot_type / project_handouts_slots)は調査ドメイン側に残置し、調査側が割付解決済みのファイルを Session に登録します。
「どの枠にどの提示物を出すか」の割付は Project の語彙に依存するため調査ドメイン側に残置します。基盤は割付解決済みのリストだけを受け取り、提示の実体(ファイル・順序・種別)のみを保持します。提示操作(show / hide)はルーム内ランタイムの能力(handout.present=moderator)であり、本 API の対象外です。
旧エンティティとの差分(project_handouts / extemporary_project_handouts → handouts)クリックで展開
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(handouts) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記(1 つの識別子ではない)。id=内部 BIGINT 主キー(非公開・コア内 FK 用)/ tenant_id=テナントスコープ(PoC は 0 固定)/ uuid=公開識別子(RoomEvent payload・API での参照キー・API では id として露出) |
| project_handouts.id / extemporary_project_handouts.id(出所 PK) | → 移行 | external_type / external_id | 出所 PK を external_ref 化。例 {"ProjectHandout","12"}。FK ではない。調査側の再登録・同期時の重複排除キー |
| project_id / extemporary_slot_id + slot 割付の解決結果 | → 移行 | session_id | コア内部 FK → sessions.id。調査側が割付(Slot#all_project_handouts=all + slot 指定)を解決してから登録する |
| file(ProjectHandoutUploader) | → 移行 | file | 非公開バケット+短期署名 URL を踏襲(file_url として露出) |
| content_type | → 移行 | content_type | integer enum(1=image / 2=video)→ 文字列化(image / video) |
| sort | → 移行 | sort | 提示順(default 0) |
| project_handouts.target_slot_type(all/selected)/ project_handouts_slots(join) | ❌ 対象外 | — (調査に残置) | 割付は Project の語彙。コアは割付解決済みのリストだけを受け取る |
| created_at / updated_at | → 移行 | 同左 | — |
Handout オブジェクト
idProjectHandout / ExtemporaryProjectHandout(未指定時は null)external_type とペアで使用<img> / <video> でブラウザ直表示する(WebRTC ストリーム非経由)image / video0)id は RoomEvent payload(handout.show / handout.hide の handout_id)での参照キーです。file_url の有効期限はインタビュー時間(ends_at − 取得時刻)より長いことが制約です。
{
"id": "0190a1c0-…",
"session_id": "0190a1b2-…",
"external_type": "ProjectHandout",
"external_id": "12",
"file_name": "concept_A.png",
"file_url": "https://s3.../signed…",
"content_type": "image",
"sort": 0,
"created_at": "2026-06-10T09:00:00Z"
}
Handout を登録する
調査側が割付(target_slot_type / project_handouts_slots)を解決した後、提示する実体ファイルを Session に登録します。multipart/form-data でファイル本体を送信し、Idempotency-Key に対応します。
ボディパラメータ(multipart/form-data)
| パラメータ | 説明 |
|---|---|
| file必須 binary | 提示物ファイル本体 |
| content_type必須 enum | image / video |
| sort任意 integer ≥0 | 提示順(default 0) |
| external_type任意 string(≤64) | tenant 0: ProjectHandout / ExtemporaryProjectHandout |
| external_id任意 string(≤64) | テナント側 PK(整数も文字列化)。同一 external_ref の再登録は置換(上書き)=調査側の再同期を冪等にする |
エラー
| 条件 | レスポンス |
|---|---|
file / content_type 必須・external_ref ペア指定・sort は 0 以上の整数 | 400 INVALID_REQUEST |
| 拡張子ホワイトリスト外(image=jpg/jpeg/gif/png/bmp/tiff、video=mp4/mov。PDF・Office 不可) | 422 UNSUPPORTED_FILE_TYPE |
| サイズ上限超過(image 20MB / video 300MB・仮値) | 413 FILE_TOO_LARGE |
| 解放済み(released)Session への登録 | 409 SESSION_RELEASED |
成功時は 201 Created と Location: /api/v1/handouts/{handout_id} を返します。
curl -X POST …/api/v1/sessions/0190a1b2-…/handouts \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…" \ -F "file=@concept_A.png" \ -F "content_type=image" \ -F "sort=0" \ -F "external_type=ProjectHandout" \ -F "external_id=12"
{
"id": "0190a1c0-…",
"session_id": "0190a1b2-…",
"external_type": "ProjectHandout",
"external_id": "12",
"file_name": "concept_A.png",
"file_url": "https://s3.../signed…",
"content_type": "image",
"sort": 0,
"created_at": "2026-06-10T09:00:00Z"
}
Handout を一覧する
Session に登録済みの提示物を一覧します。既定ソートは sort 昇順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20) |
curl …/api/v1/sessions/0190a1b2-…/handouts \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{ "id": "0190a1c0-…", "session_id": "0190a1b2-…", "content_type": "image", "sort": 0 }
],
"next_cursor": null,
"has_more": false
}
Handout を更新する
メタデータ(sort 等)の部分更新と、任意のファイル差し替えを行います(multipart/form-data・全フィールド任意・指定したもののみ更新)。sort のみの並べ替えはファイル再送なしで行える点が、POST upsert(同 external_ref 全置換)との使い分けです。
ボディパラメータ(multipart/form-data)
| パラメータ | 説明 |
|---|---|
| sort任意 integer ≥0 | 提示順 |
| content_type任意 enum | image / video |
| file任意 binary | 差し替えファイル本体(指定時のみ置換。拡張子 / サイズは登録時と同一検証) |
エラー
| 条件 | レスポンス |
|---|---|
file 差し替え時、拡張子ホワイトリスト外 | 422 UNSUPPORTED_FILE_TYPE |
file 差し替え時、サイズ上限超過 | 413 FILE_TOO_LARGE |
sort が 0 以上の整数でない | 400 INVALID_REQUEST |
ルーム内で提示中(直近 handout.show 後に hide なし)の file 差し替え(sort 変更は可) | 409 HANDOUT_IN_USE |
| 解放済み(released)Session の Handout | 409 SESSION_RELEASED |
成功時は 200 とリソース表現を返します。
curl -X PATCH …/api/v1/handouts/0190a1c0-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -F "sort=2"
{
"id": "0190a1c0-…",
"session_id": "0190a1b2-…",
"file_name": "concept_A.png",
"content_type": "image",
"sort": 2,
"created_at": "2026-06-10T09:00:00Z"
}
Handout を削除する
登録済みの提示物を削除します。
エラー
| 条件 | レスポンス |
|---|---|
ルーム内で提示中(直近 handout.show 後に hide なし)は削除不可 | 409 HANDOUT_IN_USE |
成功時は 204 No Content。
curl -X DELETE …/api/v1/handouts/0190a1c0-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
204 No Content
ConnectionTest
現 BrowserTestHistory を汎用化した「被験者ブラウザ / 回線の品質計測」リソースです。接続テストは基盤で実施し(ダミー配信+購読負荷・計測 15 秒以上)、専用の RtcSession を 1 本持ちます。合格判定(User.webrtc_test_status 相当)はテナント側の責務であり、基盤は計測の事実だけを返します。
診断ログ ConnectionTestEvent(entity §3.14)は基盤内部のサポート / ファネル分析用で、公開 API には露出しません。API が返すのは ConnectionTest の計測結果(summary / baseline / load)と録画参照までです。
ConnectionTest オブジェクト
created / completed / failedfalse = 計測時間が 15 秒未満(MEASUREMENT_MIN_SECONDS)等で測定不成立 →summary / baseline / load は null。テナントは「測定できませんでした / 動画を直接確認」を表示User)。匿名テストは nullnullopentok / livekit / zoomnull)null)measurement_completed=false 時は nullnulllegacy_record?)は null{ id, status } の配列。再生は §録画 download-url。なければ空配列livekit=LiveKit Cloud セッション画面)。提供されないエンジンは nullnull)summary / baseline / load はエンジン中立語彙(bitrate / packet loss / fps / jitter)で表現します。エンジン固有の生統計は基盤内部の raw_data に閉じ、API には出しません(OpenTok→LiveKit 交代でも列はそのまま使えます)。
{
"id": "0190a200-…",
"status": "completed",
"measurement_completed": true,
"external_type": "User",
"external_id": "999",
"rtc_engine": "livekit",
"dummy_stream_count": 5,
"hardware_concurrency": 8,
"connected_subscriber_count": 5,
"summary": {
"video_average_bitrate": 441000.0,
"video_median_bitrate": 477000.0,
"video_average_packets_loss_ratio": 0.0,
"video_median_packets_loss_ratio": 0.0,
"audio_average_bitrate": 31000.0,
"audio_median_bitrate": 32000.0,
"audio_average_packets_loss_ratio": 0.0,
"audio_median_packets_loss_ratio": 0.0
},
"baseline": {
"video_average_bitrate": 1850000.0,
"video_average_packets_loss_ratio": 0.001,
"frames_dropped_ratio": 0.0,
"fps": 29.8,
"audio_bitrate": 32000.0,
"audio_packets_loss_ratio": 0.0,
"audio_jitter": 0.004
},
"load": {
"video_average_bitrate": 1520000.0,
"video_average_packets_loss_ratio": 0.01,
"worst_video_bitrate": 980000.0,
"worst_video_packets_loss_ratio": 0.03,
"overall_frames_dropped_ratio": 0.02,
"worst_frames_dropped_ratio": 0.06,
"overall_fps": 27.0,
"worst_fps": 18.0,
"audio_average_bitrate": 31000.0,
"audio_average_packets_loss_ratio": 0.002,
"worst_audio_packets_loss_ratio": 0.01,
"audio_average_jitter": 0.006,
"worst_audio_jitter": 0.02
},
"recordings": [
{ "id": "0190a1f0-…", "status": "available" }
],
"provider_inspector_url": "https://cloud.livekit.io/projects/p_1uvc9dsiv14/sessions/RM_UNAo9Dh8Pq9t",
"completed_at": "2026-06-10T09:05:00Z",
"created_at": "2026-06-10T09:00:00Z"
}
旧エンティティとの差分(browser_test_histories → connection_tests)クリックで展開
| 旧フィールド(現行 minedia-www) | 種別 | 新フィールド(connection_tests) | 備考 |
|---|---|---|---|
| — | 🆕 新設 | id / tenant_id / uuid | 全コアテーブル共通の3 列をまとめた表記。id=内部 BIGINT 主キー(非公開)/ tenant_id=テナントスコープ(PoC は 0 固定)/ uuid=公開識別子(API では id として露出) |
has_one :ot_session | → 移行 | rtc_session_id | 所有方向を逆転しコア内部 FK 化 → rtc_sessions.id(テスト専用セッション・unique) |
| user_id(物理 FK) | → 移行 | external_type / external_id | 被験者 identity(tenant 委譲。例 {"User","999"})。FK ではない |
| — | 🆕 新設 | status | ライフサイクル created / completed / failed に明示化 |
| dummy_stream_count / hardware_concurrency / connected_subscriber_count | → 移行 | 同左 | 負荷シミュレーション構成(現状踏襲) |
| baseline_* 計測群(video bitrate / loss / fps / audio jitter 等) | → 移行 | 同左(API baseline) | ベースライン計測(現状踏襲) |
| load_* 計測群(average / worst の bitrate / loss / fps / jitter) | → 移行 | 同左(API load) | 負荷時計測(現状踏襲) |
| bits_data(JSON serialize) | → 移行 | raw_data | 汎用名へ(json)。生サンプル系列・エンジン固有の生統計はここに閉じる |
| 総合集計 8 列(video / audio_average / median_bitrate / *_packets_loss_ratio) | → 移行 | 同左(API summary) | 一覧描画に必須のためコアへ移植(旧「持ち込まない」判断を撤回)。管理画面 Web RTC 一覧の平均値 / 中央値 / パケットロス |
接続テストを開始する
被験者用の接続テストを作成し、基盤のテスト画面 URL(短命トークン付き)を払い出します。テナントは被験者ブラウザをこの URL に誘導してください。Idempotency-Key に対応します。
ボディパラメータ
| パラメータ | 説明 |
|---|---|
| external_type任意 string(≤64) | 被験者のテナント側参照(tenant 0: User)。external_id とペア指定。両方 null で匿名テスト |
| external_id任意 string(≤64) | 被験者のテナント側 PK。external_type とペア指定 |
| rtc_engine任意 enum | テスト対象エンジン(default はテナント設定) |
エラー
| 条件 | レスポンス |
|---|---|
| external_ref を片方のみ指定 | 400 INVALID_REQUEST |
成功時は 201 Created と Location: /api/v1/connection-tests/{connection_test_id} を返します。
テスト URL の有効期限は 30 分固定です。
curl -X POST …/api/v1/connection-tests \ -H "Authorization: ApiKey key_live_a1b2.sk_…" \ -H "Idempotency-Key: 7c9e6679-…" \ -H "Content-Type: application/json" \ -d '{ "external_type": "User", "external_id": "999", "rtc_engine": "livekit" }'
{
"id": "0190a200-…",
"test_url": "https://interview.minedia.com/connection-tests/0190a200-…?t=…",
"expires_at": "2026-06-10T09:30:00Z"
}
被験者ごとの履歴を取得する
minedia-www の operators/users/{id}「Web RTC」一覧のデータ源泉です。各項目が summary(平均値 / 中央値 / パケットロス)・recordings(動画)・provider_inspector_url(Inspector)・measurement_completed を内包するため、1 リクエストで一覧表を描画でき、行ごとの詳細呼び出し(N+1)は不要です。既定ソートは created_at 降順です。
クエリパラメータ
| パラメータ | 説明 |
|---|---|
| external_type必須 | 被験者単位の照会キー(external_id とペア指定) |
| external_id必須 | 被験者単位の照会キー(external_type とペア指定) |
| cursor任意 | ページングカーソル |
| limit任意 | 1〜100(default 20)。最新 N 件は limit で取得 |
external_ref を省略して作成した匿名テストは本一覧(被験者単位照会)には現れません。匿名テストは id 直接取得(次節)でのみ参照できます。
curl "…/api/v1/connection-tests?external_type=User&external_id=999" \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"items": [
{
"id": "0190a200-…",
"status": "completed",
"measurement_completed": true,
"external_type": "User",
"external_id": "999",
"rtc_engine": "livekit",
"summary": {
"video_average_bitrate": 441000.0,
"video_median_bitrate": 477000.0,
"video_average_packets_loss_ratio": 0.0,
"audio_average_bitrate": 31000.0,
"audio_median_bitrate": 32000.0,
"audio_average_packets_loss_ratio": 0.0
},
"recordings": [ { "id": "0190a1f0-…", "status": "available" } ],
"provider_inspector_url": "https://cloud.livekit.io/projects/p_1uvc9dsiv14/sessions/RM_UNAo9Dh8Pq9t",
"created_at": "2026-06-10T09:00:00Z"
}
],
"next_cursor": null,
"has_more": false
}
結果を取得する
id(UUID 形式)で 1 件取得します。匿名テストもこの経路でのみ参照できます。計測結果(summary / baseline / load)を内包したリソース表現を返します。
エラー
| 条件 | レスポンス |
|---|---|
| 不存在 or 他テナント | 404 NOT_FOUND |
curl …/api/v1/connection-tests/0190a200-… \ -H "Authorization: ApiKey key_live_a1b2.sk_…"
{
"id": "0190a200-…",
"status": "completed",
"measurement_completed": true,
"external_type": "User",
"external_id": "999",
"rtc_engine": "livekit",
"dummy_stream_count": 5,
"connected_subscriber_count": 5,
"summary": { "video_average_bitrate": 441000.0, "video_median_bitrate": 477000.0 },
"baseline": { "video_average_bitrate": 1850000.0, "fps": 29.8 },
"load": { "video_average_bitrate": 1520000.0, "worst_fps": 18.0 },
"recordings": [ { "id": "0190a1f0-…", "status": "available" } ],
"provider_inspector_url": "https://cloud.livekit.io/projects/p_1uvc9dsiv14/sessions/RM_UNAo9Dh8Pq9t",
"completed_at": "2026-06-10T09:05:00Z",
"created_at": "2026-06-10T09:00:00Z"
}
W5 connection_test.completed
接続テストの計測完了時に送信されます。minedia-www はこれを受けて GET /connection-tests/{connection_test_id} で計測値を取得し、合格判定(User.webrtc_test_status)を実施します。
| data フィールド | 意味 |
|---|---|
connection_test_id | 完了した接続テストの公開 ID(結果取得に使用) |
external_ref | 被験者参照 { external_type, external_id }(匿名テストは双方 null) |
status | completed / failed |
{
"id": "evt_0190a210-…",
"version": "1",
"type": "connection_test.completed",
"occurred_at": "2026-06-10T09:05:00Z",
"data": {
"connection_test_id": "0190a200-…",
"external_ref": { "external_type": "User", "external_id": "999" },
"status": "completed"
}
}
署名鍵 (JWKS)
参加トークン(EdDSA 署名)の検証用公開鍵セットです。kid で鍵をローテーションします。
このエンドポイントは共通エンベロープ(success / data)に包まれず、RFC 7517 標準形をそのまま返します。認証不要です。
公開鍵を取得する
認証不要。Model A では主に基盤内部の検証用ですが、テナント側での事前検証にも利用可能です。
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"kid": "interview-2026-06",
"x": "...",
"use": "sig"
}
]
}
ヘルスチェック
基盤 API down 時のフォールバック判断を minedia-www が行うための死活確認です。
health 系は共通エンベロープに包まれず、素の liveness / readiness を返します。認証不要です。
liveness
認証不要。浅い liveness(プロセス生存のみ)を確認します。正常時 200、異常時 503 を返します。
{
"status": "ok"
}
readiness
DB / Redis / ストレージなど依存先まで含む準備状態を確認します。負荷分散・デプロイ制御用です。正常時 200、依存先異常時 503 を返します。
二重運用期に minedia-www は作成前に /health で基盤の可用性を見て、down ならフォールバック判断を行います(案件途中のエンジン切替は不可=作成時に確定)。
{
"status": "ready",
"checks": {
"db": "ok",
"redis": "ok"
}
}