> ## Documentation Index
> Fetch the complete documentation index at: https://x-preview-mintlify-5394b2f8.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# グループ会話

> 共有の会話鍵、暗号化されたタイトル、メンバー管理、署名付きメッセージを備えた、複数参加者の X Chat グループ会話を作成します。

グループチャットは 1:1 の X Chat と**同じ暗号化モデル**を使用します:1 つの**会話鍵**をメンバーで共有し、各メンバーの**アイデンティティ公開鍵**でラップし、Chat XDK によってメッセージを暗号化・署名します。変わるのは**メンバーシップ**、**会話の作成方法**、そしてしばしば会話上の**暗号化されたタイトル/アバター**フィールドです。

1:1 のフローは[はじめに](/ja/xchat/getting-started)にあります。エンドポイントの詳細は **API リファレンス → Conversations and messages** にあります。

***

## グループが 1:1 と異なる点

| トピック     | 1:1                   | グループ                           |
| :------- | :-------------------- | :----------------------------- |
| アイデンティティ | パスでピアのユーザー ID がよく使われる | 会話 ID は通常 `g` で始まる             |
| 作成       | ユーザーへの鍵 + メッセージング     | グループの作成 / 初期化 API、次に鍵          |
| 参加者      | あなた + 1 人のピア          | 多数のユーザー。メンバーシップは変わり得る          |
| メタデータ    | 最小限                   | 名前、アバターなどが**暗号文**であり得る(会話鍵で復号) |
| 鍵ローテーション | 頻度は低い                 | 参加や退出時によく起こる                   |

暗号処理は変わりません:鍵とペイロードには **Chat XDK**、グループの作成、参加者向け鍵ラップの公開、メッセージの送信、イベントのロードには **X API**。

***

## グループを作成して鍵を確立する

1. `POST /2/chat/conversations/group/initialize` でグループ ID を発行します——レスポンスの `data.conversation_id` が、以降どこでも使う g プレフィックス付きの ID です。
2. 各メンバーのアイデンティティ公開鍵と `public_key_version` をロードします(**Encryption keys** の下の公開鍵 `GET` ルート。[`GET /2/users/public_keys`](/x-api/users/get-public-keys-for-multiple-users) は複数のユーザーを 1 回のリクエストで取得します)。使用前に各レコードを `verify_key_binding` で検証してください([はじめに](/ja/xchat/getting-started#4-set-up-conversation-keys) の警告を参照)。
3. **`prepare_group_create`** を、**すべての**メンバー(自分自身を含む)、g プレフィックス付きの ID、メンバー/管理者の ID リストとともに一度実行します。この 1 回の呼び出しで会話鍵を生成し、すべてのメンバー向けにラップし、`set_identity` からのセッションアイデンティティで作成に署名します——**2 つ**のアクション署名を返します(会話鍵の変更とグループ作成)。
4. `POST /2/chat/conversations/group` に、グループメンバー/管理者、`conversation_key_version`、`conversation_participant_keys`(SDK の **`encrypted_key`** → API の **`encrypted_conversation_key`**)、および **両方の** `action_signatures` を渡します。検証失敗は安定した人間可読メッセージとして返されます。例:`"Too many members: adding these members would exceed the allowed group size."` や `"Cannot add all members: one or more of the requested members cannot be added to this conversation."` など。
5. 暗号化/復号のために**生の**会話鍵と**バージョン**を保持します。

`prepare_group_create` は渡した `title` と `avatar_url` に署名し、それらをグループ作成イベントに逐語的に埋め込みます。サーバーはそれらをリクエストと照合するので、POST ボディの `group_name` / `group_avatar_url` の値は SDK に渡したものと**バイト単位で同一**でなければなりません——さもないと署名検証で呼び出しが失敗します。

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    # chat has keys loaded and set_identity called (see Getting Started)
    prepared = chat.prepare_group_create(
        member_public_keys,
        group_id,  # g-prefixed id from POST /2/chat/conversations/group/initialize
        member_ids, admin_ids, title="Project team",
    )
    # POST /2/chat/conversations/group with group_members, group_admins,
    # conversation_key_version, conversation_participant_keys, and BOTH
    # entries of prepared["action_signatures"]
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    // chat has keys loaded and setIdentity called (see Getting Started)
    const prepared = chat.prepareGroupCreate({
      publicKeys: memberPublicKeys,
      conversationId: groupId, // g-prefixed id from POST /2/chat/conversations/group/initialize
      memberIds, adminIds, title: 'Project team',
    });
    // prepared.actionSignatures has two entries — send both
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    // chat has keys loaded and set_identity called (see Getting Started)
    let mut params = GroupCreateParams::new(
        member_public_keys, &group_id, member_ids, admin_ids,
    );
    params.title = Some("Project team".into());
    let prepared = chat.prepare_group_create(params)?;
    // prepared.action_signatures has two entries — send both
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    // chat has keys loaded and SetIdentity called (see Getting Started)
    prepared, err := chat.PrepareGroupCreate(chatxdk.GroupCreateParams{
        PublicKeys: memberPublicKeys, ConversationID: groupID,
        MemberIDs: memberIDs, AdminIDs: adminIDs, Title: "Project team",
    })
    // prepared.ActionSignatures has two entries — send both
    _ = prepared
    _ = err
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    // chat has keys loaded and SetIdentity called (see Getting Started)
    var prepared = chat.PrepareGroupCreate(
        new GroupCreateParams(memberPublicKeys, groupId, memberIds, adminIds)
        {
            Title = "Project team",
        });
    // prepared.ActionSignatures has two entries — send both
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    // chat has keys loaded and setIdentity called (see Getting Started)
    GroupCreateParams params =
        new GroupCreateParams(memberPublicKeys, groupId, memberIds, adminIds);
    params.title = "Project team";
    PreparedConversationChange prepared = chat.prepareGroupCreate(params);
    // prepared.actionSignatures has two entries — send both
    ```
  </Tab>
</Tabs>

参加者鍵とアクション署名(`message_id`、`encoded_message_event_detail`、ネストされた `message_event_signature`)のボディマッピングは、[はじめに — 会話鍵](/ja/xchat/getting-started#4-set-up-conversation-keys) の keys POST と同じです。

メンバーシップが変わるときは、新しいメンバー ID と現在の名簿(メンバー、管理者、保留中のメンバー、および現在のタイトル/アバター/TTL が設定されていればそれら)とともに **`prepare_group_members_change`** を呼び出します。会話鍵をローテーションし、グループ作成と同様に**2 つ**のアクション署名を返します——すべてを **add members**(`POST /2/chat/conversations/{id}/members`)に POST してください。その後、**鍵変更**トラフィックが来ることを想定してください:[はじめにの鍵ローテーション](/ja/xchat/getting-started#6-receive-and-decrypt)と同じように扱ってください(`extract_conversation_keys` / `decrypt_events`、その後に最新バージョンで暗号化)。

`prepare_group_members_change` は渡した名簿にのみラップされた**新しい**会話鍵を生成するため、新規メンバーは新しい鍵バージョンを受け取り、以前のバージョンで送られたメッセージを復号することはできません。逆は成り立ちません:ローテーションは**以前の**バージョンへのアクセスを取り消しません——古い鍵を既に持っている人は誰でも、その鍵で暗号化されたメッセージを引き続き読めます。会話鍵が漏えいした疑いがある場合は `prepare_conversation_key_change` でローテーションしてください。これは将来のメッセージのみを保護します。

***

## 暗号化されたグループメタデータ

一部の会話フィールド(たとえば表示**名**や**アバター URL**)は会話鍵で**暗号化された**まま届く場合があります。これは `encrypt_message` ではなく、Chat XDK の汎用 **`encrypt` / `decrypt`** ペアです(UTF-8 文字列を入力、base64 暗号文を出力、**生の**会話鍵を使用)。

あるフィールドが暗号化されて保存されるかどうかは、それを書き込むクライアントが決定します:`prepare_group_create` はあなたが渡したタイトルをそのまま署名して送信します(会話鍵はその呼び出しが生成するまで存在しないので、作成時のタイトルはその鍵で暗号化できません)。フィールドが暗号文になっている会話を読むときは、そのフィールドが書き込まれた時点で有効だった鍵バージョンで `decrypt` を使って復号してください。

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    # Decrypt a field from the conversation object (name may vary by API shape)
    group_name = chat.decrypt(conversation["group_name"], raw_conv_key)

    # Encrypt before update if your API accepts ciphertext metadata
    encrypted_name = chat.encrypt("Project team", raw_conv_key)
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    const groupName = chat.decrypt(conversation.groupName, rawConvKey);
    const encryptedName = chat.encrypt('Project team', rawConvKey);
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    // conv_key: &XChatConversationKey from extract_conversation_keys / decrypt_conversation_key
    let group_name = chat.decrypt(&conversation_group_name_b64, &conv_key)?;
    let encrypted_name = chat.encrypt("Project team", &conv_key)?;
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    groupName, err := chat.Decrypt(conversationGroupNameB64, rawConvKey)
    encryptedName, err := chat.Encrypt("Project team", rawConvKey)
    _ = groupName
    _ = encryptedName
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    string groupName = chat.Decrypt(conversationGroupNameB64, rawConvKey);
    string encryptedName = chat.Encrypt("Project team", rawConvKey);
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    String groupName = chat.decrypt(conversationGroupNameB64, rawConvKey);
    String encryptedName = chat.encrypt("Project team", rawConvKey);
    ```
  </Tab>
</Tabs>

そのメタデータに該当する**現在の**会話鍵バージョンを使用してください。鍵がローテーションされている場合は、フィールドが書き込まれた時点で有効だった鍵バージョンで復号してください(あるいはメタデータがローテーション時に常に書き直されるならプロダクトのルールに従ってください)。

***

## メッセージとイベント

生の会話鍵を持っていれば、グループでの送受信は 1:1 と同じです。

* **送信:** `encrypt_message` → send-message API([はじめに](/ja/xchat/getting-started#5-send-a-message))
* **受信:** events API または[リアルタイム配信](/ja/xchat/real-time-events) → `decrypt_event` / `decrypt_events`
* **メディア:** グループの会話 ID を使った[メディア](/ja/xchat/media)

メンバーシップ由来のローテーション後は、常に**最新の**鍵バージョンで暗号化してください。

### 離脱したメンバーによる鍵変更

グループの鍵変更イベントは、それを実行した人物——多くの場合、作成者または管理者——によって署名されます。そのメンバーが後に**グループを離脱**した(あるいは非有効化された)場合、公開鍵エンドポイントは彼らの鍵を返さなくなるため、検証付きの復号パス(署名鍵付きの `decrypt_events`)はそれらの鍵変更イベントで `signature missing or no matching signing key` により失敗します。イベントが壊れているわけではなく、検証用の資材が提供されなくなっただけです。

長期に存続するグループはこれを想定し、検証できない鍵変更イベントについては **`extract_conversation_keys`** にフォールバックしてください。このパスは署名チェックをスキップし、あなたのアイデンティティ鍵で復号することで会話鍵を復元します。セキュリティモデルは以下の理由で保たれます:

* **あなたのアイデンティティ鍵に向けて暗号化された**鍵素材のみが復元可能です——第三者があなたに読める鍵を注入することはできません
* **各メッセージ**は依然として送信者自身に対して署名検証されるため、メッセージの発信者性(authorship)は影響を受けません

検証パスを優先してください:`decrypt_events` を使い(`set_cache_keys(true)` が有効なときは SDK の鍵キャッシュも同時に更新します)、それが拒否する鍵変更イベントについてのみ `extract_conversation_keys` を用いてください。

***

## チェックリスト

1. `POST /2/chat/conversations/group/initialize` で g プレフィックス付きの ID を発行する
2. **すべての**メンバーで `prepare_group_create`。参加者鍵ラップと**両方**のアクション署名を `POST /2/chat/conversations/group` に POST する
3. 生の鍵 + バージョンをキャッシュし、鍵変更イベントで更新する
4. メンバーシップ変更時は `prepare_group_members_change`(2 つの署名)→ `POST /2/chat/conversations/{id}/members`
5. フィールドが暗号文の場合はグループメタデータを `decrypt` で復号する
6. 送受信は 1:1 と同じパターンで行う
