# Enviar leads do CRM ou planilha para o GS Engage

> Sincronize leads vindos de formulário, CRM ou planilha criando cada um via API, sem gerar duplicados e respeitando o limite de requisições.

- URL canônica: https://docs.growthstation.app/docs/api/guias/enviar-leads-do-crm
- Idioma: pt-BR
- Última atualização: 2026-09-20T08:21:20.450Z
- Produto: GS Engage
- Mantido por: Produto GS Engage



<PageHero emoji="🔄" title="Enviar leads do CRM ou planilha para o GS Engage" description="Traga cada novo lead da sua origem externa para dentro do GS Engage, prontinho no topo da fila do vendedor." gradient="brand" />

Você já captura leads em algum lugar: um formulário do site, o seu CRM, uma planilha que o time de marketing atualiza. O que você quer é simples: **cada lead novo aparecer automaticamente no GS Engage**, na frente do vendedor certo, sem digitação manual e sem cadastro repetido.

Este guia mostra como fazer isso de duas formas: **com código** (chamando a API) ou **sem código** (usando o Zapier). Se a ideia de "chamar a API" ainda soa técnica, relaxa: um *endpoint* é só um endereço na internet que recebe seus dados. Vamos passar por cada parte.

<Callout type="tip" title="O que você ganha com isso">
  Todo lead criado pela API entra marcado como <TextHighlight>Levantada de Mão</TextHighlight> e aparece no **topo da fila de atividades** do vendedor. Ou seja: o lead quente que acabou de chegar não fica esperando.
</Callout>

🗺️ Como o fluxo funciona [#️-como-o-fluxo-funciona]

Antes de qualquer código, vale entender o caminho que cada lead percorre. A regra de ouro é: **primeiro procure, depois crie** — assim você nunca cadastra a mesma pessoa duas vezes.

<Mermaid
  chart={`graph TD
A["Lead novo na origem<br/>(formulário / CRM / planilha)"] --> B["Busca no GS Engage<br/>GET /api/v1/leads?search="]
B --> C{"Já existe?"}
C -->|"Sim"| D["Atualiza o existente<br/>PATCH /api/v1/leads/{leadId}"]
C -->|"Não"| E["Cria o lead<br/>POST /api/v1/leads"]
E --> F["Lead entra como<br/>Levantada de Mão"]
F --> G["Aparece no topo da<br/>fila do vendedor"]`}
/>

🔑 Antes de começar [#-antes-de-começar]

Você vai precisar de duas coisas.

<FieldInfoGroup>
  <FieldInfo title="Sua chave de API (apiKey)" required>
    Crie na plataforma em <TextHighlight>Configurações → Configurações de API</TextHighlight>. É um texto de cerca de 40 caracteres. Trate como senha.
  </FieldInfo>

  <FieldInfo title="Os campos personalizados já criados" showOptional>
    Se você quer preencher campos além dos padrão (ex.: "Origem da campanha"), eles precisam **já existir** no projeto. Veja o alerta abaixo.
  </FieldInfo>
</FieldInfoGroup>

<Callout type="warning" title="A apiKey vai na URL — cuide dela como senha">
  A autenticação é feita por *query param* na URL, no formato `?apiKey=SUA_CHAVE` (não é um header). Como ela aparece na URL, ela também aparece em logs de servidor, no histórico do navegador e em links compartilhados. Guarde em variável de ambiente e **nunca cole em canais públicos**.
</Callout>

<Callout type="danger" title="Toda chamada é real — não existe ambiente de teste">
  O GS Engage não tem *sandbox* separado. Qualquer `POST` cria um lead de verdade, em produção. Ao experimentar, comece pelas leituras (`GET`) e só rode a criação quando tiver certeza dos dados.
</Callout>

🧭 Passo a passo (com código) [#-passo-a-passo-com-código]

<Steps>
  <Step num={1} title="Valide sua chave">
    Faça uma leitura leve para confirmar que a chave funciona. Um `200` com uma lista (mesmo vazia, `[]`) já confirma tudo.

    ```bash
    curl "https://api.gsengage.com/api/v1/custom-fields?apiKey=SUA_CHAVE"
    ```

    Essa mesma chamada te devolve os campos personalizados do projeto — guarde-a, porque você vai precisar dos identificadores deles mais adiante.
  </Step>

  <Step num={2} title="Procure antes de criar (evite duplicados)">
    Use o filtro `search` para descobrir se o lead já existe. Ele busca por **nome, empresa, e-mail ou telefone**.

    ```bash
    curl "https://api.gsengage.com/api/v1/leads?search=maria@empresa.com&apiKey=SUA_CHAVE"
    ```

    Se a resposta trouxer resultados em `data`, o lead já existe — atualize com `PATCH /api/v1/leads/{leadId}` em vez de criar de novo. Se vier vazio, siga para a criação.
  </Step>

  <Step num={3} title="Crie o lead">
    Envie os dados com `POST`. O único requisito obrigatório é **ao menos um contato** (um e-mail, telefone ou celular).

    ```bash
    curl -X POST "https://api.gsengage.com/api/v1/leads?apiKey=SUA_CHAVE" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Maria Silva",
        "company": "Empresa Exemplo Ltda",
        "role": "Gerente de Compras",
        "emails": [{ "value": "maria@empresa.com", "label": "Trabalho" }],
        "mobiles": [{ "value": "+5511999998888", "label": "WhatsApp" }]
      }'
    ```

    A resposta traz o lead criado com seu identificador. Pronto: ele já está na fila do vendedor como Levantada de Mão.
  </Step>
</Steps>

🧩 Como mapear os campos da sua origem [#-como-mapear-os-campos-da-sua-origem]

Cada lead do seu CRM ou planilha precisa ser "traduzido" para o formato do GS Engage. A parte que mais confunde são os **contatos**: eles não são um texto simples, mas listas de objetos no formato `value` / `label`.

| Campo na sua origem    | Campo no GS Engage | Formato                         |
| ---------------------- | ------------------ | ------------------------------- |
| Nome                   | `name`             | texto                           |
| Empresa                | `company`          | texto                           |
| Cargo                  | `role`             | texto                           |
| E-mail(s)              | `emails`           | lista de `{ "value", "label" }` |
| Telefone(s) fixo(s)    | `phones`           | lista de `{ "value", "label" }` |
| Celular(es) / WhatsApp | `mobiles`          | lista de `{ "value", "label" }` |
| Campos extras          | `customFields`     | ver seção abaixo                |

O `value` é o dado em si (o número, o e-mail). O `label` é um rótulo livre que ajuda o vendedor a saber do que se trata (ex.: `"Trabalho"`, `"Pessoal"`, `"WhatsApp"`). Você pode enviar **vários contatos** em cada lista.

```json
{
  "name": "João Pereira",
  "company": "Comércio ABC",
  "role": "Diretor",
  "emails": [
    { "value": "joao@abc.com", "label": "Trabalho" },
    { "value": "joao.pessoal@gmail.com", "label": "Pessoal" }
  ],
  "phones": [
    { "value": "+551133334444", "label": "Escritório" }
  ],
  "mobiles": [
    { "value": "+5511988887777", "label": "WhatsApp" }
  ]
}
```

<Callout type="warning" title="Lembre-se: pelo menos um contato">
  A criação **exige ao menos um contato** — um item em `emails`, `phones` ou `mobiles`. Um lead sem nenhuma forma de contato é recusado com erro `400`.
</Callout>

Campos personalizados (customFields) [#campos-personalizados-customfields]

Se a sua planilha tem colunas como "Origem da campanha" ou "Faturamento estimado", você as envia dentro de `customFields`.

<Callout type="danger" title="O campo personalizado precisa existir antes no projeto">
  Você **não cria** campos personalizados pela API de leads. Eles precisam já estar cadastrados no projeto. Liste os que existem com `GET /api/v1/custom-fields` e use o identificador de cada um. Enviar um campo que não existe resulta em erro de validação.
</Callout>

```bash
# 1) Descubra os campos que existem e seus identificadores
curl "https://api.gsengage.com/api/v1/custom-fields?apiKey=SUA_CHAVE"
```

O resultado é uma lista (um *array* puro, sem paginação). Use o identificador de cada campo ao montar o `customFields` do seu lead, casando com o valor que veio da sua origem.

⚙️ Enviar vários leads de uma vez (em lote) [#️-enviar-vários-leads-de-uma-vez-em-lote]

Ao importar uma planilha inteira, você vai fazer muitas chamadas seguidas. Aqui entra o **rate limit** (limite de requisições): você pode fazer até **100 escritas por minuto** (`POST`/`PATCH`) em uma janela de 60 segundos. Se passar disso, a API responde `429` com o header `Retry-After` dizendo quantos segundos esperar.

A boa prática é: para cada lead, **buscar antes** (evitar duplicado), **criar** e, se levar um `429`, **esperar e tentar de novo** (backoff).

<Tabs items={['JavaScript', 'Python']}>
  <Tab value="JavaScript">
    ```js
    const BASE = "https://api.gsengage.com/api/v1";
    const apiKey = process.env.GS_ENGAGE_API_KEY; // nunca deixe a chave no código

    const leads = [
      { name: "Maria Silva", company: "Empresa Exemplo", role: "Gerente",
        emails: [{ value: "maria@empresa.com", label: "Trabalho" }] },
      { name: "João Pereira", company: "Comércio ABC", role: "Diretor",
        mobiles: [{ value: "+5511988887777", label: "WhatsApp" }] },
    ];

    async function callWithBackoff(url, options = {}) {
      while (true) {
        const res = await fetch(url, options);
        if (res.status === 429) {
          const wait = Number(res.headers.get("Retry-After") || 5);
          await new Promise((r) => setTimeout(r, wait * 1000));
          continue; // tenta de novo respeitando o Retry-After
        }
        return res;
      }
    }

    async function upsertLead(lead) {
      const email = lead.emails?.[0]?.value ?? lead.mobiles?.[0]?.value;

      // 1) Procura antes de criar
      const search = await callWithBackoff(
        `${BASE}/leads?search=${encodeURIComponent(email)}&apiKey=${apiKey}`
      );
      const found = (await search.json()).data ?? [];
      if (found.length > 0) {
        console.log(`Já existe: ${lead.name} — pulando`);
        return;
      }

      // 2) Cria
      const res = await callWithBackoff(`${BASE}/leads?apiKey=${apiKey}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(lead),
      });
      if (!res.ok) {
        const { error } = await res.json(); // erros vêm dentro de "error"
        console.error(`Falha em ${lead.name}: ${error?.message}`);
        return;
      }
      console.log(`Criado: ${lead.name}`);
    }

    for (const lead of leads) {
      await upsertLead(lead); // sequencial, sem estourar o limite
    }
    ```
  </Tab>

  <Tab value="Python">
    ```python
    import os, time, requests

    BASE = "https://api.gsengage.com/api/v1"
    api_key = os.environ["GS_ENGAGE_API_KEY"]  # nunca deixe a chave no código

    leads = [
        {"name": "Maria Silva", "company": "Empresa Exemplo", "role": "Gerente",
         "emails": [{"value": "maria@empresa.com", "label": "Trabalho"}]},
        {"name": "João Pereira", "company": "Comércio ABC", "role": "Diretor",
         "mobiles": [{"value": "+5511988887777", "label": "WhatsApp"}]},
    ]

    def call_with_backoff(method, url, **kwargs):
        while True:
            res = requests.request(method, url, **kwargs)
            if res.status_code == 429:
                wait = int(res.headers.get("Retry-After", "5"))
                time.sleep(wait)  # respeita o Retry-After e tenta de novo
                continue
            return res

    def upsert_lead(lead):
        email = (lead.get("emails") or lead.get("mobiles"))[0]["value"]

        # 1) Procura antes de criar
        search = call_with_backoff(
            "GET", f"{BASE}/leads", params={"search": email, "apiKey": api_key}
        )
        if search.json().get("data"):
            print(f"Já existe: {lead['name']} — pulando")
            return

        # 2) Cria
        res = call_with_backoff(
            "POST", f"{BASE}/leads",
            params={"apiKey": api_key}, json=lead
        )
        if not res.ok:
            print(f"Falha em {lead['name']}: {res.json().get('message')}")
            return
        print(f"Criado: {lead['name']}")

    for lead in leads:
        upsert_lead(lead)  # sequencial, sem estourar o limite
    ```
  </Tab>
</Tabs>

<DoDont>
  <DoDontItem type="do">
    Busque com 

    `search`

     antes de cada criação para não duplicar leads.
  </DoDontItem>

  <DoDontItem type="do">
    Respeite o 

    `Retry-After`

     quando receber 

    `429`

     — espere e tente de novo.
  </DoDontItem>

  <DoDontItem type="do">
    Guarde a 

    `apiKey`

     em variável de ambiente.
  </DoDontItem>

  <DoDontItem type="dont">
    Não dispare centenas de 

    `POST`

     em paralelo — você estoura o limite de 100/min.
  </DoDontItem>

  <DoDontItem type="dont">
    Não envie 

    `customFields`

     que ainda não existem no projeto.
  </DoDontItem>

  <DoDontItem type="dont">
    Não cole a URL com a chave em chats, tickets ou commits.
  </DoDontItem>
</DoDont>

🧰 Prefere não escrever código? Use o Zapier [#-prefere-não-escrever-código-use-o-zapier]

Se você não quer manter um script rodando, dá para conectar a sua origem (formulário, CRM, planilha) ao GS Engage usando o **Zapier**, sem código. O GS Engage expõe integração nativa com o Zapier — você monta um fluxo visual "quando chegar um lead novo aqui, crie o lead no GS Engage". Ferramentas como **Make** e **n8n** também conseguem consumir a mesma API REST.

<CardGrid cols={2}>
  <CardLink href="/docs/api/no-code/zapier" icon="⚡" title="Integração com Zapier" span>
    Conecte formulários, CRMs e planilhas ao GS Engage sem manter um servidor rodando.
  </CardLink>

  <CardLink href="/docs/api/no-code/make-e-n8n" icon="🔧" title="Make e n8n" span>
    As mesmas chamadas de API em fluxos visuais, com Make ou n8n.
  </CardLink>
</CardGrid>

❓ Perguntas frequentes [#-perguntas-frequentes]

<FAQ>
  <FAQItem question="O que acontece se eu criar o mesmo lead duas vezes?">
    Você fica com dois cadastros separados. Por isso o passo da busca com `search=` é importante: procure por e-mail ou telefone antes de criar e, se já existir, atualize com `PATCH /api/v1/leads/{leadId}` em vez de criar de novo.
  </FAQItem>

  <FAQItem question="Preciso preencher nome e empresa para criar um lead?">
    O único requisito obrigatório é **ao menos um contato** (um item em `emails`, `phones` ou `mobiles`). Nome, empresa e cargo são recomendados para o vendedor, mas o que trava a criação sem contato é o erro `400`.
  </FAQItem>

  <FAQItem question="Como envio celular e WhatsApp?">
    Ambos vão na lista `mobiles`, cada um como `{ "value": "número", "label": "rótulo" }`. Use o `label` para diferenciar (ex.: `"WhatsApp"`, `"Pessoal"`).
  </FAQItem>

  <FAQItem question="Recebi um erro 400. O que faço?">
    O corpo do erro vem dentro de `error`: `error.message` e uma lista `error.errors` com o `field` (o caminho do campo) e a explicação de cada problema, já em português. Leia o `field` para saber qual campo corrigir — geralmente é um contato faltando ou um campo personalizado inexistente.
  </FAQItem>

  <FAQItem question="Onde o lead aparece depois de criado?">
    Todo lead criado via API entra como **Levantada de Mão** e vai para o topo da fila de atividades do vendedor, sinalizando que é um contato quente e recém-chegado.
  </FAQItem>
</FAQ>

Artigos Relacionados [#artigos-relacionados]

<RelatedArticles>
  <RelatedArticle href="/docs/api/guias/adicionar-lead-cadencia" title="Adicionar um lead e iniciar uma cadência" description="Depois de criar, coloque o lead em uma cadência e dispare a prospecção." />

  <RelatedArticle href="/docs/api/no-code/zapier" title="Integração sem código com Zapier" description="Conecte suas origens de leads ao GS Engage sem escrever código." />

  <RelatedArticle href="/docs/api/referencia" title="Referência da API" description="Todos os endpoints, campos e filtros disponíveis." />
</RelatedArticles>
