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

GET /v1/instances/{instanceId}/labels#

Lista etiquetas ativas da instância: { "labels": [{ "label_id": "1", "name": "Lead Quente", "color": 3 }] }.

POST /v1/instances/{instanceId}/labels#

Cria etiqueta. Corpo: { "name": "Lead Quente", "color": 3 }. color deve estar entre 0 e 19; retorna 201 {label_id,name,color}.

PATCH /v1/instances/{instanceId}/labels/{labelId}#

Renomeia/recolore etiqueta. Corpo: { "name": "...", "color": 3 }. Retorna 200; 404 NOT_FOUND se não existir.

DELETE /v1/instances/{instanceId}/labels/{labelId}#

Exclui etiqueta e remove suas associações. Retorna 204.

GET /v1/instances/{instanceId}/labels/{labelId}/chats#

Lista chats associados a etiqueta: { "label_id": "1", "chats": ["554137984905@s.whatsapp.net"] }.

POST /v1/instances/{instanceId}/chats/{chatId}/labels/{labelId}#

Aplica etiqueta ao chat. Retorna 200 { "label_id": "1", "chat": "554137984905@s.whatsapp.net", "associated": true }.

DELETE /v1/instances/{instanceId}/chats/{chatId}/labels/{labelId}#

Remove etiqueta do chat. Retorna 200 { "label_id": "1", "chat": "554137984905@s.whatsapp.net", "associated": false }.

POST /v1/instances/{instanceId}/messages/{messageId}/labels/{labelId}#

Aplica etiqueta a mensagem. messageId aceita UUID Catcher ou hex WhatsApp. Retorna 200 { "label_id": "1", "message_id": "3EB0ABC123", "associated": true }.

DELETE /v1/instances/{instanceId}/messages/{messageId}/labels/{labelId}#

Remove etiqueta de mensagem. messageId aceita UUID Catcher ou hex WhatsApp. Retorna 200 { "label_id": "1", "message_id": "3EB0ABC123", "associated": false }.

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

GET /v1/instances/{instanceId}/chats/{chatId}/notes#

Lista notas privadas do chat: { "notes": [{ "id": 1, "chat_jid": "554137984905@s.whatsapp.net", "note": "cliente prefere contato à tarde", "author_user_id": 7, "created_at": "2026-03-07T10:00:00Z", "updated_at": "2026-03-07T10:00:00Z" }] }.

POST /v1/instances/{instanceId}/chats/{chatId}/notes#

Cria nota privada. Corpo: { "note": "cliente prefere contato à tarde" }. Retorna 201 {id,chat_jid,note,author_user_id,created_at,updated_at}.

PATCH /v1/instances/{instanceId}/chats/{chatId}/notes/{noteId}#

Atualiza nota privada. Corpo: { "note": "..." }. Retorna 200; 404 NOT_FOUND se não existir nesse chat.

DELETE /v1/instances/{instanceId}/chats/{chatId}/notes/{noteId}#

Remove nota privada. Retorna 204; 404 NOT_FOUND se não existir nesse chat.

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.

GET /v1/instances/{instanceId}/business/profile#

Lê perfil WhatsApp Business. Query opcional: jid.

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").

GET /v1/instances/{instanceId}/business/catalog#

Lê página do catálogo. Query params: jid, limit, cursor.

GET /v1/instances/{instanceId}/business/collections#

Lê coleções do catálogo. Query params: jid, limit.

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.