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

GET/v1/seller/conversations

Ordenada por last_message_at desc. Filtros:

ParâmetroPara quê
statusopen ou closed.
typepre_delivery ou post_delivery.
subject_idSlug do assunto pós-venda (ex.: defective_product, wrong_item).
qBusca por order_id (ULID do pedido).
page, per_pagePaginaçã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, store ou platform).
  • 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=01K8PBMSG00004VWXYZ12345678

Enviar 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_codeQuando
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).

sequenceDiagram autonumber participant I as Integrador participant A as API participant S as Storage S3 I->>A: POST /attachments/intent<br/>{ original_name, mime_type, size_bytes } A-->>I: { attachment_ulid, upload_url, expires_at } Note over A: anexo: status=RESERVED I->>S: PUT {upload_url}<br/>(binário do arquivo) S-->>I: 200 OK I->>A: POST /messages<br/>{ body, attachments: [ulid] } Note over A: confirma anexo<br/>(checa que existe no S3)<br/>status=CONFIRMED A-->>I: { id, ... }

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

LimiteValor
Tamanho máximo25 MB
Validade da URL de upload15 minutos
Anexos por mensagem5
Tipos permitidosimage/jpeg, image/png, image/webp, image/gif, application/pdf, video/mp4, video/webm, video/quicktime

Erros já na etapa 1:

message_codeQuando
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_codeHTTPQuando
UNAUTHORIZED401Token ausente, inválido ou expirado.
FORBIDDEN403Sem permissão.
NOT_FOUND404Conversa inexistente.
VALIDATION_ERROR422Campos inválidos.
CONVERSATION_NOT_PARTICIPANT403Loja não participa da conversa.
CONVERSATION_CLOSED422Tentativa de enviar em conversa encerrada.
CONVERSATION_ATTACHMENT_TOO_LARGE422Anexo acima de 25 MB.
CONVERSATION_ATTACHMENT_MIME_NOT_ALLOWED422Tipo de arquivo fora da lista.
CONVERSATION_ATTACHMENT_NOT_CONFIRMED422Anexo não teve PUT no storage.