API de Serviços
CRUD completo de serviços via API. Gerencie cadastro de serviços com código municipal e NBS.
Gerencie o cadastro de serviços do Bunto ERP. Este endpoint permite listar, consultar, criar, atualizar e excluir serviços da sua empresa via API.
Base URL
Produção:
https://api-backend.bunto.com.br/v1/servicos/
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: servicos com a ação correspondente (read, write ou delete).
Endpoints
Listar Serviços
GET /v1/servicos/
Escopo necessário: servicos: read
Retorna a lista paginada de serviços 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 descrição ou código |
situacao | string | - | Filtrar por situação: A (ativo), I (inativo) |
ordenar | string | descricao | Campo de ordenação: descricao, codigo, preco |
direcao | string | asc | Direção da ordenação: asc ou desc |
Exemplo com cURL
bashcurl 'https://api-backend.bunto.com.br/v1/servicos/?pagina=1&por_pagina=10&busca=consultoria&ordenar=preco&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 servicos com filtros resposta = requests.get( f"{BASE_URL}/servicos/", headers=headers, params={ "pagina": 1, "por_pagina": 25, "situacao":"A", "ordenar": "descricao", "direcao": "asc", }, ) dados = resposta.json() if dados["success"]: servicos = dados["data"]["resultados"] paginacao = dados["data"]["paginacao"] print(f"Total de servicos: {paginacao['total_registros']}") for servico in servicos: print(f" - [{servico['codigo']}] {servico['descricao']} - R$ {servico['preco']}") 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", situacao: "A", ordenar: "descricao", direcao: "asc", }); const resposta = await fetch(`${BASE_URL}/servicos/?${params}`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const servicos = dados.data.resultados; const paginacao = dados.data.paginacao; console.log(`Total de servicos: ${paginacao.total_registros}`); servicos.forEach((servico) => { console.log(` - [${servico.codigo}] ${servico.descricao} - R$ ${servico.preco}`); }); } else { console.error(`Erro: ${resposta.status}`); }
Resposta (200 OK)
json{ "success": true, "message": "38 registros encontrados", "data": { "resultados": [ { "id": 1, "descricao":"Consultoria em Gestão Empresarial", "codigo":"SERV-001", "preco":"250.00", "unidade": "HR", "situacao":"A", "situacao_display": "Ativo" }, { "id": 2, "descricao":"Manutencao Preventiva de Equipamentos", "codigo":"SERV-002", "preco":"180.00", "unidade": "UN", "situacao":"A", "situacao_display": "Ativo" } ], "paginacao": { "pagina_atual": 1, "total_paginas": 2, "total_registros": 38, "por_pagina": 25, "proxima": "https://api-backend.bunto.com.br/v1/servicos/?pagina=2", "anterior": null } } }
Campos da Listagem
| Campo | Tipo | Descrição |
|---|---|---|
id | integer | Identificador único do serviço |
descricao | string | Descrição do serviço |
codigo | string / null | Código do serviço |
preco | decimal | Preço do serviço |
unidade | string / null | Unidade de medida (HR, UN, etc.) |
situacao | string | Situação: A (ativo), I (inativo) |
situacao_display | string | Descrição legível da situação (ex.: "Ativo", "Inativo") |
Obter Serviço
GET /v1/servicos/{id}/
Escopo necessário: servicos: read
Retorna os dados completos de um serviço específico, incluindo códigos fiscais e informações complementares.
Exemplo com cURL
bashcurl https://api-backend.bunto.com.br/v1/servicos/1/ \ -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", } servico_id = 1 resposta = requests.get(f"{BASE_URL}/servicos/{servico_id}/", headers=headers) dados = resposta.json() if dados["success"]: servico = dados["data"] print(f"Servico: {servico['descricao']}") print(f"Preco: R$ {servico['preco']}") if servico["codigo_servico_municipal"]: print(f"Codigo Municipal: {servico['codigo_servico_municipal']}") else: print(f"Erro: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const servicoId = 1; const resposta = await fetch(`${BASE_URL}/servicos/${servicoId}/`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const servico = dados.data; console.log(`Servico: ${servico.descricao}`); console.log(`Preco: R$ ${servico.preco}`); if (servico.codigo_servico_municipal) { console.log(`Codigo Municipal: ${servico.codigo_servico_municipal}`); } } else { console.error(`Erro: ${JSON.stringify(dados)}`); }
Resposta (200 OK)
json{ "success": true, "message": "Registro encontrado", "data": { "id": 1, "descricao":"Consultoria em Gestão Empresarial", "codigo":"SERV-001", "preco":"250.00", "unidade": "HR", "situacao":"A", "situacao_display": "Ativo", "codigo_servico_municipal": "17.01", "nbs_codigo": "1.1301", "descricao_complementar": "Servico de consultoria especializada em processos de gestão, planejamento estratégico e otimização de recursos empresariais.", "observacoes":"Valor por hora. Minimo de 4 horas por contratação.", "criado_em": "2026-01-10T09:00:00-03:00", "atualizado_em": "2026-02-08T11:30:00-03:00" } }
Campos do Detalhe (adicionais à listagem)
| Campo | Tipo | Descrição |
|---|---|---|
codigo_servico_municipal | string / null | Código do serviço na lista municipal (LC 116/2003) |
nbs_codigo | string / null | Código NBS (Nomenclatura Brasileira de Serviços) |
descricao_complementar | string / null | Descrição complementar detalhada do serviço |
observacoes | string / null | Observações internas |
criado_em | datetime | Data e hora de criação |
atualizado_em | datetime | Data e hora da última atualização |
Criar Serviço
POST /v1/servicos/
Escopo necessário: servicos: write
Cria um novo serviço na empresa do token autenticado.
Importante: O par (empresa, codigo) é único. Dois serviços da mesma empresa não podem ter o mesmo codigo. Se você tentar criar um serviço com um código já existente, a API retornará erro de validação.
Campos do Request Body
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
descricao | string | Sim | Descrição do serviço (máximo 255 caracteres) |
codigo | string | Não | Código do serviço (máximo 50 caracteres). Único por empresa |
preco | decimal | Não | Preço do serviço (padrão: 0) |
unidade | string | Não | Unidade de medida (máximo 20 caracteres) |
situacao | string | Não | Situação: A (ativo, padrão), I (inativo) |
codigo_servico_municipal | string | Não | Código do serviço municipal (máximo 40 caracteres) |
nbs_codigo | string | Não | Código NBS (máximo 20 caracteres) |
descricao_complementar | string | Não | Descrição complementar detalhada |
observacoes | string | Não | Observações internas |
Exemplo com cURL
bashcurl -X POST https://api-backend.bunto.com.br/v1/servicos/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "descricao":"Desenvolvimento de Software Sob Demanda", "codigo":"SERV-039", "preco":180.00, "unidade": "HR", "situacao":"A", "codigo_servico_municipal": "01.01", "nbs_codigo": "1.1201", "descricao_complementar": "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes e implantação.", "observacoes":"Valor por hora técnica. Projetos acima de 200 horas possuem desconto progressivo." }'
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", } novo_servico = { "descricao":"Desenvolvimento de Software Sob Demanda", "codigo":"SERV-039", "preco":180.00, "unidade": "HR", "situacao":"A", "codigo_servico_municipal": "01.01", "nbs_codigo": "1.1201", "descricao_complementar": "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes e implantação.", "observacoes":"Valor por hora técnica. Projetos acima de 200 horas possuem desconto progressivo.", } resposta = requests.post(f"{BASE_URL}/servicos/", headers=headers, json=novo_servico) dados = resposta.json() if dados["success"]: servico = dados["data"] print(f"Servico criado com sucesso! ID: {servico['id']}") print(f"Descricao: {servico['descricao']}") else: print(f"Erro ao criar servico: {dados}")
Exemplo com JavaScript
javascriptconst BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const novoServico = { descricao: "Desenvolvimento de Software Sob Demanda", codigo: "SERV-039", preco: 180.0, unidade: "HR", situacao: "A", codigo_servico_municipal: "01.01", nbs_codigo: "1.1201", descricao_complementar: "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes e implantação.", observacoes: "Valor por hora técnica. Projetos acima de 200 horas possuem desconto progressivo.", }; const resposta = await fetch(`${BASE_URL}/servicos/`, { method: "POST", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(novoServico), }); const dados = await resposta.json(); if (dados.success) { console.log(`Servico criado com sucesso! ID: ${dados.data.id}`); console.log(`Descricao: ${dados.data.descricao}`); } else { console.error(`Erro ao criar servico:`, dados); }
Resposta (201 Created)
json{ "success": true, "message": "Servico criado com sucesso", "data": { "id": 39, "descricao":"Desenvolvimento de Software Sob Demanda", "codigo":"SERV-039", "preco":"180.00", "unidade": "HR", "situacao":"A", "situacao_display": "Ativo", "codigo_servico_municipal": "01.01", "nbs_codigo": "1.1201", "descricao_complementar": "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes e implantação.", "observacoes":"Valor por hora técnica. Projetos acima de 200 horas possuem desconto progressivo.", "criado_em": "2026-02-12T15:30:00-03:00", "atualizado_em": "2026-02-12T15:30:00-03:00" } }
Atualizar Serviço
PUT /v1/servicos/{id}/
PATCH /v1/servicos/{id}/
Escopo necessário: servicos: write
Atualiza um serviç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/servicos/39/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "preco":200.00, "descricao_complementar": "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes, implantação e suporte pós-entrega por 30 dias." }'
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", } servico_id = 39 # Atualizacao parcial (PATCH) - apenas os campos que mudaram atualizacao = { "preco":200.00, "descricao_complementar": "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes, implantação e suporte pós-entrega por 30 dias.", } resposta = requests.patch( f"{BASE_URL}/servicos/{servico_id}/", headers=headers, json=atualizacao, ) dados = resposta.json() if dados["success"]: servico = dados["data"] print(f"Servico atualizado! Novo preco: R$ {servico['preco']}") 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 servicoId = 39; const atualizacao = { preco: 200.0, descricao_complementar: "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes, implantação e suporte pós-entrega por 30 dias.", }; const resposta = await fetch(`${BASE_URL}/servicos/${servicoId}/`, { method: "PATCH", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(atualizacao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Servico atualizado! Novo preco: R$ ${dados.data.preco}`); } else { console.error(`Erro ao atualizar:`, dados); }
Resposta (200 OK)
json{ "success": true, "message": "Servico atualizado com sucesso", "data": { "id": 39, "descricao":"Desenvolvimento de Software Sob Demanda", "codigo":"SERV-039", "preco":"200.00", "unidade": "HR", "situacao":"A", "situacao_display": "Ativo", "codigo_servico_municipal": "01.01", "nbs_codigo": "1.1201", "descricao_complementar": "Desenvolvimento de sistemas web e aplicativos sob medida, incluindo levantamento de requisitos, codificação, testes, implantação e suporte pós-entrega por 30 dias.", "observacoes":"Valor por hora técnica. Projetos acima de 200 horas possuem desconto progressivo.", "criado_em": "2026-02-12T15:30:00-03:00", "atualizado_em": "2026-02-12T16:45:00-03:00" } }
Excluir Serviço (Soft Delete)
DELETE /v1/servicos/{id}/
Escopo necessário: servicos: delete
Inativa o serviço (soft delete). O registro não é removido permanentemente do banco de dados -- o campo situacao é definido como I (inativo), preservando o histórico de utilização.
Exemplo com cURL
bashcurl -X DELETE https://api-backend.bunto.com.br/v1/servicos/39/ \ -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}"} servico_id = 39 resposta = requests.delete(f"{BASE_URL}/servicos/{servico_id}/", headers=headers) dados = resposta.json() if dados["success"]: print("Servico excluído com sucesso!") 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 servicoId = 39; const resposta = await fetch(`${BASE_URL}/servicos/${servicoId}/`, { method: "DELETE", headers: { Authorization: `Bearer ${TOKEN}`, }, }); const dados = await resposta.json(); if (dados.success) { console.log("Servico excluído com sucesso!"); } else { console.error(`Erro ao excluir:`, dados); }
Resposta (200 OK)
json{ "success": true, "message": "Registro excluído com sucesso" }
Importante: Serviços excluídos via soft delete têm a situação alterada para I (inativo). Para visualizar serviços inativos, filtre por situacao=I.
Erros Comuns
| Código | Erro | Causa | Solução |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Dados enviados são inválidos (campo obrigatório ausente, formato incorreto, código duplicado, etc.) | Verifique os campos obrigatórios e os tipos de dados |
| 401 | Token invalido | Token ausente, expirado, revogado ou mal formatado | Verifique se o header é Authorization: Bearer bnt_xxx |
| 403 | Token nao tem permissao | O token não possui o escopo servicos ou a ação necessária (read, write, delete) | Verifique os escopos do token no painel |
| 404 | Nao encontrado | Serviço não existe ou pertence a outra empresa | Confirme o ID e se o serviço pertence à empresa do token |
| 429 | Limite de requisicoes excedido | Rate limit ultrapassado para o tipo de operação | Implemente retry com backoff exponencial |
Exemplo de Resposta de Erro (400 - Validação)
json{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Erro de validacao", "details": { "descricao": ["Este campo é obrigatorio."], "codigo": ["Servico com este codigo ja existe nesta empresa."] } } }
Exemplo de Resposta de Erro (403 - Permissão)
json{ "detail": "Token nao tem permissao 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 requisicoes excedido. Tente novamente em 45 segundos." }
Boas práticas
- Use
por_pagina=100para reduzir o número de requisições ao listar serviços - Implemente retry com backoff exponencial ao receber
429 - Armazene dados em cache local quando possível
- Para atualizações em lote, use
PATCHapenas com os campos alterados para economizar requisições de escrita
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}"} todos_os_servicos = [] url = f"{BASE_URL}/servicos/?por_pagina=100" while url: resposta = requests.get(url, headers=headers) dados = resposta.json() if not dados["success"]: break todos_os_servicos.extend(dados["data"]["resultados"]) url = dados["data"]["paginacao"]["proxima"] print(f"Total obtido: {len(todos_os_servicos)} servicos")
Valores de Referência
Situação do Serviço
| Valor | Descrição |
|---|---|
A | Ativo |
I | Inativo (inclui serviços excluídos via soft delete) |