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

# Eventos em tempo real do X Chat

> Receba chat.received, chat.sent e outras atividades criptografadas do X Chat via webhooks ou activity stream, depois descriptografe os payloads com o Chat XDK.

O X entrega **`chat.received`**, **`chat.sent`** e atividades relacionadas do X Chat com **texto cifrado** no payload. Descriptografe com o [Chat XDK](/pt/xchat/xchat-xdk).

| Camada                   | Papel                                                                                                                                |
| :----------------------- | :----------------------------------------------------------------------------------------------------------------------------------- |
| **API de Activity do X** | `GET /2/activity/stream`; `POST` / `GET` / `PUT` / `DELETE` `/2/activity/subscriptions` (veja a segurança OpenAPI por operação)      |
| **Webhooks**             | Rotas opcionais `POST` / `GET` `/2/webhooks` e `PUT` / `DELETE` `/2/webhooks/{webhook_id}` se você terminar em sua própria URL HTTPS |
| **Chat XDK**             | `decrypt_event` / `decrypt_events`, com os armazenamentos de sessão `set_signing_keys` / `set_cache_keys`                            |

Tipos privados de evento do X Chat requerem autorização do usuário monitorado. Anexos de arquivos criptografados do X Chat usam **`media_hash_key`** e o download de mídia do X Chat — não os parâmetros da API de Posts `expansions=attachments.media_keys` / `media.fields=variants`.

***

## Tipos de evento

| Evento                   | Quando                                                |
| :----------------------- | :---------------------------------------------------- |
| `chat.received`          | Usuário inscrito recebe um DM criptografado           |
| `chat.sent`              | Usuário inscrito envia um DM criptografado            |
| `chat.conversation_join` | Usuário inscrito entra em um grupo (quando oferecido) |

***

## 1. Escolha a entrega

**Activity stream (geralmente mais simples para bots):** `GET /2/activity/stream` com um Bearer token de app (opcional `backfill_minutes`, `start_time`, `end_time` conforme OpenAPI). Filtre no cliente por `chat.received` / `chat.sent`.

**Assinaturas de atividade:** gerencie assinaturas duráveis com:

* `POST /2/activity/subscriptions` — criar
* `GET /2/activity/subscriptions` — listar (paginado)
* `PUT /2/activity/subscriptions/{subscription_id}` — atualizar
* `DELETE /2/activity/subscriptions/{subscription_id}` ou `DELETE /2/activity/subscriptions?ids=` — deletar

Os corpos de requisição e os escopos necessários são definidos na operação OpenAPI de cada rota. Criar uma assinatura X Activity API (XAA) requer **autorização em contexto de usuário** (OAuth 2.0 em contexto de usuário com os escopos relevantes, como `dm.read` para eventos de chat) para o usuário cuja atividade você monitora.

**Webhooks:** se você terminar eventos em seu endpoint HTTPS, registre um webhook com `POST /2/webhooks`, passe pelos desafios de CRC e depois crie suas assinaturas de atividade com `POST /2/activity/subscriptions`, referenciando seu `webhook_id` (veja as operações de Webhooks e Activity no OpenAPI). Os XDKs de Python/TypeScript podem expor helpers para webhooks e atividade quando sua versão do SDK os incluir.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from xdk import Client

    # Stream (app token) — exact helper names depend on your XDK version
    stream_client = Client(bearer_token="YOUR_BEARER_TOKEN")
    # for event in stream_client.activity.stream():
    #     handle_payload(event)  # see "Decrypt with the Chat XDK" below

    # Or create a subscription — requires user-context auth for the monitored user
    client = Client(access_token="YOUR_OAUTH2_USER_TOKEN")
    client.activity.create_subscription({
        "event_type": "chat.received",
        "filter": {"user_id": "USER_ID_TO_MONITOR"},
    })
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import { Client } from '@xdevplatform/xdk';

    // Creating a subscription requires user-context auth for the monitored user
    const client = new Client({ accessToken: 'YOUR_OAUTH2_USER_TOKEN' });
    await client.activity.createSubscription({
      event_type: 'chat.received',
      filter: { user_id: 'USER_ID_TO_MONITOR' },
    });
    // Stream: client.activity.stream() when available in your SDK version
    ```
  </Tab>
</Tabs>

Assine também `chat.sent` se você precisar de cópias de saída. Outras linguagens: chame as mesmas rotas HTTPS `/2/activity/*` diretamente (token em contexto de usuário para criar assinaturas, Bearer token de app para o stream).

***

## 2. CRC (apenas webhooks)

Se você usar webhooks, responda aos Challenge-Response Checks (GET `crc_token`) com HMAC-SHA256 do token usando seu consumer secret, no formato JSON esperado pelo seu produto de webhook (normalmente `sha256=<base64>`).

***

## 3. Descriptografe com o Chat XDK

Campos ao vivo: **`payload.encoded_event`**, opcional **`payload.conversation_key_change_event`**. Deduplique entregas por **`event_uuid`**; deduplique mensagens pelo **`message_id`** carregado no evento descriptografado — ele faz parte do conteúdo assinado, enquanto sequence ids são metadados não assinados atribuídos pelo backend.

Os snippets abaixo usam os dois armazenamentos de sessão **opcionais** para o handler mais curto: `set_signing_keys` guarda as chaves públicas dos participantes (buscadas uma vez do [endpoint de chaves públicas](/x-api/chat/get-user-public-keys)) e `set_cache_keys(true)` mantém a chave verificada de cada conversa, de modo que `decrypt_event` só precisa do evento. Quando um payload traz `conversation_key_change_event`, execute-o antes por `decrypt_events`: isso verifica a mudança de chave e, com caching ativado, retém sua chave para a chamada de `decrypt_event`. Prefere nenhum estado na instância? Passe as chaves por chamada — veja a nota no final desta seção.

JavaScript usa tipos de evento em camelCase (`message`); outros bindings usam `"Message"` e campos em snake\_case.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    # chat has keys loaded and set_identity called (see Getting Started)
    chat.set_cache_keys(True)
    chat.set_signing_keys(participant_signing_keys)  # all participants, from the public-key routes

    data = body.get("data") or {}
    if data.get("event_type") in ("chat.received", "chat.sent"):
        p = data.get("payload") or {}
        if p.get("conversation_key_change_event"):
            # Verify the key change and retain its key in the cache
            chat.decrypt_events([p["conversation_key_change_event"]])
        ev = chat.decrypt_event(p["encoded_event"])
        if ev["type"] == "Message":
            print(ev["sender_id"], ev["content"]["text"])
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    // chat has keys loaded and setIdentity called (see Getting Started)
    chat.setCacheKeys(true);
    chat.setSigningKeys(participantSigningKeys); // all participants, from the public-key routes

    const data = body?.data ?? {};
    if (data.event_type === 'chat.received' || data.event_type === 'chat.sent') {
      const p = data.payload ?? {};
      if (p.conversation_key_change_event) {
        // Verify the key change and retain its key in the cache
        chat.decryptEvents([p.conversation_key_change_event]);
      }
      const ev = chat.decryptEvent(p.encoded_event);
      if (ev.type === 'message') {
        console.log(ev.senderId, ev.content.text);
      }
    }
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    // chat has keys loaded and set_identity called (see Getting Started)
    chat.set_cache_keys(true);
    chat.set_signing_keys(participant_signing_keys); // all participants

    if let Some(kc) = key_change.as_deref() {
        // Verify the key change and retain its key in the cache
        let _ = chat.decrypt_events(&[kc], &[]);
    }
    // Decrypt with the cached conversation key; verify against the stored signing keys
    let event = chat.decrypt_event(&encoded_event, &Default::default(), &[])?;
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    // chat has keys loaded and SetIdentity called (see Getting Started)
    chat.SetCacheKeys(true)
    _ = chat.SetSigningKeys(participantSigningKeys) // all participants

    if keyChange != "" {
        // Verify the key change and retain its key in the cache
        _, _ = chat.DecryptEvents([]string{keyChange}, nil)
    }
    // Decrypt with the cached conversation key; verify against the stored signing keys
    event, err := chat.DecryptEvent(encodedEvent, nil, nil)
    if err == nil && event.Type == "Message" {
        fmt.Println(event.AsMessage().Text())
    }
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    // chat has keys loaded and SetIdentity called (see Getting Started)
    chat.SetCacheKeys(true);
    chat.SetSigningKeys(participantSigningKeys); // all participants

    if (!string.IsNullOrEmpty(keyChangeB64))
    {
        // Verify the key change and retain its key in the cache
        chat.DecryptEvents(new[] { keyChangeB64 });
    }
    // Decrypt with the cached conversation key; verify against the stored signing keys
    var evt = chat.DecryptEvent(encodedEvent);
    if (evt.GetProperty("type").GetString() == "Message")
        Console.WriteLine(evt.GetProperty("content").GetProperty("text").GetString());
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    // chat has keys loaded and setIdentity called (see Getting Started)
    chat.setCacheKeys(true);
    chat.setSigningKeys(participantSigningKeys); // all participants

    if (keyChangeB64 != null && !keyChangeB64.isEmpty()) {
        // Verify the key change and retain its key in the cache
        chat.decryptEvents(List.of(keyChangeB64), null);
    }
    // Decrypt with the cached conversation key; verify against the stored signing keys
    JsonNode evt = chat.decryptEvent(encodedEvent, (Map<String, byte[]>) null, null);
    if ("Message".equals(evt.path("type").asText())) {
        System.out.println(evt.path("content").path("text").asText());
    }
    ```
  </Tab>
</Tabs>

Para manter os mapas de chave em suas próprias mãos, `extract_conversation_keys` descriptografa as chaves de `conversation_key_change_event` e `decrypt_event` as aceita (junto com as chaves de assinatura do remetente) como argumentos explícitos — um argumento explícito e não vazio sempre prevalece sobre os armazenamentos.

Histórico: [`GET /2/chat/conversations/{id}/events`](/x-api/chat/get-chat-conversation-events) + **`decrypt_events`** — veja [Primeiros passos](/pt/xchat/getting-started#6-receive-and-decrypt).

***

## Formato do payload (ao vivo)

```json theme={null}
{
  "data": {
    "event_type": "chat.received",
    "event_uuid": "0f52b591-4b7e-4f13-92cd-30e6b2a3f18a",
    "payload": {
      "conversation_id": "1215441834412953600-1843439638876491776",
      "sender_id": "1843439638876491776",
      "encoded_event": "BASE64_ENCODED_MESSAGE_EVENT",
      "conversation_key_version": "1782945126642",
      "conversation_key_change_event": "BASE64_ENCODED_KEY_CHANGE_EVENT"
    }
  }
}
```

***

## Práticas

* Verifique assinaturas de webhook conforme os requisitos da plataforma
* Defina os armazenamentos de sessão uma vez: `set_signing_keys` para todos os participantes, `set_cache_keys(true)` para chaves de conversa
* Aplique blobs de mudança de chave (via `decrypt_events`) antes de descriptografar mensagens dependentes
* Deduplique entregas por `event_uuid` e mensagens pelo `message_id` assinado
