Prospecção (API)
Contato + etiquetas + card no funil numa chamada só — o endpoint pensado para automações de captação (n8n, planilhas, scraping).
Quando usar
POST /v1/prospects substitui a sequência POST /v1/contacts → POST /v1/contacts/{id}/tags → POST /v1/cards que uma automação de prospecção fazia em 2 a 4 chamadas. Numa chamada só ele:
- cria ou completa o contato (upsert por telefone, igual ao
POST /v1/contacts); - aplica etiquetas (por nome ou por id);
- grava a nota, se houver;
- cria o card na etapa pedida — ou decide que não deve criar, e diz por quê.
Use contacts + cards separados quando o fluxo já sabe que o contato existe (ex.: reagir a uma resposta) ou quando você precisa decidir a etapa depois de olhar dados que só a API tem. Use prospects quando o ponto de partida é uma lista externa (raspagem, planilha, CRM de terceiro) e a intenção é sempre a mesma: "cadastre e comece a prospecção".
Este endpoint nunca envia mensagem. Ele só cadastra e move o card — quem aborda o lead é o Maestro (automação), disparado pelo card entrando na etapa. Ver Etiquetas e o Maestro abaixo.
Contrato
curl -X POST https://www.scalacrm.com/api/v1/prospects \
-H "Authorization: Bearer sk_live_…" \
-H "Idempotency-Key: prospeccao-2026-09-16-0042" \
-H "Content-Type: application/json" \
-d '{
"phone": "5551999998888",
"stage_id": "9f8e…",
"name": "Maria Silva",
"tags": ["Lead frio", "Planilha setembro"],
"note": "Encontrada na raspagem do site X",
"check_whatsapp": true
}'
| campo | tipo | obrigatório | teto | observação |
|---|---|---|---|---|
phone | string | sim | 8–30 chars | qualquer formato com DDI+DDD+número |
stage_id | uuid | sim | — | etapa onde o card nasce (GET /v1/pipelines) |
name | string | não | 160 | |
email | string | não | 255 | |
note | string | não | 2000 | vira nota no contato — só na criação; um retry (mesma Idempotency-Key) não duplica |
tags | string[] | não | 20 itens, 40 chars cada | por nome — cria a etiqueta se ela ainda não existir |
tag_ids | uuid[] | não | 20 | por id, se você já sabe qual é (GET /v1/tags) |
source | "import" | "manual" | "ads" | não | — | só na criação, e só quando não há sinal de anúncio (ver abaixo) |
utm | objeto | não | — | utm_source/medium/campaign/term/content, 120–200 chars cada |
custom_fields | objeto | não | — | só preenche chaves já criadas em Configurações → Preferências; chave desconhecida volta em ignored_fields |
title | string | não | 160 | texto do card; padrão é o nome do contato |
value_cents | number | não | 0–1.000.000.000 | valor estimado do card |
check_whatsapp | boolean | não | — | consulta se o número tem WhatsApp antes de decidir o card (ver abaixo) |
tags e tag_ids podem ser usados juntos — o conjunto final é a união dos dois, sem duplicar.
Resposta
{
"ok": true,
"contact_id": "3f9a…",
"created": true,
"card_id": "8b1c…",
"skipped": null,
"whatsapp": "exists"
}
created: falsesignifica que o telefone já existia — os campos vazios foram completados, os preenchidos ficaram como estavam (mesma regra doPOST /v1/contacts).card_idsó vem preenchido quando um card foi criado (skipped: null) ou quando o contato já tinha um card ativo (skipped: "has_active_card") — nos outros dois casos,card_idénull.whatsappsó aparece na resposta quando você mandoucheck_whatsapp: true.
Os 3 motivos de skipped
O contato (e as etiquetas, e a nota) são sempre gravados — o que muda é se o card é criado. skipped conta o primeiro motivo que se aplicar, nesta ordem de prioridade:
skipped | motivo | o que acontece com o card |
|---|---|---|
opt_out | o contato pediu para parar (contact_optouts) ou tem a etiqueta "opt-out" — inclusive uma que esta própria chamada acabou de aplicar | nenhum card é criado |
no_whatsapp | check_whatsapp: true foi enviado e o número não tem WhatsApp | nenhum card é criado |
has_active_card | o contato já tem um card ativo em algum funil | não é movido — mover um card de "Reunião agendada" de volta para "Entrada" seria destrutivo; a resposta devolve o card_id do card existente, sem tocar nele |
null | nenhum dos três | o card é criado normalmente, na etapa pedida |
source não desliga a origem de anúncio
O source escolhe a origem só quando não há sinal de anúncio nenhum no que você mandou. Se o payload trouxer utm, click_ids ou lead_ads, o contato nasce como Anúncio (ads) e o source declarado é ignorado.
Isso é de propósito: é a origem ads que dispara os eventos de conversão para Meta e Google. Um {"click_ids": {"gclid": "…"}, "source": "import"} que nascesse import guardaria o gclid na ficha e nunca o mandaria de volta para o Google — o clique pago viraria um lead sem conversão, e nada na tela acusaria.
Na prática: use source para dizer de onde veio um lead sem rastreio (import para planilha, manual para cadastro a mão). Com rastreio, deixe o rastreio falar.
whatsapp: exists / missing / unknown
Com check_whatsapp: true, a resposta traz um dos três valores:
exists— o número tem WhatsApp confirmado.missing— a consulta rodou e o número não tem WhatsApp.unknown— não deu para consultar (conexão WhatsApp Lite não configurada na conta, falha na consulta, ou a resposta veio sem o número). Isso não é o mesmo que "não tem WhatsApp".
Só marque o número como morto quando vier
missing;unknownsignifica que não deu para consultar. Umunknowntratado como morto apagaria um lead que pode estar perfeitamente vivo — é por isso que sómissingviraskipped: "no_whatsapp";unknownnunca bloqueia a criação do card.
⚠️ Use
check_whatsapppara decidir, nunca para varrer a base. A consulta usa a sessão pareada do seu WhatsApp Lite — é o seu número perguntando ao WhatsApp se cada número existe. Checar listas grandes em alta velocidade é exatamente o comportamento que o WhatsApp associa a spam, e pode levar ao bloqueio do número. Mande a flag quando o resultado vai mudar o que você faz com aquele lead; jamais numa varredura de planilha.
Etiquetas e o Maestro
O gatilho Lead novo cadastrado do Maestro (Automações) é enfileirado no insert do contato — mas as condições da regra (etiqueta, origem, etc.) são avaliadas quando o job roda (a cada minuto), lendo as etiquetas do contato naquele momento, não as que existiam no instante do insert.
Na prática isso significa que etiquetar na mesma chamada funciona: se você manda tags: ["Lead frio"] junto com a criação do contato, e sua regra do Maestro tem a condição "etiqueta = Lead frio", a regra casa — porque quando o job avalia a condição (alguns segundos depois), a etiqueta já está gravada.
Quero saber quando o lead muda de etapa
Duas formas, conforme o que você precisa:
- Webhook de saída
card.stage_changed(Configurações → Integrações → Webhooks): dispara toda vez que um card muda de etapa — inclusive quando o card nasce por este endpoint e é movido depois pelo Maestro ou por um atendente. O payload trazcard_id,contact_id,pipeline_idefrom_stage/to_stage(cada um comidenome). - Passo "Enviar webhook (POST)" do Maestro, no gatilho Lead entrou numa etapa do funil: se você já tem uma regra de automação reagindo à entrada numa etapa específica, acrescente um passo "Enviar webhook (POST)" nela — é a forma mais direta de notificar sua automação SEM assinar um webhook global.
Para reunião marcada, o webhook de saída é appointment.created (mesmo menu de Webhooks).
