API de Marketing
CRUD de conversões offline para Google e Meta Ads via API.
Gerencie as conversões offline do Bunto ERP para integração com Google Ads e Meta Ads. Este endpoint permite listar, consultar, criar, atualizar e excluir registros de conversões offline (leads, oportunidades, vendas e pagamentos) via API.
Base URL
Produção:
https://api-backend.bunto.com.br/v1/marketing/
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: marketing com a ação correspondente (read, write ou delete).
Endpoints
Listar Conversões
GET /v1/marketing/
Escopo necessário: marketing: read
Retorna a lista paginada de conversões offline da empresa.
Query Parameters
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
pagina | integer | 1 | Número da página |
por_pagina | integer | 25 | Registros por página (máximo: 100) |
busca | string | - | Busca por gclid ou fbclid |
tipo | string | - | Filtrar por tipo: lead, oportunidade, venda, pagamento |
status_google | string | - | Filtrar por status de envio ao Google (ex: enviado, pendente, erro) |
data_inicio | date | - | Filtrar conversões a partir desta data (formato: YYYY-MM-DD) |
data_fim | date | - | Filtrar conversões até esta data (formato: YYYY-MM-DD) |
ordenar | string | criado_em | Campo de ordenação: criado_em, valor_receita, tipo |
direcao | string | desc | Direção da ordenação: asc ou desc |
Exemplo com cURL
bashcurl 'https://api-backend.bunto.com.br/v1/marketing/?pagina=1&por_pagina=10&tipo=venda&data_inicio=2026-01-01&data_fim=2026-01-31&ordenar=valor_receita&direcao=desc' \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } # Listar conversões de venda do mês de janeiro resposta = requests.get( f"{BASE_URL}/marketing/", headers=headers, params={ "pagina": 1, "por_pagina": 25, "tipo": "venda", "data_inicio": "2026-01-01", "data_fim": "2026-01-31", "ordenar": "valor_receita", "direcao": "desc", }, ) dados = resposta.json() if dados["success"]: conversoes = dados["data"]["resultados"] paginacao = dados["data"]["paginacao"] print(f"Total de conversões: {paginacao['total_registros']}") for conv in conversoes: google = conv["status_google"] or "N/A" meta = conv["status_meta"] or "N/A" print(f" - [{conv['tipo']}] R$ {conv['valor_receita']} | Google: {google} | Meta: {meta}") else: print(f"Erro: {resposta.status_code}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const params = new URLSearchParams({ pagina: "1", por_pagina: "25", tipo: "venda", data_inicio: "2026-01-01", data_fim: "2026-01-31", ordenar: "valor_receita", direcao: "desc", }); const resposta = await fetch(`${BASE_URL}/marketing/?${params}`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const conversoes = dados.data.resultados; const paginacao = dados.data.paginacao; console.log(`Total de conversões: ${paginacao.total_registros}`); conversoes.forEach((conv) => { const google = conv.status_google || "N/A"; const meta = conv.status_meta || "N/A"; console.log(` - [${conv.tipo}] R$ ${conv.valor_receita} | Google: ${google} | Meta: ${meta}`); }); } else { console.error(`Erro: ${resposta.status}`); }
Resposta (200 OK)
json{ "success": true, "message": "8 registros encontrados", "data": { "resultados": [ { "id": 120, "tipo": "venda", "valor_receita": "4500.00", "valor_lucro": "1350.00", "status_google": "enviado", "status_meta": "enviado", "criado_em": "2026-01-28T16:30:00-03:00" }, { "id": 118, "tipo": "venda", "valor_receita": "2800.00", "valor_lucro": "840.00", "status_google": "enviado", "status_meta": null, "criado_em": "2026-01-25T10:15:00-03:00" }, { "id": 115, "tipo": "venda", "valor_receita": "1200.00", "valor_lucro": "360.00", "status_google": "pendente", "status_meta": "pendente", "criado_em": "2026-01-20T09:00:00-03:00" } ], "paginacao": { "pagina_atual": 1, "total_paginas": 1, "total_registros": 8, "por_pagina": 25, "proxima": null, "anterior": null } } }
Campos da Listagem
| Campo | Tipo | Descrição |
|---|---|---|
id | integer | Identificador único da conversão |
tipo | string | Tipo da conversão: lead, oportunidade, venda, pagamento |
valor_receita | decimal | Valor da receita associada à conversão |
valor_lucro | decimal | Valor do lucro associado à conversão |
status_google | string / null | Status de envio ao Google Ads (ex: enviado, pendente, erro) |
status_meta | string / null | Status de envio ao Meta Ads (ex: enviado, pendente, erro) |
criado_em | datetime | Data e hora de criação do registro |
Obter Conversão
GET /v1/marketing/{id}/
Escopo necessário: marketing: read
Retorna os dados completos de uma conversão específica, incluindo parâmetros UTM, click IDs e datas de envio.
Exemplo com cURL
bashcurl https://api-backend.bunto.com.br/v1/marketing/120/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } conversao_id = 120 resposta = requests.get(f"{BASE_URL}/marketing/{conversao_id}/", headers=headers) dados = resposta.json() if dados["success"]: conv = dados["data"] print(f"Conversão #{conv['id']} [{conv['tipo']}]") print(f"Receita: R$ {conv['valor_receita']}") print(f"Lucro: R$ {conv['valor_lucro']}") print(f"GCLID: {conv['gclid']}") print(f"FBCLID: {conv['fbclid']}") print(f"UTM: {conv['utm_source']} / {conv['utm_medium']} / {conv['utm_campaign']}") print(f"Google: {conv['status_google']} (enviado em {conv['google_enviado_em']})") print(f"Meta: {conv['status_meta']} (enviado em {conv['meta_enviado_em']})") else: print(f"Erro: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const conversaoId = 120; const resposta = await fetch(`${BASE_URL}/marketing/${conversaoId}/`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const conv = dados.data; console.log(`Conversão #${conv.id} [${conv.tipo}]`); console.log(`Receita: R$ ${conv.valor_receita}`); console.log(`Lucro: R$ ${conv.valor_lucro}`); console.log(`GCLID: ${conv.gclid}`); console.log(`FBCLID: ${conv.fbclid}`); console.log(`UTM: ${conv.utm_source} / ${conv.utm_medium} / ${conv.utm_campaign}`); console.log(`Google: ${conv.status_google} (enviado em ${conv.google_enviado_em})`); console.log(`Meta: ${conv.status_meta} (enviado em ${conv.meta_enviado_em})`); } else { console.error(`Erro: ${JSON.stringify(dados)}`); }
Resposta (200 OK)
json{ "success": true, "message": "Registro encontrado", "data": { "id": 120, "tipo": "venda", "valor_receita": "4500.00", "valor_lucro": "1350.00", "status_google": "enviado", "status_meta": "enviado", "criado_em": "2026-01-28T16:30:00-03:00", "gclid": "CjwKCAiA-P-rBhBEEiwAQEXhH8Z2fG7kL9mN3pQ5rS7tU1vW3xY5zA", "fbclid": "IwAR1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4Y5z", "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "campanha_verao_2026", "ocorrido_em": "2026-01-28T15:45:00-03:00", "google_enviado_em": "2026-01-28T16:35:00-03:00", "meta_enviado_em": "2026-01-28T16:35:00-03:00", "atualizado_em": "2026-01-28T16:35:00-03:00" } }
Campos do Detalhe (adicionais à listagem)
| Campo | Tipo | Descrição |
|---|---|---|
gclid | string / null | Google Click ID (identificador de clique do Google Ads) |
fbclid | string / null | Facebook Click ID (identificador de clique do Meta Ads) |
utm_source | string / null | Parâmetro UTM source (origem do tráfego) |
utm_medium | string / null | Parâmetro UTM medium (meio do tráfego) |
utm_campaign | string / null | Parâmetro UTM campaign (nome da campanha) |
ocorrido_em | datetime / null | Data e hora em que a conversão efetivamente ocorreu |
google_enviado_em | datetime / null | Data e hora em que a conversão foi enviada ao Google Ads |
meta_enviado_em | datetime / null | Data e hora em que a conversão foi enviada ao Meta Ads |
atualizado_em | datetime / null | Data e hora da última atualização |
Criar Conversão
POST /v1/marketing/
Escopo necessário: marketing: write
Cria uma nova conversão offline na empresa do token autenticado. Os parâmetros de click ID (gclid, fbclid) e UTM são opcionais, mas pelo menos um deles deve ser informado para que a conversão possa ser sincronizada com Google Ads ou Meta Ads.
Campos do Request Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
tipo | string | Sim | Tipo da conversão: lead, oportunidade, venda, pagamento |
valor_receita | decimal | Não | Valor da receita (padrão: 0) |
valor_lucro | decimal | Não | Valor do lucro |
gclid | string | Não | Google Click ID (máximo 255 caracteres) |
fbclid | string | Não | Facebook Click ID (máximo 255 caracteres) |
utm_source | string | Não | Parâmetro UTM source (máximo 255 caracteres) |
utm_medium | string | Não | Parâmetro UTM medium (máximo 255 caracteres) |
utm_campaign | string | Não | Parâmetro UTM campaign (máximo 255 caracteres) |
ocorrido_em | datetime | Não | Data e hora da conversão (formato ISO 8601; se não informado, usa a data atual) |
Exemplo com cURL
bashcurl -X POST https://api-backend.bunto.com.br/v1/marketing/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "tipo": "venda", "valor_receita": 2500.00, "valor_lucro": 750.00, "gclid": "CjwKCAiA-P-rBhBEEiwAQEXhH_novoClickId123", "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "campanha_carnaval_2026", "ocorrido_em": "2026-02-12T14:00:00-03:00" }'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } nova_conversao = { "tipo": "venda", "valor_receita": 2500.00, "valor_lucro": 750.00, "gclid": "CjwKCAiA-P-rBhBEEiwAQEXhH_novoClickId123", "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "campanha_carnaval_2026", "ocorrido_em": "2026-02-12T14:00:00-03:00", } resposta = requests.post(f"{BASE_URL}/marketing/", headers=headers, json=nova_conversao) dados = resposta.json() if dados["success"]: conv = dados["data"] print(f"Conversão criada com sucesso! ID: {conv['id']}") print(f"Tipo: {conv['tipo']}") print(f"Receita: R$ {conv['valor_receita']}") else: print(f"Erro ao criar conversão: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const novaConversao = { tipo: "venda", valor_receita: 2500.0, valor_lucro: 750.0, gclid: "CjwKCAiA-P-rBhBEEiwAQEXhH_novoClickId123", utm_source: "google", utm_medium: "cpc", utm_campaign: "campanha_carnaval_2026", ocorrido_em: "2026-02-12T14:00:00-03:00", }; const resposta = await fetch(`${BASE_URL}/marketing/`, { method: "POST", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(novaConversao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Conversão criada com sucesso! ID: ${dados.data.id}`); console.log(`Tipo: ${dados.data.tipo}`); console.log(`Receita: R$ ${dados.data.valor_receita}`); } else { console.error(`Erro ao criar conversão:`, dados); }
Resposta (201 Created)
json{ "success": true, "message": "Conversão criada com sucesso", "data": { "id": 125, "tipo": "venda", "valor_receita": "2500.00", "valor_lucro": "750.00", "status_google": null, "status_meta": null, "criado_em": "2026-02-12T15:30:00-03:00", "gclid": "CjwKCAiA-P-rBhBEEiwAQEXhH_novoClickId123", "fbclid": null, "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "campanha_carnaval_2026", "ocorrido_em": "2026-02-12T14:00:00-03:00", "google_enviado_em": null, "meta_enviado_em": null, "atualizado_em": "2026-02-12T15:30:00-03:00" } }
Atualizar Conversão
PUT /v1/marketing/{id}/
PATCH /v1/marketing/{id}/
Escopo necessário: marketing: write
Atualiza uma conversão 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)
bashcurl -X PATCH https://api-backend.bunto.com.br/v1/marketing/125/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "valor_receita": 3200.00, "valor_lucro": 960.00, "fbclid": "IwAR1b2C3d4E5f6G7h8I9j0K_novoFbClickId456" }'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json", } conversao_id = 125 # Atualização parcial (PATCH) - corrigir valores e adicionar fbclid atualizacao = { "valor_receita": 3200.00, "valor_lucro": 960.00, "fbclid": "IwAR1b2C3d4E5f6G7h8I9j0K_novoFbClickId456", } resposta = requests.patch( f"{BASE_URL}/marketing/{conversao_id}/", headers=headers, json=atualizacao, ) dados = resposta.json() if dados["success"]: conv = dados["data"] print(f"Conversão atualizada! Receita: R$ {conv['valor_receita']}") print(f"FBCLID: {conv['fbclid']}") else: print(f"Erro ao atualizar: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const conversaoId = 125; const atualizacao = { valor_receita: 3200.0, valor_lucro: 960.0, fbclid: "IwAR1b2C3d4E5f6G7h8I9j0K_novoFbClickId456", }; const resposta = await fetch(`${BASE_URL}/marketing/${conversaoId}/`, { method: "PATCH", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(atualizacao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Conversão atualizada! Receita: R$ ${dados.data.valor_receita}`); console.log(`FBCLID: ${dados.data.fbclid}`); } else { console.error(`Erro ao atualizar:`, dados); }
Resposta (200 OK)
json{ "success": true, "message": "Conversão atualizada com sucesso", "data": { "id": 125, "tipo": "venda", "valor_receita": "3200.00", "valor_lucro": "960.00", "status_google": null, "status_meta": null, "criado_em": "2026-02-12T15:30:00-03:00", "gclid": "CjwKCAiA-P-rBhBEEiwAQEXhH_novoClickId123", "fbclid": "IwAR1b2C3d4E5f6G7h8I9j0K_novoFbClickId456", "utm_source": "google", "utm_medium": "cpc", "utm_campaign": "campanha_carnaval_2026", "ocorrido_em": "2026-02-12T14:00:00-03:00", "google_enviado_em": null, "meta_enviado_em": null, "atualizado_em": "2026-02-12T16:45:00-03:00" } }
Excluir Conversão (Permanente)
DELETE /v1/marketing/{id}/
Escopo necessário: marketing: delete
Exclui permanentemente a conversão do banco de dados. Esta ação é irreversível -- diferente de outros endpoints, registros de marketing não utilizam soft delete.
Exemplo com cURL
bashcurl -X DELETE https://api-backend.bunto.com.br/v1/marketing/125/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4'
Exemplo com Python
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} conversao_id = 125 resposta = requests.delete(f"{BASE_URL}/marketing/{conversao_id}/", headers=headers) dados = resposta.json() if dados["success"]: print("Conversão excluída permanentemente!") else: print(f"Erro ao excluir: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const conversaoId = 125; const resposta = await fetch(`${BASE_URL}/marketing/${conversaoId}/`, { method: "DELETE", headers: { Authorization: `Bearer ${TOKEN}`, }, }); const dados = await resposta.json(); if (dados.success) { console.log("Conversão excluída permanentemente!"); } else { console.error(`Erro ao excluir:`, dados); }
Resposta (200 OK)
json{ "success": true, "message": "Registro excluído com sucesso" }
Atenção: A exclusão de conversões de marketing é permanente. O registro é removido do banco de dados e não pode ser recuperado. Se a conversão já foi enviada ao Google Ads ou Meta Ads, a exclusão no Bunto não remove a conversão das plataformas de anúncios.
Erros Comuns
| Código | Erro | Causa | Solução |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Dados enviados são inválidos (campo obrigatório ausente, tipo de conversão inválido, formato incorreto, etc.) | Verifique os campos obrigatórios e os tipos de dados |
| 401 | Token inválido | Token ausente, expirado, revogado ou mal formatado | Verifique se o header é Authorization: Bearer bnt_xxx |
| 403 | Token não tem permissão | O token não possui o escopo marketing ou a ação necessária (read, write ou delete) | Verifique os escopos do token no painel |
| 404 | Não encontrado | Conversão não existe ou pertence a outra empresa | Confirme o ID e se a conversão pertence à empresa do token |
| 429 | Limite de requisições excedido | Rate limit ultrapassado para o tipo de operação | Implemente retry com backoff exponencial |
Exemplo de Resposta de Erro (400)
json{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Erro de validação", "details": { "tipo": ["\"custom\" não é um valor válido. Valores aceitos: lead, oportunidade, venda, pagamento."] } } }
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ção | Métodos HTTP | Limite |
|---|---|---|
| Leitura | GET, HEAD, OPTIONS | 120 requisições/minuto |
| Escrita | POST, PUT, PATCH | 30 requisições/minuto |
| Exclusão | DELETE | 10 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=100para reduzir o número de requisições ao listar conversões - Implemente retry com backoff exponencial ao receber
429 - Armazene dados em cache local quando possível
- Use filtros de data (
data_inicioedata_fim) etipopara limitar o volume de dados retornados - Para atualizações em lote, use
PATCHapenas com os campos alterados para economizar requisições de escrita - Envie o
gclidoufbclidno momento da criação para garantir a atribuição correta da conversão
Paginação
Todos os endpoints de listagem retornam dados paginados.
Parâmetros
| Parâmetro | Tipo | Padrão | Máximo | Descrição |
|---|---|---|---|---|
pagina | integer | 1 | - | Número da página |
por_pagina | integer | 25 | 100 | Registros 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
| Campo | Tipo | Descrição |
|---|---|---|
pagina_atual | integer | Número da página atual |
total_paginas | integer | Total de páginas disponíveis |
total_registros | integer | Total de registros encontrados |
por_pagina | integer | Registros por página |
proxima | string / null | URL da próxima página (null se for a última) |
anterior | string / null | URL da página anterior (null se for a primeira) |
Exemplo: percorrer todas as páginas (Python)
pythonimport requests BASE_URL = "https://api-backend.bunto.com.br/v1" TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4" headers = {"Authorization": f"Bearer {TOKEN}"} todas_as_conversoes = [] url = f"{BASE_URL}/marketing/?por_pagina=100" while url: resposta = requests.get(url, headers=headers) dados = resposta.json() if not dados["success"]: break todas_as_conversoes.extend(dados["data"]["resultados"]) url = dados["data"]["paginacao"]["proxima"] print(f"Total obtido: {len(todas_as_conversoes)} conversões")