# イベントウェブフック

## 概要

Sora は、予め設定しておいた URL に、シグナリングの接続や切断、
録画の終了といった様々なイベントを HTTP リクエストとして送信するウェブフックの機能を持っています。

この機能を使うことで、Sora のイベントをアプリケーション側と簡単に連携することが可能です。

## 注意

`sora.conf` の [event_webhook_url](SORA_CONF.html#e1a4d2) を有効にしない場合でも `log/event_webhook.log` または `log/event_webhook.jsonl` は生成されます。

## 設定

### ウェブフックリクエスト送信先のサーバーがベーシック認証を利用している場合

もしウェブフックリクエスト送信先のサーバーがベーシック認証を利用している場合は `sora.conf` にて [webhook_basic_authn](SORA_CONF.html#081a9f) に `true` を設定することでベーシック認証を利用できます。

[webhook_basic_authn_user_id](SORA_CONF.html#a107fd) と [webhook_basic_authn_password](SORA_CONF.html#f70fe9) に使用するユーザー ID とパスワードを設定して下さい。


### ウェブフックリクエスト送信先のサーバーが自己署名証明書などを利用している場合

**この設定はおすすめしません**

デフォルトでは自己署名証明書などの正規の認証局から発行されていない証明書を利用した場合には、信頼できないと判断しエラーになります。

もし自己署名証明書を利用したサーバーがウェブフックリクエスト送信先になる場合は、
`sora.conf` にて [webhook_insecure](SORA_CONF.html#676769) に `true` を設定することで証明書のチェックを行わないようになります。

### event_webhook_url

**デフォルト**: 未設定

イベントウェブフックリクエスト送信先の URL です。イベントウェブフック機能を利用する場合は指定してください。

この URL には HTTPS の URL を指定することも可能です。

HTTPS の URL を指定する場合、リクエスト送信先のアプリケーションについて基本的には正規の認証局から発行された証明書を利用してください。

独自の証明書を利用する場合は [ウェブフックリクエスト送信先のサーバーが自己署名証明書などを利用している場合](EVENT_WEBHOOK.html#467c4b) をご確認ください。

HTTP のレスポンスは 200 OK 等の 200 番台のステータスコードの必要があります。

## HTTP ヘッダー

> **注釈**
>
> JSON のパース時の判断などに利用してください。

イベントウェブフックの HTTP ヘッダー に `x-sora-event-webhook-type` というヘッダー名でイベントウェブフックのタイプが入ってきます。

`type` が `connection.created` の場合は `x-sora-event-webhook-type: connection.created` のように値が入ってきます。

## 接続イベント

- `connection.created` と `connection.destroyed` はセットです。
- `connection.created` がリクエスト送信されずに `connection.destroyed` がリクエスト送信されることはありません。
- `connection.created` がリクエスト送信された場合、 `connection.destroyed` が **必ず** リクエスト送信されます。
- `connection.created` の後に `connection.destroyed` がリクエスト送信されます。順番は保証されます。

### 共通項目

- id- イベントごとの ID です
- version- Sora のバージョンが `文字列` で入ってきます
- label- sora.conf の label で指定した値が入ってきます
- node_name- Sora のノード名が入ってきます
- minutes- 接続経過時間 (分) が入ってきます
- created_time- connection.created 時の時間が UNIX 時間形式で入ってきます
- created_timestamp- connection.created 時の時間が RFC 3339 UTC 形式 (マイクロ秒) で入ってきます
- total_received_bytes- Sora がクライアントから受信したパケットの合計数です
- total_sent_bytes- Sora がクライアントへ送信したパケットの合計数です
- turn_transport_type- udp か tcp が入ってきます
  - TURN-UDP または TURN-TCP (TURN-TLS 含む) のどちらを使用したかがわかります
- audio- false の場合は audio を使用しません
  - コーデックやビットレートが入ってくる場合は {"codec_type": "OPUS", "bit_rate": 32} が入ってきます
  - コーデックの種類は OPUS です
  - ビットレートはクライアントが送ってこない場合は含まれません- もし default_audio_bit_rate に値を指定していた場合はその値が使用されます
    - 最小が 6 で、最大が 510 です
  - `sora.conf` の [legacy_webhook_audio_video_json_structure](SORA_CONF.html#3be597) を `false` にした場合以下のようにフラット化します- `audio: true`
    - `audio_codec_type: "OPUS"`
    - `audio_bit_rate: 32`
    - 詳細は [ウェブフックの audio と video 項目の JSON 構造のフラット化](WEBHOOK_AUDIO_VIDEO_FLATTEN_JSON.html) をご確認ください
- video- false の場合は video を使用しません
  - コーデックやビットレートが入ってくる場合は {"codec_type": "VP9", "bit_rate": 100} が入ってきます- コーデックの種類は VP8、VP9、AV1、H264、H265 の 5 種類です
    - ビットレートはクライアントが送ってこない場合は `sora.conf` の [default_video_bit_rate](SORA_CONF.html#620132) が使用されます- デフォルトは 500 です
      - 最小が 1 で、最大が 30000 です
      - 15000 より大きい値は現時点でサポート外です
  - `sora.conf` の [legacy_webhook_audio_video_json_structure](SORA_CONF.html#3be597) を `false` にした場合以下のようにフラット化します- `video: true`
    - `video_codec_type: "VP8"`
    - `video_bit_rate: 500`
    - 詳細は [ウェブフックの audio と video 項目の JSON 構造のフラット化](WEBHOOK_AUDIO_VIDEO_FLATTEN_JSON.html) をご確認ください
- multistream- true の場合はマルチストリームが有効な接続です
- simulcast- true の場合はサイマルキャストが有効な接続です
- spotlight- true の場合はスポットライトが有効な接続です
- role- `sendrecv` (送受信) / `sendonly` (送信のみ) / `recvonly` (受信のみ)
- event_metadata- この項目はオプションです
  - サーバーが定義を自由にできる値です
  - 認証サーバーから event_metadata を返した値が含まれます
  - 詳細は [event_metadata の払い出し](AUTH_WEBHOOK_RETURN.html#8b1b1f) をご確認ください
- channel_id- コネクションが接続しているチャネル ID です
- session_id- コネクションが接続しているチャネル の現在のセッションの ID です
  - UUIDv4 を Base32 でエンコードした 26 バイトの文字列です
- client_id- コネクションに割り当てられたクライアント ID です
- bundle_id- コネクションに割り当てられたバンドル ID です
- connection_id- コネクションに割り当てられたユニークな ID です
  - UUIDv4 を Base32 でエンコードした 26 バイトの文字列です


### connection.created

**接続**

シグナリングの接続が成功して、WebRTC としての通信が開始可能になった時の状態を送信します。

- data.channel_connections- 現在そのチャネルに接続しているクライアントの接続数です
  - 自分は含まれます
- data.channel_sendrecv_connections- 現在そのチャネルで送受信している配信者の接続数です
  - 自分は含まれます
- data.channel_sendonly_connections- 現在そのチャネルを送信のみしている配信者の接続数です
  - 自分は含まれます
- data.channel_recvonly_connections- 現在そのチャネルを受信のみしている視聴者の接続数です
  - 自分は含まれます

```json
{
  "type": "connection.created",
  "channel_id": "<String>",
  "session_id": "<Base32-UUIDv4>",
  "client_id": "<String | Base32-UUIDv4>",
  "bundle_id": "<String | Base32-UUIDv4>",
  "connection_id": "<Base32-UUIDv4>",
  "timestamp": "<UTC RFC3339 Timestamp>",
  "event_metadata": "<JSON Object>",
  "data": {
    "created_time": "<UNIX-Time>",
    "created_timestamp": "<UTC RFC3339 Timestamp>",
    "audio": {
      "codec_type": "<String>"
    },
    "channel_connections": "<Integer>",
    "channel_recvonly_connections": "<Integer>",
    "channel_sendonly_connections": "<Integer>",
    "channel_sendrecv_connections": "<Integer>",
    "minutes": "<Integer>",
    "total_received_bytes": "<Integer>",
    "total_sent_bytes": "<Integer>",
    "turn_transport_type": "<String>",
    "video": {
      "bit_rate": "<Integer>",
      "codec_type": "<String>"
    }
  },
  "id": "<Base32-UUIDv4>",
  "label": "<String>",
  "multistream": "<Boolean>",
  "node_name": "<String>",
  "role": "<sendrecv | sendonly | recvonly>",
  "simulcast": "<Boolean>",
  "spotlight": "<Boolean>",
  "version": "<String>"
}
```


### connection.destroyed

**切断**

シグナリングの接続が切断した時に送信します。

- data.type_disconnect_reason- `"type": "disconnect"` 送信時に `"reason"` に指定した **文字列** が入ります
  - `"reason"` を指定していなかった場合、この項目は含まれません
- data.disconnect_api_reason- DisconnectChannel API または Disconnect API を利用して切断した際、オプションの `"reason"` に指定した **JSON オブジェクト** がこの `"reason"` に含まれます
  - `"reason"` を指定していなかった場合、この項目は含まれません
- data.reason- 基本的には `data.disconnect_api_reason` と同様です
  - ただし `"reason"` を指定していなかった場合に、項目が省略されるのではなく、値に null が入る点が異なっています
- data.channel_connections- 現在そのチャネルに接続しているクライアントの接続数です
  - 自分は含まれません
- data.channel_sendrecv_connections- 現在そのチャネルで送受信している配信者の接続数です
  - 自分は含まれません
- data.channel_sendonly_connections- 現在そのチャネルを送信のみしている配信者の接続数です
  - 自分は含まれません
- data.channel_recvonly_connections- 現在そのチャネルを受信のみしている視聴者の接続数です
  - 自分は含まれません
- data.destroyed_time- connection.destroyed 時の時間が UNIX 時間形式で入ってきます
- data.destroyed_timestamp- connection.destroyed 時の時間が RFC 3339 UTC 形式 (マイクロ秒) で入ってきます

```json
{
  "type": "connection.destroyed",
  "channel_id": "<String>",
  "session_id": "<Base32-UUIDv4>",
  "client_id": "<String | Base32-UUIDv4>",
  "bundle_id": "<String | Base32-UUIDv4>",
  "connection_id": "<Base32-UUIDv4>",
  "timestamp": "<UTC RFC3339 Timestamp>",
  "event_metadata": "<JSON Object>",
  "data": {
    "created_time": "<UNIX-Time>",
    "created_timestamp": "<UTC RFC3339 Timestamp>",
    "destroyed_time": "<UNIX-Time>",
    "destroyed_timestamp": "<UTC RFC3339 Timestamp>",
    "audio": {
      "codec_type": "<String>"
    },
    "channel_connections": "<Integer>",
    "channel_recvonly_connections": "<Integer>",
    "channel_sendonly_connections": "<Integer>",
    "channel_sendrecv_connections": "<Integer>",
    "minutes": "<Integer>",
    "reason": "<String>",
    "type_disconnect_reason": "<String>",
    "total_received_bytes": "<Integer>",
    "total_sent_bytes": "<Intger>",
    "turn_transport_type": "<String>",
    "video": {
      "bit_rate": "<Intger>",
      "codec_type": "VP9"
    }
  },
  "id": "<Base32-UUIDv4>",
  "label": "<String>",
  "multistream": "<Boolean>",
  "node_name": "<String>",
  "role": "<sendrecv | sendonly | recvonly>",
  "simulcast": "<Boolean>",
  "spotlight": "<Boolean>",
  "version": "<String>"
}
```


### connection.updated

**接続状態更新**

接続したタイミングから、クライアントごとの接続状態が、それぞれ 1 分間に 1 回送られてきます。

- data.channel_connections- 現在そのチャネルに接続しているクライアントの接続数です
  - 自分が含まれます
- data.channel_sendrecv_connections- 現在そのチャネルで送受信している配信者の接続数です
  - 自分が含まれます
- data.channel_sendonly_connections- 現在そのチャネルを送信のみしている配信者の接続数です
  - 自分が含まれます
- data.channel_recvonly_connections- 現在そのチャネルを受信のみしている視聴者の接続数です
  - 自分が含まれます

```json
{
  "type": "connection.updated",
  "channel_id": "<String>",
  "session_id": "<Base32-UUIDv4>",
  "client_id": "<String | Base32-UUIDv4>",
  "bundle_id": "<String | Base32-UUIDv4>",
  "connection_id": "<Base32-UUIDv4>",
  "timestamp": "<UTC RFC3339 Timestamp>",
  "event_metadata": "<JSON>",
  "data": {
    "audio": {
      "codec_type": "<String>"
    },
    "channel_connections": ,
    "channel_recvonly_connections": "<Integer>",
    "channel_sendonly_connections": "<Integer>",
    "channel_sendrecv_connections": "<Integer>",
    "created_time": "<UNIX-Time>",
    "created_timestamp": "<UTC RFC3339 Timestamp>",
    "minutes": "<Integer>",
    "total_received_bytes": "<Integer>",
    "total_sent_bytes": "<Integer>",
    "turn_transport_type": "<String>",
    "video": {
      "bit_rate": "<Integer>",
      "codec_type": "<String>"
    }
  },
  "id": "<Base32-UUIDv4>",
  "label": "<String>",
  "multistream": "<Boolean>",
  "node_name": "<String>",
  "role": "<sendrecv | sendonly | recvonly>",
  "simulcast": "<Boolean>",
  "spotlight": "<Boolean>",
  "version": "<String>"
}
```


### connection.failed

**接続失敗**

> **重要**
>
> このイベントを受信する場合は、 `sora.conf` にて [ignore_connection_failed_webhook](SORA_CONF.html#45096a) を無効にする必要があります。

シグナリングが失敗したり、異常が起きたりした場合に送信されます。

```json
{
    "type": "connection.failed",
    "id": "<Base32-UUIDv4>",
    "role": "<sendrecv | sendonly | recvonly>",
    "channel_id": "<String>",
    "client_id": "<String | Base32-UUIDv4>",
    "bundle_id": "<String | Base32-UUIDv4>",
    "connection_id": "<Base32-UUIDv4>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "data": {
        "message": "<String>",
        "channel_connections": "<Integer>",
        "channel_sendrecv_connections": "<Integer>",
        "channel_sendonly_connections": "<Integer>",
        "channel_recvonly_connections": "<Integer>",
        "total_received_bytes": "<Integer>",
        "total_sent_bytes": "<Integer>"
    },
    "label": "<String>",
    "multistream": "<Boolean>",
    "node_name": "<String>",
    "simulcast": "<Boolean>",
    "spotlight": "<Boolean>",
    "version": "<String>"
}
```

## 録画・録音イベント


### recording.started

> **重要**
>
> このイベントには `event_metadata` は含まれません

**録画開始**

- data.metadata- StartRecording API で指定した metadata が入ります
  - 指定していない場合は `metadata` の項目が入ってきません

```json
{
    "type": "recording.started",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "data": {
        "channel_id": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "metadata": "<JSON-Object>",
        "split_only": "<Boolean>",
        "created_at": "<UNIX-Time>",
        "expire_time": "<Integer>",
        "expired_at": "<UNIX-Time>"
    }
}
```


### recording.report

> **重要**
>
> このイベントには `event_metadata` は含まれません

**録画結果報告**

- data.archives[].start_time_offset- StartRecording API を叩いてから何秒経過した後にこの録画が開始したかを表しています
- data.archives[].stop_time_offset- StartRecording API を叩いてから何秒経過した後にこの録画が終了したかを表しています
- data.metadata- StartRecording API で指定した metadata が入ります
  - 指定していない場合は `metadata` の項目が入ってきません
- data.expired_at- 期限が切れた日時を UNIX Time で返します

#### 一括録画時 recording.report

```json
{
    "type": "recording.report",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "data": {
        "channel_id": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "metadata": "<JSON-Object>",
        "split_only": "<Boolean>",
        "created_at": "<UNIX-Time>",
        "expire_time": "<Integer>",
        "expired_at": "<UNIX-Time>",
        "file_path": "<String>",
        "filename": "<String>",
        "start_timestamp": "<UTC RFC3339 Timestamp>",
        "stop_timestamp": "<UTC RFC3339 Timestamp>",
        "archives": [
            {
                "label": "<String>",
                "node_name": "<String>",
                "client_id": "<String | Base32-UUIDv4>",
                "bundle_id": "<String | Base32-UUIDv4>",
                "connection_id": "<Base32-UUIDv4>",
                "file_path": "<String>",
                "filename": "<String>",
                "metadata_file_path": "<String>",
                "metadata_filename": "<String>",
                "start_time_offset": "<Integer>",
                "start_timestamp": "<UTC RFC3339 Timestamp>",
                "stop_time_offset": "<Integer>",
                "stop_timestamp": "<UTC RFC3339 Timestamp>",
                "size": "<Integer>"
            },
            {
                "label": "<String>",
                "node_name": "<String>",
                "client_id": "<String | Base32-UUIDv4>",
                "bundle_id": "<String | Base32-UUIDv4>",
                "connection_id": "<Base32-UUIDv4>",
                "file_path": "<String>",
                "filename": "<String>",
                "metadata_file_path": "<String>",
                "metadata_filename": "<String>",
                "start_time_offset": "<Integer>",
                "start_timestamp": "<UTC RFC3339 Timestamp>",
                "stop_time_offset": "<Integer>",
                "stop_timestamp": "<UTC RFC3339 Timestamp>",
                "size": "<Integer>"
            }
        ]
    }
}
```

#### 分割録画時 recording.report

```json
{
    "type": "recording.report",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "data": {
        "channel_id": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "metadata": "<JSON-Object>",
        "split_only": "<Boolean>",
        "split_duration": "<Integer>",
        "created_at": "<UNIX-Time>",
        "expire_time": "<Integer>",
        "expired_at": "<UNIX-Time>",
        "file_path": "<String>",
        "filename": "<String>",
        "start_timestamp": "<UTC RFC3339 Timestamp>",
        "stop_timestamp": "<UTC RFC3339 Timestamp>",
        "archives": [
            {
                "label": "<String>",
                "node_name": "<String>",
                "client_id": "<String | Base32-UUIDv4>",
                "bundle_id": "<String | Base32-UUIDv4>",
                "connection_id": "<Base32-UUIDv4>",
                "start_time_offset": "<Integer>",
                "start_timestamp": "<UTC RFC3339 Timestamp>",
                "stop_time_offset": "<Integer>",
                "stop_timestamp": "<UTC RFC3339 Timestamp>",
                "split_last_index": "<String>"
            },
            {
                "label": "<String>",
                "node_name": "<String>",
                "client_id": "<String | Base32-UUIDv4>",
                "bundle_id": "<String | Base32-UUIDv4>",
                "connection_id": "<Base32-UUIDv4>",
                "start_time_offset": "<Integer>",
                "start_timestamp": "<UTC RFC3339 Timestamp>",
                "stop_time_offset": "<Integer>",
                "stop_timestamp": "<UTC RFC3339 Timestamp>",
                "split_last_index": "<String>"
            }
        ]
    }
}
```

## 録画・録音保存イベント


### archive.started

**録画ファイル保存開始**

```json
{
    "type": "archive.started",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "session_id": "<Base32-UUIDv4>",
    "client_id": "<String | Base32-UUIDv4>",
    "bundle_id": "<String | Base32-UUIDv4>",
    "connection_id": "<Base32-UUIDv4>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "event_metadata": "<JSON>",
    "data": {
        "channel_id": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "session_id": "<Base32-UUIDv4>",
        "client_id": "<String | Base32-UUIDv4>",
        "bundle_id": "<String | Base32-UUIDv4>",
        "connection_id": "<Base32-UUIDv4>",
        "created_at": "<Unix Time>",
        "audio": {
            "codec_type": "<String>"
        },
        "video": {
            "codec_type": "<String>",
            "bit_rate": "<Integer>",
            "height": "<Integer>",
            "width": "<Integer>"
        },
        "start_time": "<Unix Time>",
        "start_time_offset": "<Integer>",
        "start_timestamp": "<UTC RFC3339 Timestamp>"
    }
}
```


### archive.available

**録画保存ファイル出力**

- data.start_timestamp- この録画が開始された時間 (UTC) を RFC 3339 形式 (マイクロ秒) で表しています
- data.stop_timestamp- この録画が終了した時間(UTC) を RFC 3339 形式 (マイクロ秒) で表しています
- data.start_time- この録画が開始された時間を UNIX 時間形式で表しています
- data.stop_time- この録画が終了した時間を UNIX 時間形式で表しています
- data.start_time_offset- StartRecording API を叩いてから何秒経過した後にこの録画が開始したかを表しています
- data.stop_time_offset- StartRecording API を叩いてから何秒経過した後にこの録画が終了したかを表しています
- data.stats- サポート向けに、録画に使用したパケット情報の統計を格納しています
  - 内部向け情報のため、予告なく変更されることがあります

```json
{
    "type": "archive.available",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "session_id": "<Base32-UUIDv4>",
    "client_id": "<String | Base32-UUIDv4>",
    "bundle_id": "<String | Base32-UUIDv4>",
    "connection_id": "<Base32-UUIDv4>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "event_metadata": "<JSON>",
    "data": {
        "channel_id": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "session_id": "<Base32-UUIDv4>",
        "client_id": "<String | Base32-UUIDv4>",
        "bundle_id": "<String | Base32-UUIDv4>",
        "connection_id": "<Base32-UUIDv4>",
        "created_at": "<Unix Time>",
        "file_path": "<String>",
        "filename": "<String>",
        "metadata_file_path": "<String>",
        "metadata_filename": "<String>",
        "size": "<Integer>",
        "audio": {
            "codec_type": "<String>"
        },
        "video": {
            "codec_type": "<String>",
            "bit_rate": "<Integer>",
            "height": "<Integer>",
            "width": "<Integer>"
        },
        "stats": {},
        "start_time": "<Unix Time>",
        "start_time_offset": "<Integer>",
        "start_timestamp": "<UTC RFC3339 Timestamp>",
        "stop_time": "<Unix Time>",
        "stop_time_offset": "<Integer>",
        "stop_timestamp": "<UTC RFC3339 Timestamp>",
    }
}
```


### split-archive.available

**分割録画ファイル出力**

- data.split_index- 分割された録画ファイルのインデックス
  - 0001 から始まり 9999 までいったら、その後は 10000, 10001 と増えていきます
- data.start_timestamp- 分割して出力された録画ファイルを開始された時間 (UTC) を RFC 3339 形式 (マイクロ秒) で表しています
- data.stop_timestamp- 分割して出力された録画ファイルを終了した時間 (UTC) を RFC 3339 形式 (マイクロ秒) で表しています
- data.start_time- 分割して出力された録画ファイルを開始した時間を UNIX 時間形式で表しています
- data.stop_time- 分割して出力された録画ファイルを終了した時間を UNIX 時間形式で表しています
- data.start_time_offset- この分割して出力された録画が、StartRecording API を叩いてから何秒経過した後に開始したかを表しています
- data.stop_time_offset- この分割して出力された録画が、StartRecording API を叩いてから何秒経過した後に終了したかを表しています
- data.stats- サポート向けに、録画に使用したパケット情報の統計を格納しています
  - 内部向け情報のため、予告なく変更されることがあります

```json
{
    "type": "split-archive.available",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "session_id": "<Base32-UUIDv4>",
    "client_id": "<String | Base32-UUIDv4>",
    "bundle_id": "<String | Base32-UUIDv4>",
    "connection_id": "<Base32-UUIDv4>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "event_metadata": "<JSON>",
    "data": {
        "channel_id": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "split_index": "<String>",
        "session_id": "<Base32-UUIDv4>",
        "client_id": "<String | Base32-UUIDv4>",
        "bundle_id": "<String | Base32-UUIDv4>",
        "connection_id": "<Base32-UUIDv4>",
        "created_at": "<Unix Time>",
        "file_path": "<String>",
        "filename": "<String>",
        "metadata_file_path": "<String>",
        "metadata_filename": "<String>",
        "size": "<Integer>",
        "video": {
            "codec_type": "<String>",
            "bit_rate": "<Integer>",
            "height": "<Integer>",
            "width": "<Integer>"
        },
        "audio": {
            "codec_type": "<String>"
        },
        "stats": {
        },
        "start_time": "<Unix Time>",
        "start_time_offset": "<Integer>",
        "start_timestamp": "<UTC RFC3339 Timestamp>",
        "stop_time": "<Unix Time>",
        "stop_time_offset": "<Integer>",
        "stop_timestamp": "<UTC RFC3339 Timestamp>",
    }
}
```

> **注釈**
>
> stats は省略しています


### split-archive.end

**分割録画終了**

- data.start_time- 録画を開始した UNIX 時間形式で表しています
- data.stop_time- 録画を終了した UNIX 時間形式で表しています
- data.start_timestamp- 録画を開始した時間 (UTC) を RFC 3339 形式 (マイクロ秒) で表しています
- data.stop_timestamp- 録画を終了した時間 (UTC) を RFC 3339 形式 (マイクロ秒) で表しています
- data.stats- サポート向けに、録画に使用したパケット情報の統計を格納しています
  - 内部向け情報のため、予告なく変更されることがあります

```json
{
    "type": "split-archive.end",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "channel_id": "<String>",
    "session_id": "<Base32-UUIDv4>",
    "client_id": "<String | Base32-UUIDv4>",
    "bundle_id": "<String | Base32-UUIDv4>",
    "connection_id": "<Base32-UUIDv4>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "event_metadata": "<JSON>",
    "data": {
        "split_last_index": "<String>",
        "recording_id": "<Base32-UUIDv4>",
        "channel_id": "<String>",
        "session_id": "<Base32-UUIDv4>",
        "client_id": "<String | Base32-UUIDv4>",
        "bundle_id": "<String | Base32-UUIDv4>",
        "connection_id": "<Base32-UUIDv4>",
        "video": {
            "codec_type": "<String>",
            "bit_rate": "<Integer>"
        },
        "audio": {
            "codec_type": "<String>",
            "bit_rate": "<Integer>"
        },
        "file_path": "<String>",
        "filename": "<String>",
        "stats": {},
        "start_time": "<Unix Time>",
        "start_time_offset": "<Integer>",
        "start_timestamp": "<UTC RFC3339 Timestamp>",
        "stop_time": "<Unix Time>",
        "stop_time_offset": "<Integer>",
        "stop_timestamp": "<UTC RFC3339 Timestamp>",
    }
}
```


### archive.failed

**録画ファイル保存失敗**

```json
{
    "type": "archive.failed",
    "id": "<Base32-UUIDv4>",
    "version": "<String>",
    "label": "<String>",
    "node_name": "<String>",
    "timestamp": "<UTC RFC3339 Timestamp>",
    "channel_id": "<String>",
    "session_id": "<Base32-UUIDv4>",
    "client_id": "<String | Base32-UUIDv4>",
    "bundle_id": "<String | Base32-UUIDv4>",
    "connection_id": "<Base32-UUIDv4>",
    "event_metadata": "<JSON>",
    "data": {
        "recording_id": "<Base32-UUIDv4>",
        "session_id": "<Base32-UUIDv4>",
        "file_path": "<String>",
        "filename": "<String>",
        "video": {
            "codec_type": "<String>",
            "bit_rate": "<Integer>",
            "height": "<Integer>",
            "width": "<Integer>"
        },
        "audio": {
            "codec_type": "<String>"
        },
        "stats": {},
        "start_time": "<Unix Time>",
        "start_timestamp": "<UTC RFC3339 Timestamp>",
        "stop_time": "<Unix Time>",
        "stop_timestamp": "<UTC RFC3339 Timestamp>",
    }
}
```

## スポットライト機能


### spotlight.focused

**フォーカス設定**

フォーカスされた際にウェブフックが飛びます。

```json
{
  "type": "spotlight.focused",
  "channel_id": "<String>",
  "client_id": "<String | Base32-UUIDv4>",
  "bundle_id": "<String | Base32-UUIDv4>",
  "connection_id": "<Base32-UUIDv4>",
  "timestamp": "<UTC RFC3339 Timestamp>",
  "spotlight_number:": "<Integer>",
  "fixed": "<Boolean>",
  "id": "<Base32-UUIDv4>",
  "label": "<String>",
  "version": "<String>",
  "audio": "<Boolean>",
  "video": "<Boolean>"
}
```


### spotlight.unfocused

**フォーカス解除**

フォーカスが外れた際にウェブフックが飛びます。

```json
{
  "type": "spotlight.unfocused",
  "channel_id": "<String>",
  "client_id": "<String | Base32-UUIDv4>",
  "bundle_id": "<String | Base32-UUIDv4>",
  "connection_id": "<Base32-UUIDv4>",
  "timestamp": "<UTC RFC3339 Timestamp>",
  "spotlight_number:": "<Integer>",
  "audio": "<Boolean>",
  "id": "<Base32-UUIDv4>",
  "label": "<String>",
  "version": "<String>",
  "audio": "<Boolean>",
  "video": "<Boolean>"
}
```


## シーケンス図


### connection.*

seqdiag {
    default_fontsize = 15;
    edge_length = 250;

    === 認証成功 ===

    クライアント <<- Sora [label = '"type": "offer"'];
    クライアント ->> Sora [label = '"type": "answer"', noactivate];

    === WebRTC 確立 ===

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.created"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    ... 接続から 1 分経過 ...

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.updated"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント ->> Sora [label = '"type": "disconnect"', noactivate];

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.destroyed"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント <<- Sora [label = 'WebSocket Close'];
}


### event_metadata

seqdiag {
    default_fontsize = 15;
    edge_length = 250;

    クライアント ->> Sora [label = '"type": "connect"'];
        Sora -> アプリケーションサーバー [label = '認証ウェブフック'];
        Sora <-- アプリケーションサーバー [label = '200 OK\n"allowed": true,\n"event_metadata": {"pk": 1}'];
    クライアント <<- Sora [label = '"type": "offer"'];
    クライアント ->> Sora [label = '"type": "answer"', noactivate];

    === WebRTC 確立 ===

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.created",\n"event_metadata": {"pk": 1}'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    ... 接続から 1 分経過 ...

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.updated",\n"event_metadata": {"pk": 1}'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント ->> Sora [label = '"type": "disconnect"', noactivate];

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.destroyed",\n"event_metadata": {"pk": 1}'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    クライアント <<- Sora [label = 'WebSocket Close'];
}
