Caso de uso mais comum da API Syncro: criar leads programaticamente a partir de form externo, sistema legado, importação batch ou integração custom. Esse artigo cobre POST /leads e POST /leads/upsert com exemplos completos.
Pré-requisitos
- API Key gerada. Veja Gerar API Key.
- Pipeline + etapa cadastrados no Syncro.
- Cliente HTTP (cURL, Postman, Node, Python, etc).
Endpoint
POST https://app.syncro.chat/api/v1/leads
Headers obrigatórios
X-API-Key: crm_a1b2c3...
Content-Type: application/json
Accept: application/json
Payload mínimo
Apenas campos obrigatórios:
{
"name": "João Silva",
"pipeline_id": 1,
"stage_id": 5
}
💡 Dica: pra descobrir
pipeline_idestage_id, façaGET /api/v1/pipelinesantes.
Payload completo
Todos os campos suportados:
{
"name": "João Silva",
"phone": "+5511987654321",
"email": "[email protected]",
"company": "Acme Corp",
"value": 5000.50,
"source": "site-form",
"notes": "Veio do formulário de contato",
"tags": ["prospect", "premium", "indicacao"],
"pipeline_id": 1,
"stage_id": 5,
"assigned_to": 3,
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "summer_2026",
"utm_term": "crm-vendas",
"utm_content": "banner-azul",
"fbclid": "fb_click_id_xyz",
"gclid": "google_click_id_abc",
"custom_fields": {
"porte_empresa": "media",
"setor": "Tecnologia",
"receita_anual": 500000
}
}
Validações
| Campo | Regra |
|---|---|
name |
required, max 255 chars |
phone ou email |
pelo menos um obrigatório |
value |
numérico, ≥ 0 |
pipeline_id |
required, deve existir |
stage_id |
required, deve existir e pertencer ao pipeline |
assigned_to |
nullable, deve existir como user do tenant |
tags |
array de strings |
notes |
text, max 1MB |
custom_fields |
object com field_name => value |
Resposta de sucesso
Status 201 Created:
{
"success": true,
"data": {
"id": 1234,
"name": "João Silva",
"phone": "+5511987654321",
"email": "[email protected]",
"company": "Acme Corp",
"value": 5000.50,
"source": "site-form",
"status": "active",
"pipeline_id": 1,
"stage_id": 5,
"assigned_to": 3,
"tags": ["prospect", "premium", "indicacao"],
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "summer_2026",
"created_at": "2026-04-30T14:30:00Z",
"updated_at": "2026-04-30T14:30:00Z"
}
}
Resposta de erro
422 Validation error:
{
"success": false,
"error": "The given data was invalid.",
"errors": {
"name": ["O campo nome é obrigatório."],
"stage_id": ["O stage_id selecionado não existe."]
}
}
Exemplos por linguagem
cURL
curl -X POST "https://app.syncro.chat/api/v1/leads" \
-H "X-API-Key: crm_a1b2c3..." \
-H "Content-Type: application/json" \
-d '{
"name": "João Silva",
"phone": "+5511987654321",
"email": "[email protected]",
"pipeline_id": 1,
"stage_id": 5,
"tags": ["prospect"],
"source": "site-form"
}'
PHP
<?php
$payload = [
'name' => 'João Silva',
'phone' => '+5511987654321',
'email' => '[email protected]',
'pipeline_id' => 1,
'stage_id' => 5,
'tags' => ['prospect'],
'source' => 'site-form',
];
$ch = curl_init('https://app.syncro.chat/api/v1/leads');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => [
'X-API-Key: crm_a1b2c3...',
'Content-Type: application/json',
'Accept: application/json',
],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 201) {
$lead = json_decode($response, true)['data'];
echo "Lead criado: ID {$lead['id']}";
} else {
echo "Erro {$status}: {$response}";
}
Node.js
const axios = require('axios');
async function createLead {
try {
const response = await axios.post(
'https://app.syncro.chat/api/v1/leads',
{
name: 'João Silva',
phone: '+5511987654321',
email: '[email protected]',
pipeline_id: 1,
stage_id: 5,
tags: ['prospect'],
source: 'site-form',
},
{
headers: {
'X-API-Key': 'crm_a1b2c3...',
'Content-Type': 'application/json',
},
}
);
console.log('Lead criado:', response.data.data.id);
} catch (error) {
console.error('Erro:', error.response?.data || error.message);
}
}
createLead;
Python
import requests
def create_lead:
url = 'https://app.syncro.chat/api/v1/leads'
headers = {
'X-API-Key': 'crm_a1b2c3...',
'Content-Type': 'application/json',
}
payload = {
'name': 'João Silva',
'phone': '+5511987654321',
'email': '[email protected]',
'pipeline_id': 1,
'stage_id': 5,
'tags': ['prospect'],
'source': 'site-form',
}
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 201:
lead = response.json['data']
print(f"Lead criado: ID {lead['id']}")
else:
print(f"Erro {response.status_code}: {response.text}")
create_lead
Idempotência — POST /leads/upsert
Se você está chamando de um sistema que pode disparar 2x (form com retry, cron com bug), use upsert ao invés de create pra evitar duplicatas.
POST https://app.syncro.chat/api/v1/leads/upsert
Payload
Mesmo de POST /leads + campo match_by:
{
"name": "João Silva",
"phone": "+5511987654321",
"email": "[email protected]",
"pipeline_id": 1,
"stage_id": 5,
"match_by": "email_or_phone"
}
match_by opções
| Valor | Comportamento |
|---|---|
email |
Match por e-mail apenas |
phone |
Match por telefone apenas |
email_or_phone |
Match por qualquer dos dois (default) |
Resposta
201 Created se criou novo:
{
"success": true,
"created": true,
"data": {... }
}
200 OK se atualizou existente:
{
"success": true,
"created": false,
"data": {... }
}
💡 Dica: idempotência elimina duplicatas mas sobrescreve campos do lead existente. Use com cuidado se cliente foi atualizado manualmente.
Custom fields
Pré-requisito
Crie custom fields em /configuracoes/campos-extras antes. Veja Campos personalizados.
Listar definições disponíveis
curl "https://app.syncro.chat/api/v1/custom-fields" \
-H "X-API-Key: crm_..."
Resposta lista cada custom field com name e field_type.
Preencher na criação
{
"name": "Maria Silva",
"pipeline_id": 1,
"stage_id": 5,
"custom_fields": {
"porte_empresa": "pequena",
"setor": "Saúde",
"receita_anual": 100000,
"data_aniversario": "1990-05-15",
"interessado": true
}
}
Tipos suportados
field_type |
Formato no JSON |
|---|---|
text, textarea |
string |
number, currency |
number |
date |
ISO string "YYYY-MM-DD" |
checkbox |
boolean |
multiselect |
array de strings |
Tags
Tags são criadas automaticamente se não existem. Não precisa cadastrar antes.
{
"tags": ["urgente", "vip", "indicacao-joao"]
}
Veja Tags de contatos.
UTM tracking
Sistema preenche lead.utm_* automaticamente. Aparecem em Relatório UTM.
{
"utm_source": "instagram",
"utm_medium": "social",
"utm_campaign": "lancamento-2026",
"utm_term": "crm-vendas",
"utm_content": "carrossel-3",
"fbclid": "IwAR0xyz",
"gclid": "abc123def456"
}
Atribuição automática
Pra atribuir lead a usuário específico:
{
"assigned_to": 3
}
3 é o ID do user. Pra encontrar IDs, use admin → /configuracoes/usuarios.
Sem assigned_to, lead fica sem responsável (departamento ainda pode aplicar via round-robin se configurado).
Disparar webhook após criação
Se você tem WebhookSubscription ativa com evento lead.created, ele dispara automaticamente após POST /leads retornar 201.
Veja Webhooks de saída.
Boas práticas
1. Use upsert sempre que possível
Idempotência protege contra duplicatas em retries.
2. Sanitize input antes de enviar
Validação ocorre no servidor, mas erros 422 desperdiçam requests. Valide no client primeiro.
3. Tags consistentes
vip ≠ VIP ≠ Vip no Syncro (case-sensitive). Padronize lowercase no seu lado.
4. Phone em E.164
+5511987654321 (com + e DDI). Sistema normaliza, mas formato consistente facilita debug.
5. Logue response
Salve lead.id retornado em seu sistema pra referência cruzada futura.
Cenários práticos
Form externo no site
User preenche form WordPress → plugin/script faz POST.
POST /api/v1/leads
{
"name": "{nome do form}",
"email": "{email do form}",
"phone": "{telefone do form}",
"pipeline_id": 1,
"stage_id": 5,
"source": "wordpress-contato",
"utm_source": "{utm da URL}"
}
Importação batch
Migração de outro CRM. Loop em CSV → POST por linha.
import csv
import requests
with open('clientes.csv') as f:
reader = csv.DictReader(f)
for row in reader:
requests.post(
'https://app.syncro.chat/api/v1/leads/upsert',
headers={'X-API-Key': 'crm_...'},
json={
'name': row['nome'],
'email': row['email'],
'phone': row['telefone'],
'pipeline_id': 1,
'stage_id': 5,
'source': 'migracao-crm-antigo',
'match_by': 'email',
}
)
💡 Dica: respeite rate limit (60 req/min). Adicione
time.sleep(1)entre requests.
Lead Ads custom
Se você usa Lead Ads mas não pelo Syncro nativo, pode receber webhook próprio + chamar /leads/upsert.
Webhook de e-commerce
Cliente compra → webhook do Shopify dispara seu endpoint → você cria lead no Syncro.
Erros comuns
"Phone or email is required"
Sistema exige pelo menos um. Adicione algum.
"Stage_id does not belong to the pipeline_id"
Etapa não pertence ao pipeline informado. Confira GET /pipelines.
"Tag exceeds max length"
Tags têm limite de 50 chars cada. Reduza.
"Custom field 'X' not found"
Custom field X não existe ou está inativo. Crie em /configuracoes/campos-extras ou ative.
"Plan limit reached: max_leads"
Tenant atingiu limite de leads do plano. Faça upgrade ou exclua leads antigos.
Próximos passos
- Pra ver outros endpoints, veja Endpoints disponíveis.
- Pra receber eventos sobre leads, veja Webhooks de saída.