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

# Conversas em grupo

> Crie conversas em grupo no X Chat com chaves de conversa compartilhadas, títulos criptografados, gerenciamento de membros e mensagens assinadas.

Bate-papos em grupo usam o **mesmo modelo de criptografia** do X Chat 1:1: uma **chave de conversa** compartilhada pelos membros, empacotada para a **chave pública de identidade** de cada membro, com mensagens criptografadas e assinadas pelo Chat XDK. O que muda é a **composição de membros**, **como você cria a conversa** e, muitas vezes, campos de **título/avatar criptografados** na conversa.

Fluxos 1:1 estão em [Primeiros passos](/pt/xchat/getting-started). Detalhes de endpoints estão em **Referência da API → Conversas e mensagens**.

***

## Como os grupos diferem de 1:1

| Tópico           | 1:1                                                             | Grupo                                                                                  |
| :--------------- | :-------------------------------------------------------------- | :------------------------------------------------------------------------------------- |
| Identidade       | Frequentemente endereçado por ID de usuário do par nos caminhos | O ID da conversa normalmente começa com `g`                                            |
| Criar            | Chaves + envio de mensagens para um usuário                     | APIs de criar / inicializar grupo, depois chaves                                       |
| Participantes    | Você + um par                                                   | Muitos usuários; a composição pode mudar                                               |
| Metadados        | Mínimos                                                         | Nome, avatar etc. podem ser **texto cifrado** (descriptografe com a chave da conversa) |
| Rotação de chave | Menos frequente                                                 | Comum quando pessoas entram ou saem                                                    |

A criptografia continua sendo: **Chat XDK** para chaves e payloads; **API do X** para criar o grupo, publicar os empacotamentos de chave dos participantes, enviar mensagens e carregar eventos.

***

## Crie o grupo e estabeleça as chaves

1. Gere o ID do grupo com `POST /2/chat/conversations/group/initialize` — o `data.conversation_id` da resposta é o ID com prefixo `g` que você usará em todos os passos abaixo.
2. Carregue a chave pública de identidade de cada membro e o `public_key_version` (rotas `GET` de chave pública em **Chaves de criptografia**; [`GET /2/users/public_keys`](/x-api/users/get-public-keys-for-multiple-users) busca vários usuários em uma requisição). Verifique cada registro com `verify_key_binding` antes de usá-lo (veja o aviso em [Primeiros passos](/pt/xchat/getting-started#4-set-up-conversation-keys)).
3. Execute **`prepare_group_create`** uma vez, com **todos** os membros (incluindo você), o ID com prefixo `g` e as listas de IDs de membros/administradores. Uma chamada gera a chave da conversa, empacota-a para cada membro e assina a criação com a identidade da sessão de `set_identity` — retorna **duas** assinaturas de ação (a mudança de chave da conversa e a criação do grupo).
4. `POST /2/chat/conversations/group` com os membros/administradores do grupo, `conversation_key_version`, `conversation_participant_keys` (SDK **`encrypted_key`** → API **`encrypted_conversation_key`**) e **ambas** as `action_signatures`. Falhas de validação vêm como mensagens estáveis e legíveis, por exemplo `"Too many members: adding these members would exceed the allowed group size."` ou `"Cannot add all members: one or more of the requested members cannot be added to this conversation."`.
5. Guarde a chave da conversa em **bruto** e a **versão** para criptografia/descriptografia.

`prepare_group_create` assina o `title` e a `avatar_url` que você passa e os incorpora literalmente no evento group-create. O servidor compara isso com sua requisição, então os valores de `group_name` / `group_avatar_url` no corpo do POST devem ser **byte-idênticos** ao que você passou ao SDK — caso contrário, a chamada falha na validação de assinatura.

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

O mapeamento do corpo para chaves de participantes e assinaturas de ação (`message_id`, `encoded_message_event_detail`, `message_event_signature` aninhado) é o mesmo do POST de chaves em [Primeiros passos — chaves de conversa](/pt/xchat/getting-started#4-set-up-conversation-keys).

Quando a composição mudar, chame **`prepare_group_members_change`** com os novos IDs de membros mais a lista atual (membros, administradores, membros pendentes e o título/avatar/TTL atuais, se definidos). Ele rotaciona a chave da conversa e, assim como group-create, retorna **duas** assinaturas de ação — faça POST de tudo isso para **add members** (`POST /2/chat/conversations/{id}/members`). Depois, espere tráfego de **mudança de chave**: trate-o como [rotação de chave em Primeiros passos](/pt/xchat/getting-started#6-receive-and-decrypt) (`extract_conversation_keys` / `decrypt_events` e depois criptografe com a versão mais recente).

Como `prepare_group_members_change` gera uma chave de conversa **nova** empacotada apenas para os membros que você passa, novos membros recebem a nova versão da chave e não conseguem descriptografar mensagens enviadas com versões anteriores. O contrário não é verdadeiro: a rotação nunca revoga o acesso a versões **anteriores** — qualquer pessoa que já possua uma chave antiga ainda pode ler as mensagens criptografadas com ela. Se você suspeitar que uma chave de conversa foi exposta, rotacione com `prepare_conversation_key_change`; isso protege apenas mensagens futuras.

***

## Metadados de grupo criptografados

Alguns campos da conversa (por exemplo o **nome** de exibição ou a **URL do avatar**) podem chegar **criptografados** sob a chave da conversa. Isso **não** é `encrypt_message`; é o par genérico **`encrypt` / `decrypt`** do Chat XDK (string UTF-8 na entrada, texto cifrado em base64 na saída, com a chave da conversa em **bruto**).

Se um determinado campo é armazenado criptografado é decidido pelo cliente que o escreve: `prepare_group_create` assina e envia o título exatamente como você o fornece (a chave da conversa não existe até essa chamada gerá-la, então um título no momento da criação não pode ser criptografado com ela). Ao ler uma conversa cujos campos são texto cifrado, descriptografe-os com `decrypt` e a versão de chave que estava ativa quando o campo foi escrito.

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

Use a versão **atual** da chave da conversa que se aplica a esse metadado. Se as chaves foram rotacionadas, descriptografe com a versão que estava ativa quando o campo foi escrito (ou siga as regras do produto se os metadados forem sempre reescritos na rotação).

***

## Mensagens e eventos

Enviar e receber em um grupo é igual a 1:1 uma vez que você tenha a chave da conversa em bruto:

* **Enviar:** `encrypt_message` → API send-message ([Primeiros passos](/pt/xchat/getting-started#5-send-a-message))
* **Receber:** API de eventos ou [entrega em tempo real](/pt/xchat/real-time-events) → `decrypt_event` / `decrypt_events`
* **Mídia:** [Mídia](/pt/xchat/media) com o ID da conversa de grupo

Sempre criptografe com a versão **mais recente** da chave após uma rotação motivada por mudança de composição.

### Mudanças de chave por membros que saíram

Os eventos de mudança de chave de um grupo são assinados por quem os realizou — muitas vezes o criador ou um administrador. Se esse membro depois **sair do grupo** (ou desativar a conta), os endpoints de chaves públicas param de retornar suas chaves, então o caminho de descriptografia verificado (`decrypt_events` com chaves de assinatura) falha nesses eventos de mudança de chave com `signature missing or no matching signing key`. Os eventos não estão corrompidos; o material de verificação simplesmente não é mais servido.

Grupos de longa duração devem prever isso e recorrer a **`extract_conversation_keys`** para eventos de mudança de chave que não podem ser verificados. Esse caminho ignora a verificação de assinatura e recupera a chave de conversa descriptografando-a com sua chave de identidade. O modelo de segurança se mantém porque:

* Somente material de chave que foi **criptografado para sua chave de identidade** pode ser recuperado — um terceiro não pode injetar uma chave que você consiga ler
* Cada **mensagem** ainda tem sua assinatura verificada contra o próprio remetente, então a autoria da mensagem não é afetada

Mantenha o caminho verificado primeiro: use `decrypt_events` (que também alimenta o cache de chaves do SDK quando `set_cache_keys(true)` está habilitado) e recorra a `extract_conversation_keys` apenas para os eventos de mudança de chave que ele rejeitar.

***

## Checklist

1. Gere o ID com prefixo `g` com `POST /2/chat/conversations/group/initialize`
2. `prepare_group_create` com **todos** os membros; faça POST dos empacotamentos de chave dos participantes e **ambas** as assinaturas de ação para `POST /2/chat/conversations/group`
3. Faça cache da chave em bruto + versão; atualize nos eventos de mudança de chave
4. Em mudanças de composição, `prepare_group_members_change` (duas assinaturas) → `POST /2/chat/conversations/{id}/members`
5. Descriptografe os metadados do grupo com `decrypt` quando os campos forem texto cifrado
6. Envie/receba com os mesmos padrões de 1:1
