API de Expedição
CRUD de expedições com rastreamento via API.
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â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 número da expedição ou código de rastreio |
status | string | - | Filtrar por status: pendente, concluida, cancelada |
data_inicio | date | - | Filtrar expedições a partir desta data (formato: YYYY-MM-DD) |
data_fim | date | - | Filtrar expedições até esta data (formato: YYYY-MM-DD) |
ordenar | string | data_criacao | Campo de ordenação: data_criacao, numero_expedicao |
direcao | string | desc | Direção da ordenação: asc ou desc |
Exemplo com cURL
bashcurl '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
pythonimport 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
javascriptconst 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
| Campo | Tipo | Descrição |
|---|---|---|
id | integer | Identificador único da expedição |
numero_expedicao | string | Número sequencial da expedição |
status | string | Status da expedição: pendente, concluida, cancelada |
forma_envio_id | integer / null | ID da forma de envio vinculada |
codigo_rastreio | string / null | Código de rastreamento da transportadora |
data_criacao | datetime | Data 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
bashcurl https://api-backend.bunto.com.br/v1/expedicao/45/ \ -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", } 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
javascriptconst 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)
| Campo | Tipo | Descrição |
|---|---|---|
url_rastreio | string / null | URL completa de rastreamento |
rastreio_enviado | boolean | Se o rastreio já foi enviado ao cliente |
data_conclusao | datetime / null | Data e hora em que a expedição foi concluída |
criado_em | datetime | Data e hora de criação (alias de data_criacao) |
atualizado_em | datetime | Data e hora da última atualização |
itens | array | Lista de itens da expedição |
itens[].pedido_venda_id | integer | ID do pedido de venda vinculado |
itens[].produto_id | integer / null | ID do produto |
itens[].quantidade | integer | Quantidade 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
numero_expedicao | string | Não | Número da expedição (máximo 50 caracteres; gerado automaticamente se não informado) |
forma_envio_id | integer | Não | ID da forma de envio (ver API de Logística) |
codigo_rastreio | string | Não | Código de rastreamento da transportadora (máximo 100 caracteres) |
url_rastreio | string | Não | URL completa de rastreamento |
status | string | Não | Status da expedição: pendente, concluida, cancelada (padrão: pendente) |
Exemplo com cURL
bashcurl -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
pythonimport 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
javascriptconst 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)
bashcurl -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
pythonimport 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
javascriptconst 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
bashcurl -X DELETE https://api-backend.bunto.com.br/v1/expedicao/46/ \ -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}"} 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
javascriptconst 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ódigo | Erro | Causa | Solução |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Dados 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 |
| 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 expedicao ou a ação necessária (read, write ou delete) | Verifique os escopos do token no painel |
| 404 | Não encontrado | Expedição não existe ou pertence a outra empresa | Confirme o ID e se a expediçã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": { "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çã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 expedições - Implemente retry com backoff exponencial ao receber
429 - Armazene dados em cache local quando possível
- Use filtros de data (
data_inicioedata_fim) para limitar o volume de dados retornados - 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}"} 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")