WhatsApp Business — Labels, Notas de Chat, Perfil (§13.2)

7.W WhatsApp Business — Labels, Notas de Chat, Perfil (§13.2)#

Recursos de WhatsApp Business nativos + render-safe (validados via /baileys-reference: whatsmeow expõe os builders de app-state de label + GetBusinessProfile). Leitura de catálogo/coleções de produtos foi adicionada via fork patch #11 (port fiel das IQ queries do Baileys — w:biz:catalog). As imagens de produto são proxy-cacheadas (R2), nunca URL da CDN da Meta. send-product/catalog-message e CRUD de produto via API ficam de fora por ora (o fork já tem os métodos de escrita prontos). Mensagens de pedido recebidas já viram evento (order_received/product_received).

Labels (etiquetas do WhatsApp Business)#

Etiquetas são app-state: criar/editar/excluir e associar a chat/mensagem propagam pelos devices da operadora e renderizam no app WhatsApp Business. O servidor ecoa cada mudança de volta como evento label.* (que a Catcher persiste); a API também persiste na hora pra leitura imediata.

Método Rota Corpo / efeito
POST /v1/instances/{id}/labels { "name": "Lead Quente", "color": 3 } → cria etiqueta. label_id é auto-alocado (primeira vaga livre 1..20). color 0..19. Retorna 201 {label_id,name,color}.
GET /v1/instances/{id}/labels Lista etiquetas ativas (inclui as sincronizadas do device). { "labels": [{label_id,name,color}] }.
PATCH /v1/instances/{id}/labels/{labelId} { "name", "color" } → renomeia/recolore. 404 se a etiqueta não existe.
DELETE /v1/instances/{id}/labels/{labelId} Exclui a etiqueta e suas associações (consistente com o WhatsApp). 204.
POST /v1/instances/{id}/chats/{chatId}/labels/{labelId} Aplica a etiqueta no chat. chatId é normalizado (BR). 200 {label_id,chat,associated:true}. 404 se a etiqueta não existe.
DELETE /v1/instances/{id}/chats/{chatId}/labels/{labelId} Remove a etiqueta do chat. 200.
GET /v1/instances/{id}/labels/{labelId}/chats Lista os chats com a etiqueta (DISTINCT, JIDs normalizados). { "label_id", "chats": [...] }.
POST /v1/instances/{id}/messages/{messageId}/labels/{labelId} Aplica a etiqueta numa mensagem. messageId aceita UUID Catcher ou hex WhatsApp (resolvido pro chat+hex). 200.
DELETE /v1/instances/{id}/messages/{messageId}/labels/{labelId} Remove a etiqueta da mensagem. 200.

Limite WhatsApp: 20 etiquetas por conta. POST /labels retorna 400 "label limit reached" quando as 20 vagas estão em uso. A listagem labels/{id}/chats reflete o estado sincronizado com o WhatsApp (eco do app-state, ~1s) — a Catcher também grava na hora pra leitura imediata; os dois convergem (JID normalizado + DISTINCT).

Notas de chat (exclusivo Catcher)#

Notas privadas do operador num chat. Ficam só no banco da Catcher, nunca são enviadas pro WhatsApp (sem caminho whatsmeow, zero risco de ban) — visíveis apenas no Console. Diferencial sobre a Z-API: carregam autor + timestamps.

Método Rota Corpo
POST /v1/instances/{id}/chats/{chatId}/notes { "note": "cliente prefere contato à tarde" }201 {id,chat_jid,note,author_user_id,created_at,updated_at}
GET /v1/instances/{id}/chats/{chatId}/notes { "notes": [...] } (mais recente primeiro)
PATCH /v1/instances/{id}/chats/{chatId}/notes/{noteId} { "note": "..." }200. 404 se não existe.
DELETE /v1/instances/{id}/chats/{chatId}/notes/{noteId} 204.

note máx 8192 chars; author_user_id vem do JWT. Escopo por (instance_id, chat_jid).

Perfil Business (somente leitura)#

Método Rota Descrição
GET /v1/instances/{id}/business/profile?jid= Lê o perfil business: {jid, address, email, categories:[{id,name}], business_hours_timezone, business_hours:[{day_of_week,mode,open_time,close_time}]}. jid vazio = perfil da própria instância; senão, de qualquer número business.

SET/edição do perfil business NÃO existe via multi-device (nem whatsmeow nem Baileys expõem) — é território de Cloud API / app WhatsApp Business. Por isso só há leitura.

Catálogo de produtos (somente leitura)#

Lê o catálogo de produtos e as coleções de qualquer conta WhatsApp Business (ou da própria instância). Implementado via fork patch #11 — port fiel das IQ queries do Baileys no namespace w:biz:catalog.

Método Rota Descrição
GET /v1/instances/{id}/business/catalog?jid=&limit=&cursor= Lê uma página do catálogo. jid vazio = catálogo da própria instância; senão, de qualquer número business. limit (padrão 10) limita a página; cursor (de next_page_cursor) pagina. Retorna {products:[...], next_page_cursor}. Business sem catálogo → 200 {products:[]}.
GET /v1/instances/{id}/business/collections?jid=&limit= Lê as coleções (agrupamentos de produtos) do catálogo. limit (padrão 51). Retorna {collections:[{id,name,status,products:[...]}]}.

Cada produto: {id, name, description, retailer_id, url, price, currency, is_hidden, review_status, image_url}. price é inteiro na menor unidade da moeda (ex.: 4990 = R$ 49,90 quando currency:"BRL").

image_url é sempre URL controlada pela Catcher (R2), nunca da CDN da Meta. A imagem primária de cada produto é baixada pelo worker através do túnel/proxy da instância e cacheada no R2 (best-effort: vazia em falha de cache). O browser do consumidor nunca fala direto com mmg.whatsapp.net/fbcdn.net (lição #31/#35). Validado ao vivo em 2026-05-24 contra um produto real (Catcher 3). Catálogo é SOMENTE LEITURA. Criar/editar/remover produto e send catalog/product message NÃO estão disponíveis. A escrita (product_catalog_add/_edit, type=set) foi implementada e validada até o servidor do WhatsApp, mas o IQ de escrita não retorna resposta que o whatsmeow reconheça — mesmo numa conta commerce habilitada — então foi descontinuada (2026-05-24). Detalhes: lição #51 + .claude/rules/whatsmeow-fork.md patch #11.