> ## Documentation Index
> Fetch the complete documentation index at: https://docs.x.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Conversaciones de grupo

> Crea conversaciones de grupo de X Chat con varios participantes, claves de conversación compartidas, títulos cifrados y mensajes firmados.

Los chats de grupo usan el **mismo modelo de cifrado** que un X Chat 1:1: una **clave de conversación** compartida por los miembros, envuelta hacia la **clave pública de identidad** de cada miembro, con mensajes cifrados y firmados por el Chat XDK. Lo que cambia es la **membresía**, **cómo creas la conversación** y, a menudo, los campos de **título/avatar cifrados** en la conversación.

Los flujos 1:1 están en [Primeros pasos](/xchat/getting-started). Los detalles de los endpoints están bajo **API reference → Conversations and messages**.

***

## En qué se diferencian los grupos de los 1:1

| Tema               | 1:1                                                                   | Grupo                                                                                     |
| :----------------- | :-------------------------------------------------------------------- | :---------------------------------------------------------------------------------------- |
| Identidad          | Frecuentemente direccionado por el id de usuario del par en las rutas | El id de conversación normalmente comienza con `g`                                        |
| Crear              | Claves + mensajería a un usuario                                      | APIs de crear / inicializar grupo, luego claves                                           |
| Participantes      | Tú + un par                                                           | Muchos usuarios; la membresía puede cambiar                                               |
| Metadatos          | Mínimos                                                               | Nombre, avatar, etc. pueden ser **texto cifrado** (descifra con la clave de conversación) |
| Rotación de claves | Menos frecuente                                                       | Común cuando entra o sale gente                                                           |

La criptografía sigue siendo: **Chat XDK** para claves y cargas útiles; **X API** para crear el grupo, publicar los envoltorios de clave para los participantes, enviar mensajes y cargar eventos.

***

## Crea el grupo y establece las claves

1. Genera el id de grupo con `POST /2/chat/conversations/group/initialize` — el `data.conversation_id` de la respuesta es el id con prefijo `g` que usas en todo lo que sigue.
2. Carga la clave pública de identidad y el `public_key_version` de cada miembro (rutas `GET` de claves públicas bajo **Encryption keys**; [`GET /2/users/public_keys`](/x-api/users/get-public-keys-for-multiple-users) obtiene varios usuarios en una sola solicitud). Verifica cada registro con `verify_key_binding` antes de usarlo (consulta la advertencia en [Primeros pasos](/xchat/getting-started#4-set-up-conversation-keys)).
3. Ejecuta **`prepare_group_create`** una vez, con **todos** los miembros (incluyéndote a ti mismo), el id con prefijo `g` y las listas de ids de miembros/administradores. Una sola llamada genera la clave de conversación, la envuelve para cada miembro y firma la creación — devuelve **dos** firmas de acción (el cambio de clave de conversación y la creación del grupo).
4. `POST /2/chat/conversations/group` con los miembros/administradores del grupo, `conversation_key_version`, `conversation_participant_keys` (SDK **`encrypted_key`** → API **`encrypted_conversation_key`**) y **ambas** `action_signatures`. Los fallos de validación regresan como mensajes estables y legibles, por ejemplo `"Too many members: adding these members would exceed the allowed group size."` o `"Cannot add all members: one or more of the requested members cannot be added to this conversation."`.
5. Conserva la clave de conversación **en bruto** y la **versión** para cifrar/descifrar.

El `title` y el `avatar_url` que pasas a `prepare_group_create` se firman y se incrustan textualmente en el evento de creación del grupo, y el servidor los compara con la solicitud — así que los valores `group_name` / `group_avatar_url` en el cuerpo del POST deben ser **byte a byte idénticos** a lo que pasaste al SDK, o la llamada falla la validación de firma.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    prepared = chat.prepare_group_create(
        "YOUR_USER_ID", signing_key_version, 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}
    const prepared = chat.prepareGroupCreate({
      senderId: myUserId, signingKeyVersion, 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}
    let mut params = GroupCreateParams::new(
        &sender_id, &signing_key_version, 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}
    prepared, err := chat.PrepareGroupCreate(chatxdk.GroupCreateParams{
        SenderID: myUserID, SigningKeyVersion: signingKeyVersion,
        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}
    var prepared = chat.PrepareGroupCreate(new GroupCreateParams {
        SenderId = myUserId, SigningKeyVersion = signingKeyVersion,
        PublicKeys = memberPublicKeys, ConversationId = groupId,
        MemberIds = memberIds, AdminIds = adminIds, Title = "Project team",
    });
    // prepared.ActionSignatures has two entries — send both
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    GroupCreateParams params = new GroupCreateParams();
    params.senderId = myUserId;
    params.signingKeyVersion = signingKeyVersion;
    params.publicKeys = memberPublicKeys;
    params.conversationId = groupId;
    params.memberIds = memberIds;
    params.adminIds = adminIds;
    params.title = "Project team";
    PreparedConversationChange prepared = chat.prepareGroupCreate(params);
    // prepared.actionSignatures has two entries — send both
    ```
  </Tab>
</Tabs>

El mapeo del cuerpo para las claves de participantes y las firmas de acción (`message_id`, `encoded_message_event_detail`, `message_event_signature` anidado) es el mismo que el POST de claves en [Primeros pasos — claves de conversación](/xchat/getting-started#4-set-up-conversation-keys).

Cuando cambia la membresía, llama a **`prepare_group_members_change`** con los nuevos ids de miembros más el roster actual (miembros, administradores, miembros pendientes y el título/avatar/TTL actual si está configurado). Rota la clave de conversación y, al igual que la creación de grupo, devuelve **dos** firmas de acción — envía todo con POST a **add members** (`POST /2/chat/conversations/{id}/members`). Después espera tráfico de **cambio de clave**: trátalo como la [rotación de claves en Primeros pasos](/xchat/getting-started#6-receive-and-decrypt) (`extract_conversation_keys` / `decrypt_events`, luego cifra con la versión más reciente).

Como `prepare_group_members_change` genera una clave de conversación **nueva** envuelta solo para el roster que pasas, los nuevos miembros reciben la nueva versión de la clave y no pueden descifrar los mensajes enviados bajo versiones anteriores. Lo inverso no es cierto: la rotación nunca revoca el acceso a **versiones anteriores** — cualquiera que ya tenga una clave vieja puede seguir leyendo los mensajes cifrados con ella. Si sospechas que una clave de conversación fue expuesta, rota con `prepare_conversation_key_change`; esto protege únicamente los mensajes futuros.

***

## Metadatos de grupo cifrados

Algunos campos de conversación (por ejemplo el **nombre** para mostrar o la **URL del avatar**) pueden llegar **cifrados** con la clave de conversación. Eso **no** es `encrypt_message`; es el par genérico **`encrypt` / `decrypt`** del Chat XDK (cadena UTF-8 de entrada, texto cifrado base64 de salida, con la clave de conversación **en bruto**).

Si un campo dado se almacena cifrado lo decide el cliente que lo escribe: `prepare_group_create` firma y envía el título exactamente como se lo proporcionas (la clave de conversación no existe hasta que esa llamada la genera, así que un título en el momento de la creación no puede cifrarse con ella). Cuando lees una conversación cuyos campos son texto cifrado, descífralos con `decrypt` y la versión de clave que estaba activa cuando el campo se escribió.

<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>

Usa la versión **actual** de la clave de conversación que aplica a esos metadatos. Si las claves rotaron, descifra con la versión que estaba activa cuando el campo se escribió (o sigue las reglas del producto si los metadatos siempre se reescriben en la rotación).

***

## Mensajes y eventos

Enviar y recibir en un grupo es lo mismo que en un 1:1 una vez que tienes la clave de conversación en bruto:

* **Enviar:** `encrypt_message` → API send-message ([Primeros pasos](/xchat/getting-started#5-send-a-message))
* **Recibir:** API de events o [entrega en tiempo real](/xchat/real-time-events) → `decrypt_event` / `decrypt_events`
* **Multimedia:** [Multimedia](/xchat/media) con el id de conversación del grupo

Siempre cifra con la versión **más reciente** de la clave después de una rotación impulsada por cambios de membresía.

***

## Lista de verificación

1. Genera el id con prefijo `g` con `POST /2/chat/conversations/group/initialize`
2. `prepare_group_create` con **cada** miembro; envía con POST los envoltorios de clave de participantes y **ambas** firmas de acción a `POST /2/chat/conversations/group`
3. Cachea la clave en bruto + versión; actualiza en eventos de cambio de clave
4. En cambios de membresía, `prepare_group_members_change` (dos firmas) → `POST /2/chat/conversations/{id}/members`
5. Descifra los metadatos del grupo con `decrypt` cuando los campos sean texto cifrado
6. Envía/recibe con los mismos patrones que en 1:1
