Contatos
Hub persiste a lista de contatos do celular pareado em DB proprio. Vale como backup forense — se o chip e banido, voce ainda tem snapshot (nomes, telefones, fotos, bios). Endpoints exigem admin key.
Origem dos dados
3 caminhos populam a tabela contacts:
messaging-history.set— dispara apenas apos pareamento inicial. Traz a agenda inteira de uma vez.contacts.upsert/contacts.update— em tempo real conforme contatos sao adicionados/editados no celular.handleInbound— toda mensagem recebida enriquece o registro do remetente (phone_numberreal viakey.senderPn,notify, etc).
⚠️ Limitacao do WhatsApp moderno: sync inicial (
messaging-history.set) hoje entrega contatos so como LID puro (sem telefone) por privacidade Meta. Cruzamento LID↔telefone vem das mensagens recebidas. Pra um chip ja pareado, fazerrepaire a unica forma de re-disparar o sync inicial.
GET /v1/chips/:chipId/contacts
Lista contatos com paginacao por cursor.
Query params
| Param | Tipo | Notas |
|---|---|---|
search | string opcional | match por nome/notify/phone_number/jid (case-insensitive) |
limit | int 1-200, default 50 | tamanho da pagina |
cursor | ULID opcional | id do ultimo item da pagina anterior |
Response
{ "data": [ { "id": "01HZTQ...", "chip_id": "...", "jid": "5511999998888@s.whatsapp.net", "lid": "12345678901234@lid", "phone_number": "5511999998888", "name": null, "notify": "Maria", "verified_name": null, "status_text": null, "has_photo": true, "photo_url": "/v1/chips/.../contacts/.../photo", "last_synced_at": "...", "removed_at": null, "created_at": "...", "updated_at": "..." } ], "next_cursor": "01HZTQ...", "total": 137}name (agenda) frequentemente e null — WA so envia em casos especificos (privacidade do operador). Use notify (pushName) como fallback.
photo_url aponta pro endpoint de stream — concatenar com base URL.
GET /v1/chips/:chipId/contacts/:jid
Detalhe de 1 contato. :jid precisa ser url-encoded (@ vira %40).
curl "$HUB/v1/chips/$CHIP_ID/contacts/5511999998888%40s.whatsapp.net" \ -H "Authorization: Bearer $ADMIN_KEY"Mesmo schema do item da lista.
404 contact-not-found se o JID nao existe no DB.
GET /v1/chips/:chipId/contacts/:jid/photo
Stream da foto de perfil. Lazy fetch: se ainda nao cacheou, hub baixa via Baileys, salva no MinIO, e responde.
curl "$HUB/v1/chips/$CHIP_ID/contacts/5511...%40s.whatsapp.net/photo" \ -H "Authorization: Bearer $ADMIN_KEY" \ -o foto.jpgHeaders de resposta:
Content-Type: image/jpegCache-Control: private, max-age=3600Cache local de 1h no cliente HTTP. No MinIO fica indefinidamente (ate proximo update detectado).
Erros
| Status | Code | Quando |
|---|---|---|
| 404 | contact-not-found | JID nao esta na tabela |
| 404 | (proprio body) | contato nao tem foto OU bloqueia visualizacao |
| 503 | chip-offline | chip nao esta connected, nao da pra fetch |
Estrategia pra app consumidora
- Listar contatos via
GET /v1/chips/:chipId/contacts?search=maria - Renderizar lista com
notify(ouname) + telefone - Na tela de detalhe, fazer
<img src="{photo_url}">direto — primeiro hit faz fetch async, subsequentes cacheiam - Pra cross-match com sua base interna: usar
phone_number(quando nao-vazio) oulid