1. Mobile
Titov
  • Titov
    • Enviar Mensagens
      • Enviar template HSM
      • Enviar mensagem livre
      • Enviar mídia
    • Contatos
      • Listar contatos
      • Listar conversa do contato
      • Criar contato
      • Atualizar contato
      • Excluir contato
    • Mobile
      • Widget de Chat — Guia de Implementação (Web e App Mobile)
      • Cria uma sessão anônima
        POST
      • Histórico da sessão
        GET
      • Envia mensagem de texto
        POST
      • Envia arquivo (imagem, áudio ou documento)
        POST
      • Stream SSE das respostas em tempo real
        GET
      • Continuar a conversa no WhatsApp
        POST
    • Grupos de Contatos
      • Listar grupos de contatos
      • Criar grupo de contatos
      • Atualizar grupo de contatos
      • Excluir grupo de contatos
    • Respostas Rápidas
      • Listar respostas rápidas
      • Criar resposta rápida
      • Atualizar resposta rápida
      • Excluir resposta rápida
    • Template
      • Listar templates aprovados por número
    • Webhooks
      • Mensagens
        • Mensagem recebida de um contato
        • Mensagem enviada pela plataforma
      • Tickets
        • Ticket de atendimento aberto
        • Ticket de atendimento fechado
      • Contatos
        • Contato criado
        • IA pediu atendimento humano
        • Lead qualificado como quente (score ≥ 70)
        • IA converteu o contato
      • Rádio
        • Áudio de ouvinte aprovado (Rádio)
    • Esquemas
      • Error
      • Contact
      • Envelope
      • HistoryMessage
      • ContactInput
      • ContactRef
      • SseMessage
      • Message
      • MessageEvent
      • PhoneTemplates
      • TicketCreatedEvent
      • ContactGroup
      • TicketClosedEvent
      • ContactGroupInput
      • ContactCreatedEvent
      • CannedReply
      • ContactHelpEvent
      • CannedReplyTextInput
      • ContactHotEvent
      • CannedReplyMediaInput
      • ContactConvertedEvent
      • PaginationLinks
      • RadioAudioEvent
      • PaginationMeta
      • CodedError
      • LegacyError
  1. Mobile

Widget de Chat — Guia de Implementação (Web e App Mobile)

API pública do widget de chat da plataforma TITOV. É o contrato usado tanto pelo
widget web (widget.js) quanto por um app mobile nativo (React Native, Flutter,
Swift, Kotlin) que queira embutir o chat.
Gerado a partir do código (routes/widget.php, WebChatController,
WebChatDomainMiddleware, WebChatDeliveryService) em 2026-07-17.

1. Arquitetura: SSE, não WebSocket#

O widget não usa WebSocket. As respostas do atendente e da IA chegam por
SSE (Server-Sent Events): o cliente abre um EventSource em /sse, e o servidor
mantém a conexão por ~30 segundos entregando mensagens de uma fila (Redis); depois
fecha, e o cliente reconecta. O envio de mensagens é HTTP POST comum.
Implicação mobile: EventSource não é nativo em React Native nem Flutter — use
uma biblioteca de SSE (ex.: react-native-sse) ou um cliente HTTP que leia o stream
text/event-stream linha a linha. Em Swift/Kotlin, URLSession/OkHttp lendo o
stream resolve.

2. Pré-requisito: cadastrar o App ID#

Um navegador é autorizado pelo header Origin (ou Referer, para same-origin),
validado contra os domínios permitidos do widget. Um app nativo não envia Origin,
então usa outro mecanismo: o header X-Widget-App-Id.
No painel, em Configurações → AI Functions → Widgets, edite o widget e preencha
"IDs de apps nativos permitidos" com o bundle identifier do app
(ex.: com.suaempresa.app), um por linha. Só apps cadastrados são aceitos.
Sem curinga. Diferente dos domínios, o campo de apps não aceita *. Cada App ID
deve ser declarado explicitamente, e a comparação é exata (in_array estrito) — envie
o valor sem espaços nas bordas.

3. Autenticação#

Dois tokens, ambos públicos no sentido de que ficam no cliente:
Widget token (wgt_...): identifica o widget/organização. Vai no path da URL.
Vem do snippet de instalação no painel.
Session token (UUID): identifica a conversa anônima do visitante. Criado via
POST /session, deve ser persistido no dispositivo.
Em toda requisição a partir de um app nativo, envie:
X-Widget-App-Id: com.suaempresa.app
A resposta pode conter Access-Control-Allow-Origin: * — é o CORS global da plataforma
e é inofensivo para um app nativo (não há navegador impondo same-origin). A autorização
real é o App ID; requisição sem App ID cadastrado recebe 403.

4. Fluxo típico#

1.
GET /config para montar a UI (opcional).
2.
Se não houver session token salvo, POST /session e persista o token.
3.
GET /messages para carregar o histórico.
4.
Abra o EventSource em /sse para receber respostas.
5.
POST /messages (ou /media) para enviar; mostre a mensagem localmente de imediato.

5. Persistência da sessão#

O widget web guarda o session token no localStorage. No mobile, guarde no armazenamento
do app (AsyncStorage, shared_preferences, UserDefaults) para retomar a conversa entre
aberturas.
Trate o 401 como sessão expirada. Se POST /messages ou /media retornar 401,
descarte o token salvo, crie uma nova sessão e reenvie. O GET /messages retorna 410
para sessão expirada (e 401 para token de sessão inválido) — mesmo tratamento.

6. Upload de mídia#

O visitante pode enviar imagem, áudio e documento. Vídeo e SVG não são aceitos.
TipoFormatos aceitosLimite
ImagemJPEG, PNG, WebP, GIF10 MB
ÁudioMP3, MP4/M4A, OGG, WAV, WebM, AAC10 MB
DocumentoPDF, TXT, CSV, DOC, DOCX, XLS, XLSX20 MB
A validação é por conteúdo real do arquivo (mimetypes), não pela extensão. A IA
transcreve áudio e lê imagem e documento automaticamente.
Além do limite por arquivo há uma quota acumulada de 50 MB por sessão por hora e um
limite de 5 uploads por minuto por sessão; ambos retornam 429.
Não defina Content-Type manualmente no upload — deixe o cliente HTTP montar o
multipart/form-data com o boundary correto.

7. Rate limits#

RotaLimiteEscopoConfigurável
POST /session10/hora (padrão)Por IPsettings.rate_limits.sessions_per_hour
POST /messages20/min (padrão)Por sessãosettings.rate_limits.messages_per_minute
POST /media5/min + 50 MB/horaPor sessãoNão

8. Tratamento de erros#

Todas as rotas JSON respondem {"error": "..."} com o status apropriado.
StatusSignificadoAção no app
400Handoff indisponível (WhatsApp não configurado / número não resolvido)Ocultar o botão de handoff.
401Sessão inválida ou expiradaCriar nova sessão e reenviar.
403Widget token inválido/inativo, App ID não cadastrado, ou Origin não permitidaVerificar App ID/domínio no painel.
410Sessão expirada (só no GET /messages)Criar nova sessão.
413Arquivo grande demais (limite do servidor web)Reduzir o arquivo.
422Texto vazio, ou tipo/tamanho de arquivo inválidoMostrar mensagem clara ao usuário.
429Rate limit ou quota excedidaAguardar e repetir.
503Limite de contatos da assinatura atingidoTentar mais tarde.
O GET /sse é a exceção: sessão inválida não retorna erro HTTP. O stream abre
com 200 e entrega um event: error com {"message":"Invalid session"}. Trate esse
evento, não o status.

9. Limitações conhecidas#

Sem push notifications. A conexão SSE é suspensa pelo SO quando o app sai de primeiro
plano. Com o app fechado, o usuário não recebe mensagens — o caso "o atendente responde
depois" não funciona sem push (FCM/APNs), que não faz parte desta integração.
Limite de sessões por IP. Em redes móveis com CGNAT muitos usuários compartilham o
mesmo IP; se necessário, aumente sessions_per_hour no painel do widget.
Sem vídeo no envio do visitante (imagem, áudio e documento apenas).
Histórico limitado a 50 mensagens no GET /messages.

10. Exemplos#

Criar sessão e enviar texto (fetch — universal)#

Enviar mídia (multipart)#

Receber respostas por SSE#

Em Flutter, use um pacote de SSE (ou http lendo o stream) apontando para a mesma URL
e header. O contrato dos eventos é idêntico.
Modificado em 2026-07-17 11:43:01
Página anterior
Excluir contato
Próxima página
Cria uma sessão anônima
Built with