# API de Integração

A API de Integração conecta o seu sistema à Leaper. Com ela você cria e atualiza leads, registra as mensagens de WhatsApp que acontecem fora da Leaper, move leads entre os status do funil e consulta leads, conversas e a configuração do funil.

Para ser avisado quando algo muda na Leaper, sem precisar consultar a API, use os [webhooks](https://leaper.com.br/desenvolvedores/webhooks/).

## Visão geral

| O que você quer fazer | Endpoint |
|---|---|
| Registrar uma mensagem de WhatsApp | [`POST /v1/message`](https://leaper.com.br/desenvolvedores/api/#registrar-mensagem) |
| Criar um lead | [`POST /v1/leads`](https://leaper.com.br/desenvolvedores/api/#criar-lead) |
| Atualizar os dados de um lead | [`PUT /v1/leads/{lead_id}`](https://leaper.com.br/desenvolvedores/api/#atualizar-lead) |
| Mover um lead de status | [`PUT /v1/leads/{lead_id}/status`](https://leaper.com.br/desenvolvedores/api/#mover-lead-de-status) |
| Listar e buscar leads | [`GET /v1/leads`](https://leaper.com.br/desenvolvedores/api/#listar-leads) |
| Consultar um lead | [`GET /v1/leads/{lead_id}`](https://leaper.com.br/desenvolvedores/api/#consultar-lead) |
| Consultar a conversa de um lead | [`GET /v1/leads/{lead_id}/conversation`](https://leaper.com.br/desenvolvedores/api/#consultar-conversa) |
| Consultar os status do funil | [`GET /v1/statuses`](https://leaper.com.br/desenvolvedores/api/#listar-status-do-funil) |
| Consultar a empresa | [`GET /v1/company`](https://leaper.com.br/desenvolvedores/api/#consultar-empresa) |

Todas as rotas usam a URL base `https://api.external.leaper.com.br`. A API recebe e devolve JSON em UTF-8; nas requisições com corpo, envie o cabeçalho `Content-Type: application/json`.

## Autenticação

Toda requisição precisa do cabeçalho `X-API-Key` com a chave da sua empresa:

```text title="Cabeçalho"
X-API-Key: sua-chave-de-api
```

Para obter a chave, fale com o suporte da Leaper. Cada chave dá acesso aos dados de uma empresa.

> [!WARNING]
> A chave dá acesso aos leads e às conversas da empresa. Guarde-a numa variável de ambiente ou num cofre de segredos e use a API só a partir do seu servidor, nunca no navegador ou num aplicativo.

| Resposta | Quando acontece |
|---|---|
| `401` | O cabeçalho não foi enviado ou a chave é inválida |
| `403` | A empresa está desativada |

## Primeira requisição

Para testar a chave, consulte os dados da empresa. Os exemplos leem a chave da variável de ambiente `LEAPER_API_KEY`.

```bash title="cURL"
curl "https://api.external.leaper.com.br/v1/company" \
  -H "X-API-Key: $LEAPER_API_KEY"
```
```javascript title="JavaScript"
const response = await fetch("https://api.external.leaper.com.br/v1/company", {
  headers: {
    "X-API-Key": process.env.LEAPER_API_KEY,
  },
});

const data = await response.json();
```
```php title="PHP"
$ch = curl_init('https://api.external.leaper.com.br/v1/company');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'X-API-Key: ' . getenv('LEAPER_API_KEY'),
    ],
]);

$data = json_decode(curl_exec($ch), true);
```
```python title="Python"
import os

import requests

response = requests.get(
    "https://api.external.leaper.com.br/v1/company",
    headers={"X-API-Key": os.environ["LEAPER_API_KEY"]},
    timeout=30,
)

data = response.json()
```

Com a chave certa, a resposta é `200`:

```json title="Resposta 200"
{
  "id": "6201138c-fc0a-4478-820f-8907907e7f00",
  "name": "Clínica Exemplo",
  "document": "12345678000190",
  "address": "Rua dos Pinheiros, 1000 - São Paulo/SP",
  "phone_number": "551130000000"
}
```

## Conceitos

### O recurso lead

Consultar, criar e atualizar um lead devolvem o mesmo formato, dividido em blocos:

| Bloco | Conteúdo |
|---|---|
| `lead` | Dados do contato, origem, tags, valor e datas |
| `status` | Status atual do lead no funil e o anterior |
| `tracking` | Atribuição: campanha, UTMs, clique, página e dispositivo |
| `form_submissions` | Formulários enviados pelo lead, do mais recente para o mais antigo |

São os mesmos blocos enviados pelos [webhooks](https://leaper.com.br/desenvolvedores/webhooks/), com os nomes dos campos sempre em snake_case. Na listagem de leads, cada item traz `lead` e `status`, e os outros dois só quando você pede com `include`. O formato completo de cada campo está em [Objetos](https://leaper.com.br/desenvolvedores/api/#objetos).

### Status do funil

Cada empresa configura os status do seu funil no painel. Consulte-os em [`GET /v1/statuses`](https://leaper.com.br/desenvolvedores/api/#listar-status-do-funil).

- **Identifique um status pelo `id`.** Ele não muda. O `code` é gerado a partir do nome, muda quando o status é renomeado e pode se repetir entre status da empresa.
- **`END_WON` e `END_LOST` são fixos.** Eles marcam o lead convertido e o lead perdido em todas as empresas.
- Mover um lead de status pela API dispara os eventos de conversão configurados para o status e os webhooks de mudança de status.

### Datas e fuso horário

- As datas das respostas vêm em ISO 8601 com fuso, no horário de Brasília: `2026-09-02T15:41:05-03:00`.
- Os filtros `start_date` e `end_date` aceitam `YYYY-MM-DD` ou `YYYY-MM-DD HH:MM:SS`, no horário de Brasília. Só a data vale para o dia inteiro.
- O `timestamp` das mensagens precisa ter fuso: `2026-09-11T10:30:00-03:00` ou `2026-09-11T13:30:00Z`.

### Paginação

A listagem de leads é paginada com `limit` (de 1 a 200, padrão 50) e `offset` (padrão 0). O total de leads que atendem aos filtros vem em `count`:

```json title="Resposta"
{
  "data": [],
  "count": 120,
  "limit": 50,
  "offset": 0
}
```

Para percorrer todos, some `limit` ao `offset` a cada página até chegar a `count`.

### Valores nulos e campos novos

- Qualquer campo pode vir `null` quando o dado não existe. Isso é normal, não um erro.
- Campos novos podem aparecer nas respostas. Ignore os que o seu sistema não conhece em vez de rejeitar a resposta.

### Erros

Todo erro vem no mesmo formato, com uma mensagem em inglês que diz o que corrigir:

```json title="Resposta"
{ "error": "lead_value must be a non-negative number with a dot as decimal separator, e.g. 1500.50" }
```

| Código | Significado |
|---|---|
| `400` | Corpo ou parâmetro inválido |
| `401` | Cabeçalho `X-API-Key` ausente ou chave inválida |
| `403` | Empresa desativada |
| `404` | O recurso não existe ou é de outra empresa |
| `405` | Método não aceito nessa rota |
| `422` | Pedido válido que não pode ser atendido, como um status de outra empresa |
| `429` | Limite de requisições atingido. Veja [Limite de requisições](https://leaper.com.br/desenvolvedores/guia/#limite-de-requisicoes) |
| `500` | Erro interno. Tente de novo em alguns instantes |

### Limite de requisições

Cada empresa tem um limite de requisições por minuto, contado por chave:

| Rotas | Limite |
|---|---|
| Leitura e escrita em geral | 120 por minuto |
| Relatórios (`/v1/reports/...`) | 20 por minuto |

Toda resposta traz quantas requisições ainda cabem na janela:

| Cabeçalho | Significado |
|---|---|
| `X-RateLimit-Limit` | Requisições permitidas na janela |
| `X-RateLimit-Remaining` | Requisições que ainda cabem |

Ao estourar o limite, a resposta é `429` com o cabeçalho `Retry-After`, em segundos:

```json title="Resposta"
{ "error": "Too many requests" }
```

Espere o tempo de `Retry-After` antes de tentar de novo. Para cargas grandes, use `limit` alto na listagem em vez de muitas chamadas pequenas, e guarde os relatórios por alguns minutos no seu lado em vez de pedir a cada acesso do usuário.

## Casos de uso

### Registrar mensagens de WhatsApp

Se as conversas acontecem num sistema seu, como um chatbot, uma central de atendimento ou outro provedor de WhatsApp, envie cada mensagem para [`POST /v1/message`](https://leaper.com.br/desenvolvedores/api/#registrar-mensagem). A Leaper encontra o lead pelo telefone ou pelo LID do WhatsApp, cria o lead se ele ainda não existir e registra a mensagem na conversa.

```bash title="cURL"
curl -X POST "https://api.external.leaper.com.br/v1/message" \
  -H "X-API-Key: $LEAPER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "5511987654321",
    "name": "Marina Alves",
    "message": "Olá, quero agendar uma avaliação",
    "message_id": "wamid.HBgNNTUxMTk4NzY1NDMyMRUCABIYFjNFQjA",
    "timestamp": "2026-09-11T10:30:00-03:00",
    "ctwa_clid": "ARAkLgdbrocNABYQnQoGHmFy"
  }'
```

- A resposta é `201` quando o lead foi criado e `200` quando ele já existia. Nos dois casos vem o `id` do lead.
- Envie o `message_id` do seu provedor. Com ele, repetir uma chamada que falhou não duplica a mensagem na conversa.
- Informe o telefone com DDI. Só os dígitos contam: `+55 (11) 98765-4321` e `5511987654321` são o mesmo contato.
- Quando a mensagem de um contato cria o lead, os parâmetros de rastreamento (`ctwa_clid`, `fbclid`, `gclid`, `wbraid`, `gbraid` e `utm`) definem a origem. A regra completa está na [referência](https://leaper.com.br/desenvolvedores/api/#registrar-mensagem).

Para registrar uma mensagem enviada pela empresa, envie `"from_me": true`. Se o contato ainda não for lead, ele só é criado quando a opção **Criar lead ao iniciar conversa** estiver ligada nas configurações da empresa. Com a opção desligada, a resposta é `200` com `{ "lead": null }` e nada é registrado.

### Criar leads do seu site ou CRM

Use [`POST /v1/leads`](https://leaper.com.br/desenvolvedores/api/#criar-lead) para cadastrar um lead com origem, dados de contato, valor e tags. O lead entra no primeiro status do funil.

```bash title="cURL"
curl -X POST "https://api.external.leaper.com.br/v1/leads" \
  -H "X-API-Key: $LEAPER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "WEBSITE",
    "client": {
      "name": "Marina Alves",
      "phone": "5511987654321",
      "email": "marina.alves@exemplo.com.br"
    },
    "lead_value": "3200"
  }'
```

> [!NOTE]
> Essa rota sempre cria um lead novo, mesmo que já exista um lead com o mesmo telefone. Para alterar um lead, use [`PUT /v1/leads/{lead_id}`](https://leaper.com.br/desenvolvedores/api/#atualizar-lead).

O `lead_value` aceita número ou texto numérico com ponto como separador decimal e sem separador de milhar, como `1500.50`. Valores como `1.500,00` são recusados, e `1.500` é lido como 1,5.

### Mover um lead no funil

1. Consulte os status do funil em [`GET /v1/statuses`](https://leaper.com.br/desenvolvedores/api/#listar-status-do-funil) e guarde os `id`.
2. Envie o `id` do status de destino para [`PUT /v1/leads/{lead_id}/status`](https://leaper.com.br/desenvolvedores/api/#mover-lead-de-status).

```bash title="cURL"
curl -X PUT "https://api.external.leaper.com.br/v1/leads/b7d4e1a2-8f3c-4a19-9c62-1e5f0a7d3b84/status" \
  -H "X-API-Key: $LEAPER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status_id": "5f3c9a1e-2b7d-4c8a-9e61-0d4b8a7c3f21" }'
```

A resposta traz o status atual, o anterior e o histórico. Enviar o status em que o lead já está não conta como mudança: nada entra no histórico e nenhum evento é disparado.

### Buscar leads

[`GET /v1/leads`](https://leaper.com.br/desenvolvedores/api/#listar-leads) combina busca por texto, status, origem, tag e período. Para trazer os leads que entraram em um status desde uma data:

```bash title="cURL"
curl -G "https://api.external.leaper.com.br/v1/leads" \
  -H "X-API-Key: $LEAPER_API_KEY" \
  --data-urlencode "status_id=5f3c9a1e-2b7d-4c8a-9e61-0d4b8a7c3f21" \
  --data-urlencode "date_field=status_changed_at" \
  --data-urlencode "start_date=2026-09-01" \
  --data-urlencode "limit=100"
```

A listagem traz `lead` e `status` de cada item. Se você também precisa da atribuição e dos formulários, peça com `include`:

```bash title="cURL"
curl -G "https://api.external.leaper.com.br/v1/leads" \
  -H "X-API-Key: $LEAPER_API_KEY" \
  --data-urlencode "include=tracking,form_submissions" \
  --data-urlencode "limit=50"
```

Os blocos são os mesmos de [Consultar lead](https://leaper.com.br/desenvolvedores/api/#consultar-lead). Com `include`, o `limit` vale no máximo 50 — acima disso a resposta é `400`, em vez de devolver menos itens em silêncio e fazer você pular leads ao paginar. Sem `include`, o teto segue sendo 200.

Para acompanhar novos leads e mudanças de status assim que acontecem, prefira os [webhooks](https://leaper.com.br/desenvolvedores/webhooks/) a consultar a listagem de tempos em tempos.

### Montar um painel com os números do funil

Em vez de baixar todos os leads para somar no seu sistema, peça os números já somados. [`GET /v1/reports/summary`](https://leaper.com.br/desenvolvedores/api/#resumo-do-periodo) traz os totais do período:

```bash title="cURL"
curl -G "https://api.external.leaper.com.br/v1/reports/summary" \
  -H "X-API-Key: $LEAPER_API_KEY" \
  --data-urlencode "start_date=2026-09-01" \
  --data-urlencode "end_date=2026-09-30"
```

```json title="Resposta 200"
{
  "period": {
    "start_date": "2026-09-01 00:00:00",
    "end_date": "2026-09-30 23:59:59",
    "date_field": "created_at"
  },
  "leads": 184,
  "converted": 37,
  "lost": 52,
  "converted_value": 148200.5,
  "open_value": 96400
}
```

`converted` são os leads no status `END_WON` e `lost`, os que estão em `END_LOST`. Os valores somam o `lead_value` de cada lead, então só aparecem se o valor estiver preenchido.

Para abrir esses números, use [`GET /v1/reports/leads-by-status`](https://leaper.com.br/desenvolvedores/api/#leads-por-status), que mostra quantos leads estão em cada etapa do funil, e [`GET /v1/reports/leads-by-source`](https://leaper.com.br/desenvolvedores/api/#leads-por-origem), que mostra de onde vieram e quanto cada origem converteu.

Os três aceitam o mesmo período: `start_date`, `end_date` e `date_field`. Sem período, valem os últimos 30 dias; a janela pode ter no máximo 92 dias. Com `date_field=status_changed_at`, o período passa a considerar a última mudança de status, útil para "o que foi fechado neste mês".

## Boas práticas

- **Chame a API do seu servidor.** A chave não pode ficar exposta no navegador ou em aplicativos.
- **Guarde os ids.** Use o `id` do lead e o `id` do status como referência no seu sistema; nomes e códigos podem mudar.
- **Repita só o que falhou por instabilidade.** Em erro `500` ou falha de conexão, tente de novo aguardando alguns segundos entre as tentativas. Em erros `4xx`, corrija o pedido antes de reenviar.
- **Envie `message_id` nas mensagens,** para que repetir uma chamada não duplique a conversa.
- **Prefira os relatórios a somar no seu lado.** Para painéis, as rotas `/v1/reports/...` já devolvem os números do período.
- **Respeite o `Retry-After`.** Em erro `429`, espere o tempo indicado; acompanhe `X-RateLimit-Remaining` para não chegar no limite.
- **Aceite campos novos e valores nulos** nas respostas.
