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

# sendMessageByTicket: Responda em Qualquer Tipo de Canal

> Use o sendMessageByTicket para responder a qualquer conversa aberta — WhatsApp, Instagram, Messenger, Webchat, Mercado Livre ou e-mail — usando apenas o ID do ticket.

O endpoint `sendMessageByTicket` permite que você responda a qualquer conversa usando apenas o ID do ticket, sem precisar saber o canal subjacente ou o identificador do destinatário. Também é a **única forma de enviar mensagens para conversas do Webchat e do Mercado Livre**, que não possuem número de telefone endereçável nem ID externo persistente.

## Visão Geral

Quando você já tem um `ticketId` vindo de um payload de webhook ou de uma chamada de API anterior, não precisa saber o canal subjacente — o endpoint `sendMessageByTicket` resolve o canal automaticamente e roteia a mensagem para a conversa correta.

Esta também é a **única forma de enviar mensagens para conversas do Webchat e do Mercado Livre**, já que esses canais não possuem número de telefone ou identificador persistente que você possa endereçar diretamente.

**URL Base**

```text theme={null}
https://{BaseUrl}/v2/api/external/{ApiID}/
```

**Autenticação**

```text theme={null}
Authorization: Bearer {BearerToken}
```

**Endpoint**

```text theme={null}
POST https://{BaseUrl}/v2/api/external/{ApiID}/sendMessageByTicket
```

***

## Conceitos-Chave

### `reopen`

Se o ticket estiver fechado no momento, você deve definir `reopen: true` para reabri-lo antes que a mensagem possa ser entregue. Enviar para um ticket fechado sem essa flag retorna um erro `409 ERR_TICKET_CLOSED`.

### `isClosed`

Defina `isClosed: true` para fechar automaticamente o ticket imediatamente após o envio da mensagem — útil para enviar uma resposta final e arquivar a conversa na mesma requisição.

### Janela de Idempotência do `externalKey`

Se o mesmo valor de `externalKey` for enviado duas vezes dentro de **120 segundos**, a segunda chamada é tratada como duplicada. Em vez de enviar novamente, a API retorna:

```json theme={null}
{
  "idempotent": true,
  "skipped": true,
  "ticketId": 1262
}
```

Isso protege contra envios duplicados acidentais causados por tentativas repetidas ou timeouts de rede.

### Uma Única Fonte de Mídia por Requisição

Cada requisição pode incluir **no máximo uma** das seguintes opções:

* `media` — um arquivo enviado como `multipart/form-data`
* `mediaUrl` — uma URL de acesso público apontando para o arquivo
* `base64Data` — o conteúdo do arquivo codificado como uma string base64

Fornecer mais de uma na mesma requisição retornará um erro de validação.

### Regras do `mediaUrl`

* Deve usar o esquema `http` ou `https`.
* Redirecionamentos não são seguidos.
* Hosts internos, privados ou loopback são bloqueados.

***

## Enviar uma Mensagem de Texto

Responda a uma conversa aberta com uma mensagem de texto simples.

```json Request theme={null}
{
  "ticketId": 1262,
  "body": "Hello! How can we help you?",
  "externalKey": "ticket-reply-001",
  "reopen": false,
  "isClosed": false
}
```

<ParamField body="ticketId" type="integer" required>
  O ID do ticket (conversa) no qual a mensagem será enviada.
</ParamField>

<ParamField body="body" type="string" required>
  O conteúdo de texto da mensagem.
</ParamField>

<ParamField body="externalKey" type="string">
  Chave de idempotência única gerada pelo seu sistema. A janela de deduplicação é de 120 segundos.
</ParamField>

<ParamField body="reopen" type="boolean" default="false">
  Defina como `true` para reabrir um ticket fechado antes de enviar. Obrigatório se o ticket não estiver atualmente aberto ou pendente.
</ParamField>

<ParamField body="isClosed" type="boolean" default="false">
  Defina como `true` para fechar o ticket imediatamente após o envio da mensagem.
</ParamField>

***

## Enviar um Arquivo via URL

Envie um arquivo de uma URL publicamente hospedada para um ticket existente.

```json Request theme={null}
{
  "ticketId": 1262,
  "mediaUrl": "https://example.com/invoice.pdf",
  "body": "Your invoice is attached.",
  "externalKey": "ticket-reply-002",
  "reopen": false,
  "isClosed": false
}
```

<ParamField body="ticketId" type="integer" required>
  O ID do ticket de destino.
</ParamField>

<ParamField body="mediaUrl" type="string" required>
  URL de acesso público do arquivo. Deve usar `http` ou `https`. Redirecionamentos e hosts privados não são permitidos.
</ParamField>

<ParamField body="body" type="string">
  Legenda opcional para acompanhar o arquivo.
</ParamField>

<ParamField body="externalKey" type="string">
  Chave de idempotência única gerada pelo seu sistema.
</ParamField>

<ParamField body="reopen" type="boolean" default="false">
  Reabre o ticket antes de enviar, caso esteja fechado no momento.
</ParamField>

<ParamField body="isClosed" type="boolean" default="false">
  Fecha o ticket após o envio.
</ParamField>

***

## Enviar um Arquivo em Base64

Envie um arquivo codificado em base64 diretamente no corpo da requisição — sem necessidade de hospedagem externa.

```json Request theme={null}
{
  "ticketId": 1262,
  "body": "See attached image.",
  "base64Data": "iVBORw0KGgoAAAANSUhEUgAAAAUA...",
  "mimeType": "image/png",
  "fileName": "screenshot",
  "externalKey": "ticket-reply-003",
  "reopen": false,
  "isClosed": false
}
```

<ParamField body="ticketId" type="integer" required>
  O ID do ticket de destino.
</ParamField>

<ParamField body="base64Data" type="string" required>
  O conteúdo do arquivo codificado como uma string base64.
</ParamField>

<ParamField body="mimeType" type="string" required>
  Tipo MIME do arquivo — por exemplo, `image/png`, `application/pdf`.
</ParamField>

<ParamField body="fileName" type="string" required>
  Nome do arquivo sem extensão — por exemplo, `screenshot`.
</ParamField>

<ParamField body="body" type="string">
  Legenda opcional para acompanhar o arquivo.
</ParamField>

<ParamField body="externalKey" type="string">
  Chave de idempotência única gerada pelo seu sistema.
</ParamField>

<ParamField body="reopen" type="boolean" default="false">
  Reabre o ticket antes de enviar, caso esteja fechado no momento.
</ParamField>

<ParamField body="isClosed" type="boolean" default="false">
  Fecha o ticket após o envio.
</ParamField>

***

## Enviar um Arquivo via Multipart

Envie um arquivo diretamente do seu sistema usando uma requisição `multipart/form-data` para o mesmo endpoint.

```text theme={null}
POST https://{BaseUrl}/v2/api/external/{ApiID}/sendMessageByTicket
Content-Type: multipart/form-data
```

| Campo do Formulário | Tipo    | Descrição                                             |
| ------------------- | ------- | ----------------------------------------------------- |
| `media`             | arquivo | O arquivo a ser enviado.                              |
| `ticketId`          | integer | O ID do ticket de destino.                            |
| `body`              | string  | Legenda opcional para acompanhar o arquivo.           |
| `externalKey`       | string  | Chave de idempotência única.                          |
| `reopen`            | boolean | Reabre o ticket antes de enviar, caso esteja fechado. |
| `isClosed`          | boolean | Fecha o ticket após o envio.                          |

***

## Resposta de Sucesso

```json theme={null}
{
  "success": true,
  "data": {
    "message": "Message sent successfully",
    "ticketId": 1262,
    "messageId": "wamid.xxxx"
  }
}
```

| Campo            | Tipo    | Descrição                                                                                                                                       |
| ---------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `success`        | boolean | `true` quando a mensagem foi aceita para entrega.                                                                                               |
| `data.message`   | string  | Confirmação legível por humanos.                                                                                                                |
| `data.ticketId`  | integer | O ID da conversa na qual a mensagem foi enviada.                                                                                                |
| `data.messageId` | string  | Identificador único da mensagem no canal. Use-o com `GET /getMessageByMessageId?messageId=wamid.xxxx` para consultar a mensagem posteriormente. |

***

## Obtendo um `ticketId` a partir de Webhooks

A forma mais confiável de obter um `ticketId` é configurar um webhook no seu canal. Quando uma nova mensagem chega, o PanÐá Zap envia (POST) um payload para o seu endpoint que inclui o ID do ticket para todos os tipos de canal — incluindo e-mail.

**Para ativar os webhooks:**

1. Vá para **Administração → Canais**.
2. Selecione o canal e clique em **Editar**.
3. Ative a chave **Webhook**.
4. Defina sua **URL do Webhook**.
5. Ative **Mensagens Recebidas**.

O formato do payload do webhook é:

```json theme={null}
{
  "method": "message",
  "msg": { ... },
  "ticket": {
    "id": 1262,
    ...
  }
}
```

Salve o `ticket.id` de cada webhook recebido para usar com `sendMessageByTicket` quando precisar responder a essa conversa.
