Developers/Documentação da API/API de CRM

API de CRM

CRUD de negócios (deals) do CRM via API.

13/02/202613 min de leitura0 visualizações

Gerencie os negocios (deals) do CRM do Bunto ERP. Este endpoint permite listar, consultar, criar, atualizar e marcar negocios como perdidos, incluindo dados de contatos, pipelines, etapas e itens vinculados, via API.

Base URL

Produção:

https://api-backend.bunto.com.br/v1/crm/

Autenticação

Todas as requisições exigem um Token de API no header Authorization:

Authorization: Bearer bnt_seu_token_aqui

Tokens são gerados em Integrações -> Tokens de API no painel do Bunto ERP. Escopo necessário: crm com a ação correspondente (read, write ou delete).


Endpoints

Listar Negócios

GET /v1/crm/

Escopo necessário: crm: read

Retorna a lista paginada de negócios da empresa. Por padrão, negócios com status perdido e transferido são excluídos da listagem (use include_all=true para incluir todos).

Query Parameters

ParâmetroTipoPadrãoDescrição
paginainteger1Número da página
por_paginainteger25Registros por página (máximo: 100)
buscastring-Busca por título do negócio ou nome do contato
statusstring-Filtrar por status: aberto, ganho, perdido, transferido
pipeline_idinteger-Filtrar por pipeline específico
include_allbooleanfalsetrue para incluir negócios perdidos e transferidos na listagem
ordenarstringcriado_emCampo de ordenação: criado_em, titulo, valor
direcaostringdescDireção da ordenação: asc ou desc

Exemplo com cURL

bash
curl 'https://api-backend.bunto.com.br/v1/crm/?pagina=1&por_pagina=10&status=aberto&pipeline_id=1&ordenar=valor&direcao=desc' \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } # Listar negócios abertos do pipeline principal, ordenados por valor resposta = requests.get( f"{BASE_URL}/crm/", headers=headers, params={ "pagina": 1, "por_pagina": 25, "status": "aberto", "pipeline_id": 1, "ordenar": "valor", "direcao": "desc", }, ) dados = resposta.json() if dados["success"]: negocios = dados["data"]["resultados"] paginacao = dados["data"]["paginacao"] print(f"Total de negócios: {paginacao['total_registros']}") for negocio in negocios: print(f" - {negocio['titulo']} | R$ {negocio['valor']} | {negocio['status']}") else: print(f"Erro: {resposta.status_code}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const params = new URLSearchParams({ pagina: "1", por_pagina: "25", status: "aberto", pipeline_id: "1", ordenar: "valor", direcao: "desc", }); const resposta = await fetch(`${BASE_URL}/crm/?${params}`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const negocios = dados.data.resultados; const paginacao = dados.data.paginacao; console.log(`Total de negócios: ${paginacao.total_registros}`); negocios.forEach((negocio) => { console.log(` - ${negocio.titulo} | R$ ${negocio.valor} | ${negocio.status}`); }); } else { console.error(`Erro: ${resposta.status}`); }

Resposta (200 OK)

json
{ "success": true, "message": "15 registros encontrados", "data": { "resultados": [ { "id": 30, "titulo": "Projeto E-commerce - Loja ABC", "status": "aberto", "valor": "45000.00", "contato_id": 12, "pipeline_id": 1, "etapa_id": 3, "data_previsao_fechamento": "2026-03-15", "criado_em": "2026-02-01T09:00:00-03:00" }, { "id": 28, "titulo": "Consultoria Fiscal - Empresa XYZ", "status": "aberto", "valor": "12500.00", "contato_id": 8, "pipeline_id": 1, "etapa_id": 2, "data_previsao_fechamento": "2026-02-28", "criado_em": "2026-01-25T14:30:00-03:00" }, { "id": 25, "titulo": "Implantação ERP - Distribuidora Sul", "status": "aberto", "valor": "8900.00", "contato_id": 5, "pipeline_id": 1, "etapa_id": 1, "data_previsao_fechamento": null, "criado_em": "2026-01-20T11:15:00-03:00" } ], "paginacao": { "pagina_atual": 1, "total_paginas": 1, "total_registros": 15, "por_pagina": 25, "proxima": null, "anterior": null } } }

Campos da Listagem

CampoTipoDescrição
idintegerIdentificador único do negócio
titulostringTítulo do negócio
statusstringStatus do negócio: aberto, ganho, perdido, transferido
valordecimalValor monetário do negócio
contato_idintegerID do contato vinculado
pipeline_idintegerID do pipeline
etapa_idintegerID da etapa atual no pipeline
data_previsao_fechamentodate / nullData prevista para fechamento do negócio
criado_emdatetimeData e hora de criação

Obter Negócio

GET /v1/crm/{id}/

Escopo necessário: crm: read

Retorna os dados completos de um negócio específico, incluindo nomes de contato, pipeline, etapa e a lista de itens vinculados.

Exemplo com cURL

bash
curl https://api-backend.bunto.com.br/v1/crm/30/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } negocio_id = 30 resposta = requests.get(f"{BASE_URL}/crm/{negocio_id}/", headers=headers) dados = resposta.json() if dados["success"]: negocio = dados["data"] print(f"Negócio: {negocio['titulo']}") print(f"Contato: {negocio['contato_nome']}") print(f"Pipeline: {negocio['pipeline_nome']} > {negocio['etapa_nome']}") print(f"Valor: R$ {negocio['valor']}") if negocio["itens"]: print("Itens:") for item in negocio["itens"]: print(f" - Produto #{item['produto_id']} | Qtd: {item['quantidade']} | R$ {item['valor_unitario']}") else: print(f"Erro: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const negocioId = 30; const resposta = await fetch(`${BASE_URL}/crm/${negocioId}/`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const negocio = dados.data; console.log(`Negócio: ${negocio.titulo}`); console.log(`Contato: ${negocio.contato_nome}`); console.log(`Pipeline: ${negocio.pipeline_nome} > ${negocio.etapa_nome}`); console.log(`Valor: R$ ${negocio.valor}`); if (negocio.itens && negocio.itens.length > 0) { console.log("Itens:"); negocio.itens.forEach((item) => { console.log(` - Produto #${item.produto_id} | Qtd: ${item.quantidade} | R$ ${item.valor_unitario}`); }); } } else { console.error(`Erro: ${JSON.stringify(dados)}`); }

Resposta (200 OK)

json
{ "success": true, "message": "Registro encontrado", "data": { "id": 30, "titulo": "Projeto E-commerce - Loja ABC", "status": "aberto", "valor": "45000.00", "contato_id": 12, "pipeline_id": 1, "etapa_id": 3, "data_previsao_fechamento": "2026-03-15", "criado_em": "2026-02-01T09:00:00-03:00", "descricao": "Implementação de loja virtual completa com integração de pagamentos.", "motivo_perda": "", "data_fechamento": null, "contato_nome": "João Silva", "pipeline_nome": "Vendas B2B", "etapa_nome": "Proposta Enviada", "atualizado_em": "2026-02-10T14:30:00-03:00", "itens": [ { "produto_id": 15, "quantidade": "1.00", "valor_unitario": "35000.00" }, { "produto_id": 22, "quantidade": "2.00", "valor_unitario": "5000.00" } ] } }

Campos do Detalhe (adicionais à listagem)

CampoTipoDescrição
descricaostringDescrição detalhada do negócio
motivo_perdastringMotivo da perda (preenchido quando status = perdido)
data_fechamentodate / nullData efetiva de fechamento do negócio
contato_nomestring / nullNome do contato vinculado
pipeline_nomestring / nullNome do pipeline
etapa_nomestring / nullNome da etapa atual no pipeline
atualizado_emdatetimeData e hora da última atualização
itensarrayLista de itens/produtos do negócio
itens[].produto_idintegerID do produto
itens[].quantidadedecimalQuantidade do item
itens[].valor_unitariodecimalValor unitário do item

Criar Negócio

POST /v1/crm/

Escopo necessário: crm: write

Cria um novo negócio na empresa do token autenticado.

Campos do Request Body

CampoTipoObrigatórioDescrição
titulostringSimTítulo do negócio (máximo 200 caracteres)
contato_idintegerSimID do contato vinculado
pipeline_idintegerSimID do pipeline
etapa_idintegerSimID da etapa inicial no pipeline
descricaostringNãoDescrição detalhada do negócio
valordecimalNãoValor monetário do negócio (padrão: 0)
statusstringNãoStatus inicial: aberto, ganho, perdido, transferido (padrão: aberto)
data_previsao_fechamentodateNãoData prevista para fechamento (formato: YYYY-MM-DD)
motivo_perdastringNãoMotivo da perda (relevante quando status = perdido)

Exemplo com cURL

bash
curl -X POST https://api-backend.bunto.com.br/v1/crm/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "titulo": "Licenciamento ERP - Farmácia Central", "contato_id": 15, "pipeline_id": 1, "etapa_id": 1, "descricao": "Licença anual do ERP com módulos fiscal e estoque.", "valor": 18000.00, "data_previsao_fechamento": "2026-04-01" }'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } novo_negocio = { "titulo": "Licenciamento ERP - Farmácia Central", "contato_id": 15, "pipeline_id": 1, "etapa_id": 1, "descricao": "Licença anual do ERP com módulos fiscal e estoque.", "valor": 18000.00, "data_previsao_fechamento": "2026-04-01", } resposta = requests.post(f"{BASE_URL}/crm/", headers=headers, json=novo_negocio) dados = resposta.json() if dados["success"]: negocio = dados["data"] print(f"Negócio criado com sucesso! ID: {negocio['id']}") print(f"Título: {negocio['titulo']}") print(f"Pipeline: {negocio['pipeline_nome']} > {negocio['etapa_nome']}") else: print(f"Erro ao criar negócio: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const novoNegocio = { titulo: "Licenciamento ERP - Farmácia Central", contato_id: 15, pipeline_id: 1, etapa_id: 1, descricao: "Licença anual do ERP com módulos fiscal e estoque.", valor: 18000.0, data_previsao_fechamento: "2026-04-01", }; const resposta = await fetch(`${BASE_URL}/crm/`, { method: "POST", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(novoNegocio), }); const dados = await resposta.json(); if (dados.success) { console.log(`Negócio criado com sucesso! ID: ${dados.data.id}`); console.log(`Título: ${dados.data.titulo}`); console.log(`Pipeline: ${dados.data.pipeline_nome} > ${dados.data.etapa_nome}`); } else { console.error(`Erro ao criar negócio:`, dados); }

Resposta (201 Created)

json
{ "success": true, "message": "Negócio criado com sucesso", "data": { "id": 31, "titulo": "Licenciamento ERP - Farmácia Central", "status": "aberto", "valor": "18000.00", "contato_id": 15, "pipeline_id": 1, "etapa_id": 1, "data_previsao_fechamento": "2026-04-01", "criado_em": "2026-02-12T15:30:00-03:00", "descricao": "Licença anual do ERP com módulos fiscal e estoque.", "motivo_perda": "", "data_fechamento": null, "contato_nome": "Maria Oliveira", "pipeline_nome": "Vendas B2B", "etapa_nome": "Qualificação", "atualizado_em": "2026-02-12T15:30:00-03:00", "itens": [] } }

Atualizar Negócio

PUT /v1/crm/{id}/
PATCH /v1/crm/{id}/

Escopo necessário: crm: write

Atualiza um negócio existente. Use PUT para atualização completa ou PATCH para atualização parcial (apenas os campos enviados serão alterados).

Exemplo com cURL (PATCH - atualização parcial)

bash
curl -X PATCH https://api-backend.bunto.com.br/v1/crm/31/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "etapa_id": 3, "valor": 22000.00, "status": "ganho" }'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } negocio_id = 31 # Atualização parcial (PATCH) - avançar etapa e atualizar valor atualizacao = { "etapa_id": 3, "valor": 22000.00, "status": "ganho", } resposta = requests.patch( f"{BASE_URL}/crm/{negocio_id}/", headers=headers, json=atualizacao, ) dados = resposta.json() if dados["success"]: negocio = dados["data"] print(f"Negócio atualizado! Status: {negocio['status']}") print(f"Etapa: {negocio['etapa_nome']}") print(f"Valor: R$ {negocio['valor']}") else: print(f"Erro ao atualizar: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const negocioId = 31; const atualizacao = { etapa_id: 3, valor: 22000.0, status: "ganho", }; const resposta = await fetch(`${BASE_URL}/crm/${negocioId}/`, { method: "PATCH", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(atualizacao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Negócio atualizado! Status: ${dados.data.status}`); console.log(`Etapa: ${dados.data.etapa_nome}`); console.log(`Valor: R$ ${dados.data.valor}`); } else { console.error(`Erro ao atualizar:`, dados); }

Resposta (200 OK)

json
{ "success": true, "message": "Negócio atualizado com sucesso", "data": { "id": 31, "titulo": "Licenciamento ERP - Farmácia Central", "status": "ganho", "valor": "22000.00", "contato_id": 15, "pipeline_id": 1, "etapa_id": 3, "data_previsao_fechamento": "2026-04-01", "criado_em": "2026-02-12T15:30:00-03:00", "descricao": "Licença anual do ERP com módulos fiscal e estoque.", "motivo_perda": "", "data_fechamento": "2026-02-12", "contato_nome": "Maria Oliveira", "pipeline_nome": "Vendas B2B", "etapa_nome": "Proposta Enviada", "atualizado_em": "2026-02-12T16:45:00-03:00", "itens": [] } }

Marcar Negócio como Perdido

DELETE /v1/crm/{id}/

Escopo necessário: crm: delete

Marca o negócio como perdido (soft delete). O registro não é removido permanentemente do banco de dados -- o status é alterado para perdido, preservando o histórico.

Exemplo com cURL

bash
curl -X DELETE https://api-backend.bunto.com.br/v1/crm/31/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4'

Exemplo com Python

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} negocio_id = 31 resposta = requests.delete(f"{BASE_URL}/crm/{negocio_id}/", headers=headers) dados = resposta.json() if dados["success"]: print("Negócio marcado como perdido com sucesso!") else: print(f"Erro ao marcar como perdido: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const negocioId = 31; const resposta = await fetch(`${BASE_URL}/crm/${negocioId}/`, { method: "DELETE", headers: { Authorization: `Bearer ${TOKEN}`, }, }); const dados = await resposta.json(); if (dados.success) { console.log("Negócio marcado como perdido com sucesso!"); } else { console.error(`Erro ao marcar como perdido:`, dados); }

Resposta (200 OK)

json
{ "success": true, "message": "Negócio marcado como perdido com sucesso" }

Importante: Diferente de outros endpoints, o DELETE em negócios não desativa o registro -- ele altera o status para perdido. Por padrão, negócios perdidos não aparecem na listagem. Use include_all=true ou status=perdido para visualizá-los. Para registrar o motivo da perda, use PATCH com o campo motivo_perda antes de chamar DELETE.


Erros Comuns

CódigoErroCausaSolução
400VALIDATION_ERRORDados enviados são inválidos (campo obrigatório ausente, formato incorreto, IDs de contato/pipeline/etapa inválidos, etc.)Verifique os campos obrigatórios e os tipos de dados
401Token inválidoToken ausente, expirado, revogado ou mal formatadoVerifique se o header é Authorization: Bearer bnt_xxx
403Token não tem permissãoO token não possui o escopo crm ou a ação necessária (read, write ou delete)Verifique os escopos do token no painel
404Não encontradoNegócio não existe ou pertence a outra empresaConfirme o ID e se o negócio pertence à empresa do token
429Limite de requisições excedidoRate limit ultrapassado para o tipo de operaçãoImplemente retry com backoff exponencial

Exemplo de Resposta de Erro (400)

json
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Erro de validação", "details": { "titulo": ["Este campo é obrigatório."], "contato_id": ["Este campo é obrigatório."], "pipeline_id": ["Este campo é obrigatório."], "etapa_id": ["Este campo é obrigatório."] } } }

Exemplo de Resposta de Erro (403)

json
{ "detail": "Token não tem permissão para este recurso" }

Rate Limiting

A API aplica limites de requisição por token (não por IP). Cada tipo de operação tem um limite diferente.

OperaçãoMétodos HTTPLimite
LeituraGET, HEAD, OPTIONS120 requisições/minuto
EscritaPOST, PUT, PATCH30 requisições/minuto
ExclusãoDELETE10 requisições/minuto

Ao exceder o limite, a API retorna 429 Too Many Requests:

json
{ "detail": "Limite de requisições excedido. Tente novamente em 45 segundos." }

Boas práticas

  • Use por_pagina=100 para reduzir o número de requisições ao listar negócios
  • Implemente retry com backoff exponencial ao receber 429
  • Armazene dados em cache local quando possível
  • Use filtros de pipeline_id e status para limitar o volume de dados retornados
  • Para atualizações em lote, use PATCH apenas com os campos alterados para economizar requisições de escrita

Paginação

Todos os endpoints de listagem retornam dados paginados.

Parâmetros

ParâmetroTipoPadrãoMáximoDescrição
paginainteger1-Número da página
por_paginainteger25100Registros por página

Atenção: Os parâmetros são pagina e por_pagina (em português), não page e per_page.

Objeto paginacao na resposta

CampoTipoDescrição
pagina_atualintegerNúmero da página atual
total_paginasintegerTotal de páginas disponíveis
total_registrosintegerTotal de registros encontrados
por_paginaintegerRegistros por página
proximastring / nullURL da próxima página (null se for a última)
anteriorstring / nullURL da página anterior (null se for a primeira)

Exemplo: percorrer todas as páginas (Python)

python
import requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} todos_os_negocios = [] url = f"{BASE_URL}/crm/?por_pagina=100" while url: resposta = requests.get(url, headers=headers) dados = resposta.json() if not dados["success"]: break todos_os_negocios.extend(dados["data"]["resultados"]) url = dados["data"]["paginacao"]["proxima"] print(f"Total obtido: {len(todos_os_negocios)} negócios")
APICRMCRUDcURLJavaScriptPythonREST
Recursos para IA