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

API de Expedição

CRUD de expedições com rastreamento via API.

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

Gerencie as expedições do Bunto ERP. Este endpoint permite listar, consultar, criar, atualizar e cancelar expedições, incluindo dados de rastreamento e itens vinculados, via API.

Base URL

Produção:

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

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: expedicao com a ação correspondente (read, write ou delete).


Endpoints

Listar Expedições

GET /v1/expedicao/

Escopo necessário: expedicao: read

Retorna a lista paginada de expedições da empresa.

Query Parameters

ParâmetroTipoPadrãoDescrição
paginainteger1Número da página
por_paginainteger25Registros por página (máximo: 100)
buscastring-Busca por número da expedição ou código de rastreio
statusstring-Filtrar por status: pendente, concluida, cancelada
data_iniciodate-Filtrar expedições a partir desta data (formato: YYYY-MM-DD)
data_fimdate-Filtrar expedições até esta data (formato: YYYY-MM-DD)
ordenarstringdata_criacaoCampo de ordenação: data_criacao, numero_expedicao
direcaostringdescDireção da ordenação: asc ou desc

Exemplo com cURL

bash
curl 'https://api-backend.bunto.com.br/v1/expedicao/?pagina=1&por_pagina=10&status=pendente&ordenar=data_criacao&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 expedições pendentes do mês atual resposta = requests.get( f"{BASE_URL}/expedicao/", headers=headers, params={ "pagina": 1, "por_pagina": 25, "status": "pendente", "data_inicio": "2026-02-01", "data_fim": "2026-02-28", "ordenar": "data_criacao", "direcao": "desc", }, ) dados = resposta.json() if dados["success"]: expedicoes = dados["data"]["resultados"] paginacao = dados["data"]["paginacao"] print(f"Total de expedições: {paginacao['total_registros']}") for exp in expedicoes: rastreio = exp["codigo_rastreio"] or "Sem rastreio" print(f" - #{exp['numero_expedicao']} [{exp['status']}] Rastreio: {rastreio}") 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: "pendente", data_inicio: "2026-02-01", data_fim: "2026-02-28", ordenar: "data_criacao", direcao: "desc", }); const resposta = await fetch(`${BASE_URL}/expedicao/?${params}`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const expedicoes = dados.data.resultados; const paginacao = dados.data.paginacao; console.log(`Total de expedições: ${paginacao.total_registros}`); expedicoes.forEach((exp) => { const rastreio = exp.codigo_rastreio || "Sem rastreio"; console.log(` - #${exp.numero_expedicao} [${exp.status}] Rastreio: ${rastreio}`); }); } else { console.error(`Erro: ${resposta.status}`); }

Resposta (200 OK)

json
{ "success": true, "message": "12 registros encontrados", "data": { "resultados": [ { "id": 45, "numero_expedicao": "EXP-2026-0045", "status": "pendente", "forma_envio_id": 2, "codigo_rastreio": "BR123456789BR", "data_criacao": "2026-02-10T14:30:00-03:00" }, { "id": 44, "numero_expedicao": "EXP-2026-0044", "status": "pendente", "forma_envio_id": 1, "codigo_rastreio": null, "data_criacao": "2026-02-09T10:15:00-03:00" }, { "id": 42, "numero_expedicao": "EXP-2026-0042", "status": "pendente", "forma_envio_id": 3, "codigo_rastreio": "JD987654321", "data_criacao": "2026-02-07T16:45:00-03:00" } ], "paginacao": { "pagina_atual": 1, "total_paginas": 1, "total_registros": 12, "por_pagina": 25, "proxima": null, "anterior": null } } }

Campos da Listagem

CampoTipoDescrição
idintegerIdentificador único da expedição
numero_expedicaostringNúmero sequencial da expedição
statusstringStatus da expedição: pendente, concluida, cancelada
forma_envio_idinteger / nullID da forma de envio vinculada
codigo_rastreiostring / nullCódigo de rastreamento da transportadora
data_criacaodatetimeData e hora de criação

Obter Expedição

GET /v1/expedicao/{id}/

Escopo necessário: expedicao: read

Retorna os dados completos de uma expedição específica, incluindo a lista de itens vinculados.

Exemplo com cURL

bash
curl https://api-backend.bunto.com.br/v1/expedicao/45/ \ -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", } expedicao_id = 45 resposta = requests.get(f"{BASE_URL}/expedicao/{expedicao_id}/", headers=headers) dados = resposta.json() if dados["success"]: exp = dados["data"] print(f"Expedição: #{exp['numero_expedicao']}") print(f"Status: {exp['status']}") print(f"Rastreio: {exp['codigo_rastreio']}") if exp["itens"]: print("Itens:") for item in exp["itens"]: print(f" - Pedido #{item['pedido_venda_id']} | Produto #{item['produto_id']} | Qtd: {item['quantidade']}") else: print(f"Erro: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const expedicaoId = 45; const resposta = await fetch(`${BASE_URL}/expedicao/${expedicaoId}/`, { headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, }); const dados = await resposta.json(); if (dados.success) { const exp = dados.data; console.log(`Expedição: #${exp.numero_expedicao}`); console.log(`Status: ${exp.status}`); console.log(`Rastreio: ${exp.codigo_rastreio}`); if (exp.itens && exp.itens.length > 0) { console.log("Itens:"); exp.itens.forEach((item) => { console.log(` - Pedido #${item.pedido_venda_id} | Produto #${item.produto_id} | Qtd: ${item.quantidade}`); }); } } else { console.error(`Erro: ${JSON.stringify(dados)}`); }

Resposta (200 OK)

json
{ "success": true, "message": "Registro encontrado", "data": { "id": 45, "numero_expedicao": "EXP-2026-0045", "status": "pendente", "forma_envio_id": 2, "codigo_rastreio": "BR123456789BR", "data_criacao": "2026-02-10T14:30:00-03:00", "url_rastreio": "https://www.linkcorreios.com.br/?id=BR123456789BR", "rastreio_enviado": false, "data_conclusao": null, "criado_em": "2026-02-10T14:30:00-03:00", "atualizado_em": "2026-02-10T14:30:00-03:00", "itens": [ { "pedido_venda_id": 120, "produto_id": 55, "quantidade": 2 }, { "pedido_venda_id": 120, "produto_id": 78, "quantidade": 1 } ] } }

Campos do Detalhe (adicionais à listagem)

CampoTipoDescrição
url_rastreiostring / nullURL completa de rastreamento
rastreio_enviadobooleanSe o rastreio já foi enviado ao cliente
data_conclusaodatetime / nullData e hora em que a expedição foi concluída
criado_emdatetimeData e hora de criação (alias de data_criacao)
atualizado_emdatetimeData e hora da última atualização
itensarrayLista de itens da expedição
itens[].pedido_venda_idintegerID do pedido de venda vinculado
itens[].produto_idinteger / nullID do produto
itens[].quantidadeintegerQuantidade de volumes do item

Criar Expedição

POST /v1/expedicao/

Escopo necessário: expedicao: write

Cria uma nova expedição na empresa do token autenticado.

Campos do Request Body

CampoTipoObrigatórioDescrição
numero_expedicaostringNãoNúmero da expedição (máximo 50 caracteres; gerado automaticamente se não informado)
forma_envio_idintegerNãoID da forma de envio (ver API de Logística)
codigo_rastreiostringNãoCódigo de rastreamento da transportadora (máximo 100 caracteres)
url_rastreiostringNãoURL completa de rastreamento
statusstringNãoStatus da expedição: pendente, concluida, cancelada (padrão: pendente)

Exemplo com cURL

bash
curl -X POST https://api-backend.bunto.com.br/v1/expedicao/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "forma_envio_id": 2, "codigo_rastreio": "BR987654321BR", "url_rastreio": "https://www.linkcorreios.com.br/?id=BR987654321BR", "status": "pendente" }'

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", } nova_expedicao = { "forma_envio_id": 2, "codigo_rastreio": "BR987654321BR", "url_rastreio": "https://www.linkcorreios.com.br/?id=BR987654321BR", "status": "pendente", } resposta = requests.post(f"{BASE_URL}/expedicao/", headers=headers, json=nova_expedicao) dados = resposta.json() if dados["success"]: exp = dados["data"] print(f"Expedição criada com sucesso! ID: {exp['id']}") print(f"Número: {exp['numero_expedicao']}") print(f"Rastreio: {exp['codigo_rastreio']}") else: print(f"Erro ao criar expedição: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const novaExpedicao = { forma_envio_id: 2, codigo_rastreio: "BR987654321BR", url_rastreio: "https://www.linkcorreios.com.br/?id=BR987654321BR", status: "pendente", }; const resposta = await fetch(`${BASE_URL}/expedicao/`, { method: "POST", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(novaExpedicao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Expedição criada com sucesso! ID: ${dados.data.id}`); console.log(`Número: ${dados.data.numero_expedicao}`); console.log(`Rastreio: ${dados.data.codigo_rastreio}`); } else { console.error(`Erro ao criar expedição:`, dados); }

Resposta (201 Created)

json
{ "success": true, "message": "Expedição criada com sucesso", "data": { "id": 46, "numero_expedicao": "EXP-2026-0046", "status": "pendente", "forma_envio_id": 2, "codigo_rastreio": "BR987654321BR", "data_criacao": "2026-02-12T15:30:00-03:00", "url_rastreio": "https://www.linkcorreios.com.br/?id=BR987654321BR", "rastreio_enviado": false, "data_conclusao": null, "criado_em": "2026-02-12T15:30:00-03:00", "atualizado_em": "2026-02-12T15:30:00-03:00", "itens": [] } }

Atualizar Expedição

PUT /v1/expedicao/{id}/
PATCH /v1/expedicao/{id}/

Escopo necessário: expedicao: write

Atualiza uma expediçã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)

bash
curl -X PATCH https://api-backend.bunto.com.br/v1/expedicao/46/ \ -H 'Authorization: Bearer bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4' \ -H 'Content-Type: application/json' \ -d '{ "codigo_rastreio": "BR111222333BR", "status": "concluida" }'

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", } expedicao_id = 46 # Atualização parcial (PATCH) - atualizar rastreio e concluir atualizacao = { "codigo_rastreio": "BR111222333BR", "status": "concluida", } resposta = requests.patch( f"{BASE_URL}/expedicao/{expedicao_id}/", headers=headers, json=atualizacao, ) dados = resposta.json() if dados["success"]: exp = dados["data"] print(f"Expedição atualizada! Status: {exp['status']}") print(f"Rastreio: {exp['codigo_rastreio']}") 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 expedicaoId = 46; const atualizacao = { codigo_rastreio: "BR111222333BR", status: "concluida", }; const resposta = await fetch(`${BASE_URL}/expedicao/${expedicaoId}/`, { method: "PATCH", headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify(atualizacao), }); const dados = await resposta.json(); if (dados.success) { console.log(`Expedição atualizada! Status: ${dados.data.status}`); console.log(`Rastreio: ${dados.data.codigo_rastreio}`); } else { console.error(`Erro ao atualizar:`, dados); }

Resposta (200 OK)

json
{ "success": true, "message": "Expedição atualizada com sucesso", "data": { "id": 46, "numero_expedicao": "EXP-2026-0046", "status": "concluida", "forma_envio_id": 2, "codigo_rastreio": "BR111222333BR", "data_criacao": "2026-02-12T15:30:00-03:00", "url_rastreio": "https://www.linkcorreios.com.br/?id=BR987654321BR", "rastreio_enviado": false, "data_conclusao": "2026-02-12T16:45:00-03:00", "criado_em": "2026-02-12T15:30:00-03:00", "atualizado_em": "2026-02-12T16:45:00-03:00", "itens": [] } }

Cancelar Expedição

DELETE /v1/expedicao/{id}/

Escopo necessário: expedicao: delete

Cancela a expedição (soft delete). O registro não é removido permanentemente do banco de dados -- o status é alterado para cancelada, preservando o histórico.

Exemplo com cURL

bash
curl -X DELETE https://api-backend.bunto.com.br/v1/expedicao/46/ \ -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}"} expedicao_id = 46 resposta = requests.delete(f"{BASE_URL}/expedicao/{expedicao_id}/", headers=headers) dados = resposta.json() if dados["success"]: print("Expedição cancelada com sucesso!") else: print(f"Erro ao cancelar: {dados}")

Exemplo com JavaScript

javascript
const BASE_URL = "https://api-backend.bunto.com.br/v1"; const TOKEN = "bnt_A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5p6Q7r8S9t0U1v2W3x4"; const expedicaoId = 46; const resposta = await fetch(`${BASE_URL}/expedicao/${expedicaoId}/`, { method: "DELETE", headers: { Authorization: `Bearer ${TOKEN}`, }, }); const dados = await resposta.json(); if (dados.success) { console.log("Expedição cancelada com sucesso!"); } else { console.error(`Erro ao cancelar:`, dados); }

Resposta (200 OK)

json
{ "success": true, "message": "Expedição cancelada com sucesso" }

Importante: Diferente de outros endpoints, o DELETE em expedições não desativa o registro -- ele altera o status para cancelada. Expedições canceladas continuam visíveis na listagem ao filtrar por status=cancelada.


Erros Comuns

CódigoErroCausaSolução
400VALIDATION_ERRORDados enviados são inválidos (campo obrigatório ausente, formato incorreto, status inválido, 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 expedicao ou a ação necessária (read, write ou delete)Verifique os escopos do token no painel
404Não encontradoExpedição não existe ou pertence a outra empresaConfirme o ID e se a expedição 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": { "status": ["\"enviada\" não é um valor válido. Valores aceitos: pendente, concluida, cancelada."] } } }

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 expedições
  • Implemente retry com backoff exponencial ao receber 429
  • Armazene dados em cache local quando possível
  • Use filtros de data (data_inicio e data_fim) 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}"} todas_as_expedicoes = [] url = f"{BASE_URL}/expedicao/?por_pagina=100" while url: resposta = requests.get(url, headers=headers) dados = resposta.json() if not dados["success"]: break todas_as_expedicoes.extend(dados["data"]["resultados"]) url = dados["data"]["paginacao"]["proxima"] print(f"Total obtido: {len(todas_as_expedicoes)} expedições")
APICRUDcURLExpediçãoJavaScriptPythonREST
Recursos para IA