Chat com cliente
Central de Atendimento. Chat ancorado em um pedido, com anexos e SLA de resposta.
Pré-requisitos: leia primeiro Comunicação: visão geral. Toda conversa nasce ancorada num pedido (ULID em
order_id) — você não vai encontrar uma forma de abrir um chat "solto", sem pedido por trás. Isso é de propósito: amarrar a conversa ao pedido dá contexto imediato (o que foi comprado, o status, o histórico) para você e para o comprador, sem precisar perguntar "de qual pedido você está falando?".
Conceitos
Participantes:customer (comprador), store (sua loja), platform (atendimento da plataforma).
Tipo:pre_delivery (aberta no pagamento do pedido, encerra na entrega) ou post_delivery (pós-venda aberto manualmente pelo comprador).
Status:open ou closed. Conversas encerradas trazem closed_reason, que pode ser order_delivered, customer_closed, admin_closed ou auto_inactive.
Kind da mensagem:user (humano) ou system (evento automático, ex.: "Pedido marcado como entregue").
Listar conversas
Ordenada por last_message_at desc. Filtros:
| Parâmetro | Para quê |
|---|---|
status | open ou closed. |
type | pre_delivery ou post_delivery. |
subject_id | Slug do assunto pós-venda (ex.: defective_product, wrong_item). |
q | Busca por order_id (ULID do pedido). |
page, per_page | Paginação (default per_page=20). |
Detalhes da conversa
GET/v1/seller/conversations/{conversation}
Não lidas
GET/v1/seller/conversations/unread-count
Soma das mensagens não lidas em todas as conversas abertas. Mensagens enviadas pela própria loja não contam. É a métrica usada nos badges do menu lateral.
Mensagens
GET/v1/seller/conversations/{conversation}/messages
Vêm em ordem cronológica (mais antiga primeiro). Cada mensagem tem kind:
user: escrita por um participante humano (customer,storeouplatform).system: gerada pela plataforma (ex.: "Pedido marcado como entregue").
O bloco sender já vem com display_name, avatar_url, avatar_color e badge.
Polling incremental
Para atualizar a tela sem refazer a paginação, envie after_message_ulid com o ULID da última mensagem recebida. Só voltam as posteriores:
GET /v1/seller/conversations/{id}/messages?after_message_ulid=01K8PBMSG00004VWXYZ12345678Enviar mensagem
POST/v1/seller/conversations/{conversation}/messages
Pode conter body (texto, máx. 5000), attachments (até 5 ULIDs já reservados via intent) ou ambos:
{
"body": "Segue a nota fiscal e a foto da embalagem.",
"attachments": [
"01K8PBATT00001VWXYZ12345678",
"01K8PBATT00002VWXYZ12345678"
]
}Erros comuns:
message_code | Quando |
|---|---|
CONVERSATION_NOT_PARTICIPANT (403) | A loja não participa da conversa. |
CONVERSATION_CLOSED (422) | Conversa encerrada. |
CONVERSATION_ATTACHMENT_NOT_CONFIRMED (422) | Algum ULID de anexo ainda não teve PUT no storage. |
Marcar como lida
POST/v1/seller/conversations/{conversation}/read
Marca como lidas todas as mensagens até (e incluindo) last_message_ulid. Retorna 204.
{
"last_message_ulid": "01K8PBMSG00004VWXYZ12345678"
}Anexos
Envio em 3 etapas: o cliente sobe o arquivo direto no storage (S3/MinIO) por uma URL pré-assinada, e só depois vincula o ULID a uma mensagem. O anexo nasce RESERVED e vira CONFIRMED automaticamente quando a mensagem é criada (o backend faz a checagem de que o arquivo chegou ao storage).
1. Reservar slot
POST/v1/seller/conversations/{conversation}/attachments/intent
Resposta:
{
"data": {
"attachment_ulid": "01K8PBATT00001VWXYZ12345678",
"upload_url": "https://cdn-content.s3.amazonaws.com/.../01K8PBATT00001....jpg?X-Amz-Signature=...",
"method": "PUT",
"expires_at": "2026-05-19T11:47:08-03:00"
}
}2. Subir o arquivo
Faça um PUT {upload_url} com o binário do arquivo.
3. Vincular à mensagem
Chame POST /messages enviando attachments: [attachment_ulid].
Limites
| Limite | Valor |
|---|---|
| Tamanho máximo | 25 MB |
| Validade da URL de upload | 15 minutos |
| Anexos por mensagem | 5 |
| Tipos permitidos | image/jpeg, image/png, image/webp, image/gif, application/pdf, video/mp4, video/webm, video/quicktime |
Erros já na etapa 1:
message_code | Quando |
|---|---|
CONVERSATION_ATTACHMENT_TOO_LARGE (422) | Tamanho acima de 25 MB. |
CONVERSATION_ATTACHMENT_MIME_NOT_ALLOWED (422) | Tipo fora da lista. |
Baixar
GET/v1/seller/conversations/{conversation}/attachments/{attachment}/access
Retorna uma URL temporária assinada pelo storage, válida por 30 minutos:
{
"data": {
"url": "https://cdn-content.s3.amazonaws.com/.../01K8PBATT00001....jpg?X-Amz-Signature=...",
"expires_at": "2026-05-19T12:02:08-03:00"
}
}Faça a requisição direto no
url. Cada chamada gera uma URL nova; as antigas continuam válidas até expirarem.
SLA de resposta
GET/v1/seller/conversations/config
Tempo de resposta esperado (em horas), definido pela plataforma. Use para destacar conversas perto do prazo na UI. A mesma rota devolve também subjects: o catálogo de assuntos de pós-venda (cada um com id, label e applies_to) — é dele que saem os valores aceitos no filtro subject_id da listagem.
{
"data": {
"sla_response_hours": 24,
"subjects": [
{ "id": "defective_product", "label": "Produto com defeito", "applies_to": "post_delivery" },
{ "id": "wrong_item", "label": "Item errado", "applies_to": "post_delivery" }
]
}
}Erros
message_code | HTTP | Quando |
|---|---|---|
UNAUTHORIZED | 401 | Token ausente, inválido ou expirado. |
FORBIDDEN | 403 | Sem permissão. |
NOT_FOUND | 404 | Conversa inexistente. |
VALIDATION_ERROR | 422 | Campos inválidos. |
CONVERSATION_NOT_PARTICIPANT | 403 | Loja não participa da conversa. |
CONVERSATION_CLOSED | 422 | Tentativa de enviar em conversa encerrada. |
CONVERSATION_ATTACHMENT_TOO_LARGE | 422 | Anexo acima de 25 MB. |
CONVERSATION_ATTACHMENT_MIME_NOT_ALLOWED | 422 | Tipo de arquivo fora da lista. |
CONVERSATION_ATTACHMENT_NOT_CONFIRMED | 422 | Anexo não teve PUT no storage. |

