# SuperFrete para Desenvolvedores: documentação completa > Fonte: https://developers.superfrete.com. Guias seguidos da referência completa da API, gerada de https://developers.superfrete.com/openapi.yaml. --- # Primeiros passos Este guia leva você do zero até a primeira cotação de frete pela API. A integração é **gratuita e não tem mensalidade**. > **Já usa uma plataforma de e-commerce?** > > Esta documentação é para quem tem um site ou sistema próprio. Se a sua loja está em uma plataforma como Nuvemshop, Shopify ou WooCommerce, veja antes as [integrações prontas no painel](https://web.superfrete.com/#/integrations). ## 1. Escolha o ambiente A SuperFrete tem dois ambientes. Cada um tem a sua URL e o seu próprio token: um token de sandbox não funciona em produção, e vice-versa. | Ambiente | URL base da API | Painel para gerar o token | | --- | --- | --- | | **Sandbox** (testes) | `https://sandbox.superfrete.com` | [sandbox.superfrete.com/#/integrations](https://sandbox.superfrete.com/#/integrations) | | **Produção** | `https://api.superfrete.com` | [web.superfrete.com/#/integrations](https://web.superfrete.com/#/integrations) | > **Sandbox é para testar** > > As etiquetas geradas no sandbox não têm validade e não podem ser usadas para postar nas transportadoras. Comece por lá e só troque para produção quando o fluxo estiver redondo. ## 2. Gere o seu token O passo a passo é o mesmo nos dois ambientes; só muda o endereço do painel. 1. Entre no painel do ambiente e abra o menu **Integrações** ([sandbox](https://sandbox.superfrete.com/#/integrations) ou [produção](https://web.superfrete.com/#/integrations)). 2. Clique em **Nova integração**. 3. Na tela **Qual a integração?**, escolha a opção da **API** (desenvolvedores). 4. Na janela que abre, clique em **Gerar token**. Se aparecer um formulário curto sobre a sua loja, preencha e continue. 5. Copie o token exibido no passo **Copie e cole o token no site do parceiro** e guarde em local seguro, como uma variável de ambiente do seu servidor. > *Tela (web.superfrete.com/#/integrations): Página Integrações conectadas, com a lista de integrações e o botão verde Nova integração no canto superior direito.* Menu Integrações: o ponto de partida para gerar o token. > *Tela (web.superfrete.com/#/integrations/select-integration-platform): Janela “Para integrar com o parceiro você precisa de um token de acesso.” com os botões Cancelar e Gerar token.* Clique em Gerar token para criar o token da API. > *Tela (web.superfrete.com/#/integrations/api): Passo “Copie e cole o token no site do parceiro”, com o campo do token e o botão de copiar.* O token fica disponível aqui. Você pode voltar a esta tela depois, clicando na integração em Integrações. O token não expira. Ele só deixa de valer se a integração for excluída em **Integrações**. Se você está construindo uma plataforma com várias lojas, o caminho é outro: veja [Autenticação](https://developers.superfrete.com/guias/autenticacao.md) e o [fluxo OAuth 2.0](https://developers.superfrete.com/guias/oauth.md). ## 3. Monte os headers Toda requisição precisa de dois headers: - `Authorization: Bearer {token}`, com o token do passo anterior. - `User-Agent` com o nome e a versão da sua aplicação e um e-mail para contato técnico entre parênteses, no formato `Aplicacao/versão (email@dominio.com)`. Usamos esse e-mail para avisar você sobre mudanças e problemas na sua integração. ```http Authorization: Bearer {token} User-Agent: MinhaLoja/1.0 (dev@minhaloja.com.br) ``` ## 4. Faça a primeira cotação Com o token de sandbox em mãos, cote um frete de São Paulo para o Rio de Janeiro: ```bash curl --request POST \ --url https://sandbox.superfrete.com/api/v0/calculator \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: MinhaLoja/1.0 (dev@minhaloja.com.br)" \ --header "Accept: application/json" \ --header "Content-Type: application/json" \ --data '{ "from": { "postal_code": "01153000" }, "to": { "postal_code": "20020050" }, "services": "1,2,17", "options": { "own_hand": false, "receipt": false, "insurance_value": 0, "use_insurance_value": false }, "package": { "height": 2, "width": 11, "length": 16, "weight": 0.3 } }' ``` A resposta traz uma opção por serviço, com preço, prazo e a caixa usada no cálculo. Os detalhes de cada campo estão na [referência de Cotar frete](https://developers.superfrete.com/referencia/md/calculateShipping.md), e as regras de cada transportadora no [guia de frete](https://developers.superfrete.com/guias/frete.md). ## 5. Adicione saldo no sandbox Para testar a emissão e o pagamento de etiquetas, a sua carteira de sandbox precisa de saldo. O pagamento é simulado: 1. Acesse [sandbox.superfrete.com/#/account/credits](https://sandbox.superfrete.com/#/account/credits). 2. Clique em **Recarregue com Pix**. 3. Escolha um valor. 4. Clique em **Recarregar com Pix**. 5. Clique em **Copiar código PIX**. 6. Cole o código na **barra de endereço do navegador** e pressione **Enter**. O pagamento é simulado e o crédito aparece na carteira de testes. Prefere ver em vídeo? [Assista ao passo a passo gravado](https://drive.google.com/file/d/1xVSLd6vp39bCBYkQe0kbAbLl5ij72Fbu/view?usp=sharing). ## Erros Quando algo dá errado, a API responde com o status HTTP correspondente e um JSON explicando o motivo. Erros de validação (status `400`) trazem o objeto `errors`, com os problemas de cada campo: ```json { "message": "Ocorreu um ou mais erros.", "errors": { "to.postal_code": ["(to.postal_code) é inválido."] } } ``` Os formatos, os status e as mensagens mais comuns estão em [Erros e limites](https://developers.superfrete.com/guias/erros.md). ## Próximos passos - [Emitir a primeira etiqueta de ponta a ponta](https://developers.superfrete.com/guias/primeira-etiqueta.md) - [Calcular frete e escolher a caixa certa](https://developers.superfrete.com/guias/frete.md) - [Receber eventos dos pedidos por webhook](https://developers.superfrete.com/guias/webhooks.md) - [Ver todos os endpoints na referência da API](https://developers.superfrete.com/referencia) Ficou com alguma dúvida? Escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com) ou fale com a gente pelo chat (assistente de chat do site). --- # Sua primeira etiqueta Neste guia você emite uma etiqueta completa pela API, do começo ao fim, **no sandbox**. Nada aqui gera cobrança nem etiqueta válida nas transportadoras, então pode testar à vontade. O caminho tem seis passos: | Passo | Chamada | O que acontece | | --- | --- | --- | | 1. Cotar | [`POST /api/v0/calculator`](https://developers.superfrete.com/referencia/md/calculateShipping.md) | Preço, prazo e caixa ideal de cada serviço | | 2. Criar a etiqueta | [`POST /api/v0/cart`](https://developers.superfrete.com/referencia/md/createShipment.md) | A etiqueta nasce com status `pending` | | 3. Pagar | [`POST /api/v0/checkout`](https://developers.superfrete.com/referencia/md/checkoutShipment.md) | Debita o saldo da carteira e libera a etiqueta | | 4. Imprimir | [`POST /api/v0/tag/print`](https://developers.superfrete.com/referencia/md/getLabelPrintUrl.md) | Devolve a URL do PDF | | 5. Consultar | [`GET /api/v0/order/info/{id}`](https://developers.superfrete.com/referencia/md/getOrder.md) | Status e código de rastreio | | 6. Acompanhar | [`POST /api/v0/webhook`](https://developers.superfrete.com/referencia/md/createWebhook.md) | A SuperFrete avisa você a cada mudança | ## Antes de começar Você precisa de quatro coisas no **sandbox**: 1. **Uma conta** em [sandbox.superfrete.com](https://sandbox.superfrete.com), com o cadastro completo e um **número de celular** salvo no perfil. Sem celular, a criação da etiqueta falha com `user.phone is required.`. 2. **Um token de sandbox.** Siga o passo a passo em [Primeiros passos](https://developers.superfrete.com/guias/primeiros-passos.md#2-gere-o-seu-token). 3. **Saldo na carteira de testes**, para pagar a etiqueta. O Pix é simulado: veja [Adicione saldo no sandbox](https://developers.superfrete.com/guias/primeiros-passos.md#5-adicione-saldo-no-sandbox). 4. **Um `User-Agent`** no formato `NomeDaAplicacao/versão (email@dominio.com)`. Deixe o token e o `User-Agent` em variáveis de ambiente para copiar os exemplos sem editar: ```bash export SUPERFRETE_TOKEN="cole-o-token-de-sandbox-aqui" export SUPERFRETE_UA="MinhaLoja/1.0 (dev@minhaloja.com.br)" export SUPERFRETE_API="https://sandbox.superfrete.com" ``` ## 1. Cote o frete Informe os CEPs de origem e destino, os serviços que quer comparar e os produtos do pedido. Com `products`, a API calcula a menor caixa que acomoda tudo. ```bash curl --request POST \ --url "$SUPERFRETE_API/api/v0/calculator" \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: $SUPERFRETE_UA" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data '{ "from": { "postal_code": "01153000" }, "to": { "postal_code": "20020050" }, "services": "1,2,17", "options": { "own_hand": false, "receipt": false, "insurance_value": 0, "use_insurance_value": false }, "products": [ { "quantity": 2, "weight": 0.3, "height": 4, "width": 12, "length": 17 } ] }' ``` A resposta é uma lista, com um item por serviço. Escolha um e anote duas coisas: o **`id`** do serviço e a **caixa** em `packages[0]`. ```json [ { "id": 1, "name": "PAC", "price": 18.61, "delivery_time": 5, "packages": [ { "format": "box", "dimensions": { "height": "8", "width": "16", "length": "24" }, "weight": "0.6" } ], "has_error": false } ] ``` > **Use a caixa da cotação na etiqueta** > > No próximo passo, envie em `volumes` a caixa devolvida aqui (`packages[0]`), e não as medidas de cada produto. Assim a etiqueta sai exatamente com as medidas que foram cotadas. Se um serviço vier com `has_error: true`, ele não atende aquele envio. O motivo está no campo `error`. ## 2. Crie a etiqueta Envie remetente, destinatário, o serviço escolhido, a caixa e o conteúdo do pacote. Neste exemplo usamos **declaração de conteúdo**, que é o caminho mais simples para testar: basta enviar a lista de `products`. ```bash curl --request POST \ --url "$SUPERFRETE_API/api/v0/cart" \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: $SUPERFRETE_UA" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data '{ "from": { "name": "Loja Exemplo", "address": "Rua Vitorino Carmilo", "number": "100", "district": "Barra Funda", "city": "São Paulo", "state_abbr": "SP", "postal_code": "01153000" }, "to": { "name": "Maria Silva", "address": "Rua Primeiro de Março", "number": "50", "district": "Centro", "city": "Rio de Janeiro", "state_abbr": "RJ", "postal_code": "20020050", "email": "maria@example.com", "document": "12345678909" }, "service": 1, "products": [ { "name": "Camiseta", "quantity": 2, "unitary_value": 59.9 } ], "volumes": { "height": 8, "width": 16, "length": 24, "weight": 0.6 }, "options": { "insurance_value": null, "receipt": false, "own_hand": false }, "platform": "MinhaLoja" }' ``` ```json { "id": "r8p3dhqjn4I0tBpnvkpU", "price": 18.61, "status": "pending" } ``` Guarde o **`id`**: é ele que você usa em todos os passos seguintes. A etiqueta também já aparece em **Etiquetas** no painel do sandbox, aguardando pagamento. Os cuidados com nome do remetente, `state_abbr`, bairro e documento estão em [Cálculo de frete e etiquetas](https://developers.superfrete.com/guias/frete.md#criar-a-etiqueta). ## 3. Pague com o saldo ```bash curl --request POST \ --url "$SUPERFRETE_API/api/v0/checkout" \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: $SUPERFRETE_UA" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data '{ "orders": ["r8p3dhqjn4I0tBpnvkpU"] }' ``` ```json { "success": true, "purchase": { "status": "paid", "orders": [ { "id": "r8p3dhqjn4I0tBpnvkpU", "price": 18.61, "service_id": 1, "tracking": "AA123456785BR", "print": { "url": "" } } ] } } ``` Se faltar saldo, a API responde `409` com `Sem saldo na carteira!`. Recarregue a carteira de testes e chame de novo. ## 4. Imprima ```bash curl --request POST \ --url "$SUPERFRETE_API/api/v0/tag/print" \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: $SUPERFRETE_UA" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data '{ "orders": ["r8p3dhqjn4I0tBpnvkpU"] }' ``` ```json { "url": "https://sandbox.superfrete.com/_etiqueta/pdf/eyJvcmRlcl9pZCI6..." } ``` Abra a URL para ver o PDF. Você pode enviar vários IDs em `orders` para receber todas as etiquetas em um arquivo só. ## 5. Consulte o pedido ```bash curl --request GET \ --url "$SUPERFRETE_API/api/v0/order/info/r8p3dhqjn4I0tBpnvkpU" \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: $SUPERFRETE_UA" \ --header "Accept: application/json" ``` A partir do status `released`, o campo `tracking` traz o código de rastreio. Os status possíveis estão em [Status da etiqueta](https://developers.superfrete.com/guias/frete.md#status-da-etiqueta). ## 6. Acompanhe por webhook Em vez de consultar o pedido de tempos em tempos, peça para a SuperFrete avisar você. Para testar, gere uma URL pública temporária em um serviço como [webhook.site](https://webhook.site) e cadastre um Webhook App: ```bash curl --request POST \ --url "$SUPERFRETE_API/api/v0/webhook" \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: $SUPERFRETE_UA" \ --header "Content-Type: application/json" \ --header "Accept: application/json" \ --data '{ "name": "Teste sandbox", "url": "https://webhook.site/sua-url-unica", "events": ["order.created", "order.released", "order.generated", "order.posted", "order.delivered", "order.cancelled"] }' ``` Guarde o `secret_token` da resposta: é com ele que você valida a assinatura de cada notificação. Agora repita os passos 2 e 3 com uma etiqueta nova e veja os eventos chegarem. > **O que esperar no sandbox** > > No sandbox, nenhum pacote é postado nem entregue de verdade. Por isso os eventos de criação, pagamento e geração da etiqueta chegam, mas `order.posted` e `order.delivered` normalmente não. Como validar a assinatura e responder do jeito certo está no [guia de webhooks](https://developers.superfrete.com/guias/webhooks.md). ## Tudo junto, em Node.js O script abaixo faz os passos 1 a 5 em sequência. Rode com Node.js 18 ou mais recente: **Node.js** ```js const API = process.env.SUPERFRETE_API ?? 'https://sandbox.superfrete.com'; const headers = { Authorization: `Bearer ${process.env.SUPERFRETE_TOKEN}`, 'User-Agent': process.env.SUPERFRETE_UA ?? 'MinhaLoja/1.0 (dev@minhaloja.com.br)', 'Content-Type': 'application/json', Accept: 'application/json', }; async function call(method, path, body) { const response = await fetch(`${API}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); const data = await response.json(); if (!response.ok) throw new Error(`${method} ${path} -> ${response.status}: ${JSON.stringify(data)}`); return data; } // 1. Cotar const quotes = await call('POST', '/api/v0/calculator', { from: { postal_code: '01153000' }, to: { postal_code: '20020050' }, services: '1,2,17', options: { own_hand: false, receipt: false, insurance_value: 0, use_insurance_value: false }, products: [{ quantity: 2, weight: 0.3, height: 4, width: 12, length: 17 }], }); const quote = quotes.find((q) => !q.has_error); const box = quote.packages[0]; console.log(`Serviço ${quote.name}: R$ ${quote.price} em ${quote.delivery_time} dias úteis`); // 2. Criar a etiqueta com a caixa da cotação const shipment = await call('POST', '/api/v0/cart', { from: { name: 'Loja Exemplo', address: 'Rua Vitorino Carmilo', number: '100', district: 'Barra Funda', city: 'São Paulo', state_abbr: 'SP', postal_code: '01153000', }, to: { name: 'Maria Silva', address: 'Rua Primeiro de Março', number: '50', district: 'Centro', city: 'Rio de Janeiro', state_abbr: 'RJ', postal_code: '20020050', email: 'maria@example.com', document: '12345678909', }, service: quote.id, products: [{ name: 'Camiseta', quantity: 2, unitary_value: 59.9 }], volumes: { height: Number(box.dimensions.height), width: Number(box.dimensions.width), length: Number(box.dimensions.length), weight: Number(box.weight), }, options: { insurance_value: null, receipt: false, own_hand: false }, platform: 'MinhaLoja', }); console.log(`Etiqueta ${shipment.id} criada (${shipment.status})`); // 3. Pagar com o saldo await call('POST', '/api/v0/checkout', { orders: [shipment.id] }); // 4. Imprimir const { url } = await call('POST', '/api/v0/tag/print', { orders: [shipment.id] }); console.log(`PDF: ${url}`); // 5. Consultar const order = await call('GET', `/api/v0/order/info/${shipment.id}`); console.log(`Status: ${order.status}, rastreio: ${order.tracking ?? 'ainda não disponível'}`); ``` ## Indo para produção Quando o fluxo estiver redondo no sandbox: 1. Gere um token na [página de integrações de produção](https://web.superfrete.com/#/integrations). 2. Troque a URL base para `https://api.superfrete.com`. 3. Recarregue a carteira de produção com Pix em **Perfil > Carteira**, ou pague as etiquetas pelo painel. 4. Cadastre o Webhook App de produção. Webhooks do sandbox e de produção são independentes. 5. Revise as [regras de cada transportadora](https://developers.superfrete.com/guias/frete.md#regras-por-transportadora) antes de liberar Jadlog, Loggi ou J&T. Ficou com alguma dúvida? Escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com) ou fale com a gente pelo chat (assistente de chat do site). --- # Autenticação Toda chamada à API da SuperFrete é autenticada com um token no header `Authorization`, no esquema Bearer. O que muda é **como você obtém esse token**. | | Token manual | OAuth 2.0 | | --- | --- | --- | | **Para quem** | Loja com site ou sistema próprio (um único seller) | Plataformas com várias lojas: Nuvemshop, Shopify, Wix, ERPs, hubs | | **Como o token é gerado** | O próprio seller gera no painel da SuperFrete | O seller autoriza a sua plataforma e o token é emitido automaticamente | | **O que você precisa** | Uma conta SuperFrete | `client_id` e `client_secret` fornecidos pela SuperFrete | | **Guia** | [Primeiros passos](https://developers.superfrete.com/guias/primeiros-passos.md#2-gere-o-seu-token) | [Fluxo OAuth 2.0](https://developers.superfrete.com/guias/oauth.md) | > **Quem pode usar OAuth 2.0** > > O OAuth 2.0 é exclusivo para operações com múltiplas lojas. Integrações de um único seller (site próprio) devem usar o token manual, gerado na [página de integrações](https://web.superfrete.com/#/integrations). ## Enviando o token Inclua o token em todas as requisições, junto com o `User-Agent` da sua aplicação: ```http Authorization: Bearer {token} User-Agent: MinhaLoja/1.0 (dev@minhaloja.com.br) ``` O `User-Agent` é obrigatório. Informe o nome e a versão da sua aplicação e termine com um e-mail para contato técnico entre parênteses, como no exemplo acima. Cada token vale para um único ambiente. Use o token de sandbox com `https://sandbox.superfrete.com` e o de produção com `https://api.superfrete.com`. Se misturar os dois, a API responde `401` com `Token inválida!`. ## Validade e revogação Os tokens **não expiram**, nem o manual nem o obtido por OAuth, e não existe refresh token. Um token só deixa de valer quando a integração é excluída em **Integrações** no painel da SuperFrete. A partir daí, as chamadas com ele voltam `401`. Trate o token como uma senha: guarde no servidor, em variável de ambiente ou cofre de segredos, e nunca exponha no front-end. Se suspeitar de vazamento, exclua a integração no painel e gere outro token. ## Token manual É o caminho para quem integra a própria loja. O seller gera o token no menu **Integrações** do painel, em **Nova integração**, escolhendo a opção da **API** e clicando em **Gerar token**. Cada ambiente tem o seu painel: - Produção: [web.superfrete.com/#/integrations](https://web.superfrete.com/#/integrations) - Sandbox: [sandbox.superfrete.com/#/integrations](https://sandbox.superfrete.com/#/integrations) O passo a passo completo está em [Primeiros passos](https://developers.superfrete.com/guias/primeiros-passos.md#2-gere-o-seu-token). > **Plataformas parceiras com token** > > Se a sua plataforma integra por token (sem OAuth), depois de concluir a integração com a SuperFrete você recebe uma identificação de produto parceiro. Ela aparece na seção de integrações do painel, e o seu usuário usa essa identificação para gerar o token que vai colar na sua plataforma. ## OAuth 2.0 Com OAuth, o seller clica em "Integrar com a SuperFrete" dentro da sua plataforma, autoriza o acesso e a sua aplicação recebe o token, sem copiar e colar nada. A implementação segue o padrão [OAuth 2.0](https://www.oauth.com/) com o fluxo *authorization code*. Para começar, peça as suas credenciais pelo [formulário de solicitação](https://docs.google.com/forms/d/e/1FAIpQLSf45TxWZnS0qjz98gF0llpLkcq1KcAjmx6vr3ru7s8xoBweYA/viewform?usp=dialog). Você recebe um **Client ID** e um **Client Secret** para produção e outro par para sandbox. No formulário você também informa a sua **`redirect_uri`**: a URL de callback da sua aplicação, para onde enviamos o código de autorização (`code`) temporário depois que o seller autoriza o acesso. O passo a passo, com as URLs de autorização e a troca do código pelo token, está no [guia do fluxo OAuth 2.0](https://developers.superfrete.com/guias/oauth.md) e na referência de [Trocar código OAuth por token](https://developers.superfrete.com/referencia/md/exchangeOAuthToken.md). > **Guarde o client_secret no backend** > > O `client_secret` nunca deve aparecer no front-end, no app mobile ou em repositórios. Faça a troca do código pelo token sempre a partir do seu servidor. --- # Fluxo OAuth 2.0 Com OAuth, o seller conecta a conta SuperFrete à sua plataforma com dois cliques e você recebe um token de acesso para operar em nome dele, sem que ninguém copie e cole token. > **Este fluxo é para plataformas com várias lojas** > > OAuth é para plataformas de e-commerce, ERPs e hubs que atendem muitos sellers. Se você está integrando **a sua própria loja**, use o token manual: veja [Autenticação](https://developers.superfrete.com/guias/autenticacao.md). ## Como funciona 1. Você pede as credenciais da sua aplicação à SuperFrete: `client_id`, `client_secret` e a `redirect_uri` cadastrada. 2. Na sua plataforma, o seller clica em algo como **Conectar SuperFrete** e você abre a **URL de autorização** da SuperFrete. 3. O seller entra na conta SuperFrete (se ainda não estiver logado) e clica em **Autorizar**. 4. Os servidores da SuperFrete fazem um `GET` na sua `redirect_uri` com um `code` temporário e o `state` que você enviou. 5. O seu backend troca esse `code` por um `access_token` em `POST /api/v0/oauth/token`. 6. A partir daí, você usa o `access_token` como `Authorization: Bearer` em todas as chamadas daquele seller. > **O code chega por uma chamada de servidor, não pelo navegador** > > Este é o ponto que mais confunde quem já implementou OAuth em outros lugares. Depois do clique em **Autorizar**, **o navegador do seller não é redirecionado para a sua `redirect_uri`**. Quem chama a sua URL é o servidor da SuperFrete, e o seller continua no painel da SuperFrete, na tela de configurações da integração. > > Por isso: > > - a requisição na `redirect_uri` não traz cookies nem a sessão do seller na sua plataforma. **Use o `state` para saber de qual seller é aquele `code`**; > - a `redirect_uri` precisa ser pública e responder com status `2xx` rapidamente. Se ela responder com erro ou não responder, a autorização falha e o seller vê a mensagem "Ocorreu uma falha na autenticação do cliente."; > - para trazer o seller de volta, abra a URL de autorização em uma nova aba ou janela e, na sua tela, acompanhe pelo seu backend quando o token daquele seller chegar. ## 1. Peça as suas credenciais Preencha o [formulário de solicitação de credenciais OAuth](https://docs.google.com/forms/d/e/1FAIpQLSf45TxWZnS0qjz98gF0llpLkcq1KcAjmx6vr3ru7s8xoBweYA/viewform?usp=dialog). Nele você informa a sua **`redirect_uri`**: a URL do seu backend que vai receber o `code`. Você recebe um par de credenciais **para cada ambiente**: | Credencial | Formato | Onde usar | | --- | --- | --- | | `client_id` | Número, por exemplo `1234` | Na URL de autorização e na troca do código | | `client_secret` | Texto secreto | Somente na troca do código, a partir do seu servidor | As credenciais de sandbox não funcionam em produção, e vice-versa. > **Guarde o client_secret no backend** > > O `client_secret` nunca deve aparecer no front-end, em app mobile ou em repositório. Guarde em variável de ambiente ou cofre de segredos. ## 2. Envie o seller para a URL de autorização Quando o seller decidir conectar a SuperFrete, abra a URL de autorização do ambiente: | Ambiente | URL de autorização | | --- | --- | | Sandbox | `https://sandbox.superfrete.com/#/auth/authorize` | | Produção | `https://web.superfrete.com/#/auth/authorize` | Com estes parâmetros na query string: | Parâmetro | Obrigatório | O que enviar | | --- | --- | --- | | `client_id` | Sim | O `client_id` do ambiente. | | `redirect_uri` | Sim | A URL cadastrada no formulário, **exatamente igual**, codificada com `encodeURIComponent`. | | `state` | Recomendado | Um valor aleatório e único por tentativa. Guarde no seu banco junto com o ID do seller: é por ele que você identifica o seller quando o `code` chegar. | ```js const AUTHORIZE_URL = 'https://sandbox.superfrete.com/#/auth/authorize'; export async function buildSuperFreteAuthorizeUrl(sellerId) { const state = crypto.randomBytes(24).toString('hex'); // Guarde o state com o seller e um prazo curto de validade. await db.oauthStates.insert({ state, sellerId, createdAt: new Date() }); const params = new URLSearchParams({ client_id: process.env.SUPERFRETE_CLIENT_ID, redirect_uri: process.env.SUPERFRETE_REDIRECT_URI, state, }); return `${AUTHORIZE_URL}?${params}`; } ``` O resultado fica assim: ```text https://sandbox.superfrete.com/#/auth/authorize?client_id=1234&redirect_uri=https%3A%2F%2Fapi.suaplataforma.com.br%2Fsuperfrete%2Fcallback&state=9f2c... ``` ## 3. O seller autoriza Se o seller ainda não estiver logado, a SuperFrete pede o login primeiro e depois volta para a tela de autorização. > *Tela (sandbox.superfrete.com/#/auth/phone): Tela de login da SuperFrete, pedindo o número de celular do seller.* Seller sem sessão ativa: primeiro o login, depois a autorização. Na tela de autorização, o seller vê a pergunta **Deseja autorizar a integração?** e clica em **Autorizar**. Para algumas plataformas parceiras, a tela também traz um formulário curto sobre a loja. > *Tela (sandbox.superfrete.com/#/auth/authorize): Tela de autorização com o logo da SuperFrete, a pergunta “Deseja autorizar a integração?” e o botão Autorizar.* A tela de consentimento que o seller vê ao abrir a sua URL de autorização. Depois do clique, a SuperFrete chama a sua `redirect_uri`. Se ela responder com `2xx`, o seller vê **Autorização concluída com sucesso!** e cai na tela de configurações da integração, onde pode escolher transportadoras e ponto de postagem. A sua integração aparece em **Integrações** no painel do seller. > *Tela (sandbox.superfrete.com/#/integrations/settings): Aviso “Autorização concluída com sucesso!” sobre a tela de configurações da integração, com as opções de transportadora e o botão Salvar ajustes.* Depois de autorizar, o seller fica no painel da SuperFrete. Mostre na sua tela que a conexão deu certo. ## 4. Receba o code na redirect_uri A SuperFrete faz esta chamada no seu backend: ```http GET /superfrete/callback?code=eyJhbGciOiJIUzI1NiIs...&state=9f2c... HTTP/1.1 Host: api.suaplataforma.com.br ``` Nesse endpoint: 1. Confira se o `state` existe no seu banco e ainda não foi usado. Se não existir, responda `400` e não faça mais nada. 2. Troque o `code` pelo token imediatamente (passo 5). O `code` **vale 10 minutos e só pode ser usado uma vez**. 3. Salve o token junto com o seller e responda `200`. **Node.js (Express)** ```js app.get('/superfrete/callback', async (req, res) => { const { code, state } = req.query; const pending = await db.oauthStates.findOneAndDelete({ state }); if (!pending || !code) return res.status(400).end(); try { const token = await exchangeCodeForToken(code, state); // passo 5 await db.sellers.update(pending.sellerId, { superfreteToken: encrypt(token.access_token), }); return res.status(200).end(); } catch (error) { console.error('SuperFrete OAuth exchange failed', error); return res.status(502).end(); } }); ``` **PHP** ```php **Responda rápido** > > O seller fica esperando na tela da SuperFrete enquanto a sua `redirect_uri` responde. Faça só o essencial antes de responder e deixe sincronizações mais pesadas para depois. ## 5. Troque o code pelo token Faça um `POST` a partir do seu servidor: | Ambiente | Endpoint | | --- | --- | | Sandbox | `https://sandbox.superfrete.com/api/v0/oauth/token` | | Produção | `https://api.superfrete.com/api/v0/oauth/token` | | Campo | Valor | | --- | --- | | `grant_type` | Sempre `authorization_code` | | `client_id` | O seu `client_id` | | `client_secret` | O seu `client_secret` | | `code` | O `code` recebido na `redirect_uri` | | `redirect_uri` | A mesma URL cadastrada, caractere por caractere | | `state` | O mesmo `state` da autorização (opcional) | O corpo pode ir em JSON ou em `application/x-www-form-urlencoded`. **cURL** ```bash curl --request POST \ --url https://sandbox.superfrete.com/api/v0/oauth/token \ --header "Content-Type: application/json" \ --data '{ "grant_type": "authorization_code", "client_id": "1234", "client_secret": "'"$SUPERFRETE_CLIENT_SECRET"'", "code": "eyJhbGciOiJIUzI1NiIs...", "redirect_uri": "https://api.suaplataforma.com.br/superfrete/callback", "state": "9f2c..." }' ``` **Node.js** ```js const TOKEN_URL = 'https://sandbox.superfrete.com/api/v0/oauth/token'; export async function exchangeCodeForToken(code, state) { const response = await fetch(TOKEN_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ grant_type: 'authorization_code', client_id: process.env.SUPERFRETE_CLIENT_ID, client_secret: process.env.SUPERFRETE_CLIENT_SECRET, code, redirect_uri: process.env.SUPERFRETE_REDIRECT_URI, state, }), }); const body = await response.json(); if (!response.ok) { // body.error: invalid_request, invalid_client, unauthorized_client... throw new Error(`${body.error}: ${body.error_description}`); } return body; // { access_token, expires_in: "never", token_type: "Bearer" } } ``` **PHP** ```php true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'grant_type' => 'authorization_code', 'client_id' => getenv('SUPERFRETE_CLIENT_ID'), 'client_secret' => getenv('SUPERFRETE_CLIENT_SECRET'), 'code' => $code, 'redirect_uri' => getenv('SUPERFRETE_REDIRECT_URI'), 'state' => $state, ]), ]); $body = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($status !== 200) { error_log("SuperFrete OAuth: {$body['error']} - {$body['error_description']}"); return null; } return $body; // access_token, expires_in ("never"), token_type ("Bearer") } ``` Resposta de sucesso: ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_in": "never", "token_type": "Bearer" } ``` ## 6. Use o token O `access_token` funciona exatamente como o token manual. Envie em todas as chamadas daquele seller, com o `User-Agent` da sua plataforma: ```bash curl --request GET \ --url https://sandbox.superfrete.com/api/v0/user \ --header "Authorization: Bearer $ACCESS_TOKEN" \ --header "User-Agent: SuaPlataforma/2.3 (dev@suaplataforma.com.br)" \ --header "Accept: application/json" ``` [Consultar usuário](https://developers.superfrete.com/referencia/md/getUser.md) é um bom primeiro teste: devolve nome, e-mail, saldo e limites da conta que acabou de autorizar. ## Validade, renovação e revogação - **O `access_token` não expira.** A resposta traz `expires_in: "never"` e não existe refresh token. Não agende renovação. - **O seller pode revogar o acesso a qualquer momento**, excluindo a integração em **Integrações** no painel da SuperFrete. A partir daí, as chamadas com aquele token voltam `401` com a mensagem `Token inválida!`. Trate esse caso marcando a conexão como desfeita e pedindo ao seller para conectar de novo. - **Se o seller autorizar outra vez**, um novo token é emitido. Substitua o antigo pelo novo. - **O `code` vale 10 minutos e é de uso único.** Se ele expirar antes da troca, basta o seller autorizar de novo. ## Erros comuns Os erros do endpoint de token seguem o formato do OAuth 2.0: ```json { "error": "invalid_client", "http_status": 401, "error_description": "Client authentication failed", "message": "Client authentication failed" } ``` | Status | `error` | `error_description` | O que fazer | | --- | --- | --- | --- | | `400` | `invalid_request` | `Required parameter 'client_id' was missing` | Faltou um campo obrigatório. O nome do campo vem na mensagem (`client_id`, `client_secret`, `redirect_uri` ou `grant_type`). | | `400` | `invalid_request` | `Required parameter code was missing` | O `code` não foi enviado. | | `400` | `unsupported_grant_type` | `The grant type requested was not recognized` | Envie `grant_type` igual a `authorization_code`. | | `401` | `invalid_client` | `Client authentication failed` | `client_id` ou `client_secret` errados, credencial de outro ambiente, ou `redirect_uri` diferente da cadastrada (até uma barra no final faz diferença). | | `401` | `unauthorized_client` | `Authorization code has expired` | Passaram mais de 10 minutos desde a autorização. Peça para o seller autorizar de novo. | | `400` | `invalid_request` | `Cannot decrypt the authorization code` | O `code` chegou incompleto ou alterado, ou é de outro ambiente. Use o valor exatamente como veio na query string. | | `500` | `server_error` | `An server error has occurred` | Erro inesperado. Também acontece ao reaproveitar um `code` que já foi trocado: peça uma nova autorização. | Na tela de autorização, a mensagem **Ocorreu uma falha na autenticação do cliente.** aparece quando o `client_id` não existe naquele ambiente, quando a `redirect_uri` não bate com a cadastrada ou quando a sua `redirect_uri` não respondeu com `2xx`. ## Checklist antes de ir para produção - [ ] Fluxo completo testado no sandbox, com as credenciais de sandbox. - [ ] `redirect_uri` de produção cadastrada, pública, com HTTPS e respondendo `2xx` em poucos segundos. - [ ] `state` aleatório por tentativa, validado e descartado depois do uso. - [ ] `client_secret` e tokens dos sellers guardados só no backend, de preferência criptografados. - [ ] Tratamento de `401` (`Token inválida!`) para quando o seller revogar o acesso. - [ ] URLs trocadas para produção: `web.superfrete.com` na autorização e `api.superfrete.com` na troca e nas chamadas. ## Próximos passos - [Emita a primeira etiqueta de ponta a ponta](https://developers.superfrete.com/guias/primeira-etiqueta.md) - [Receba os eventos dos pedidos por webhook](https://developers.superfrete.com/guias/webhooks.md) - [Veja a referência de Trocar código OAuth por token](https://developers.superfrete.com/referencia/md/exchangeOAuthToken.md) Ficou com alguma dúvida? Escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com) ou fale com a gente pelo chat (assistente de chat do site). --- # Cálculo de frete e etiquetas O caminho de um envio pela API tem quatro chamadas: 1. **Cotar** o frete: [`POST /api/v0/calculator`](https://developers.superfrete.com/referencia/md/calculateShipping.md) 2. **Criar** a etiqueta com o serviço escolhido: [`POST /api/v0/cart`](https://developers.superfrete.com/referencia/md/createShipment.md) 3. **Pagar** a etiqueta com o saldo da carteira: [`POST /api/v0/checkout`](https://developers.superfrete.com/referencia/md/checkoutShipment.md) 4. **Imprimir** a etiqueta em PDF: [`POST /api/v0/tag/print`](https://developers.superfrete.com/referencia/md/getLabelPrintUrl.md) Para conhecer os limites técnicos de cada serviço dos Correios (peso, dimensões e seguro), use [`GET /api/v0/services/info`](https://developers.superfrete.com/referencia/md/getServicesInfo.md). ## Serviços disponíveis | Código | Serviço | Transportadora | | --- | --- | --- | | `1` | PAC | Correios | | `2` | SEDEX | Correios | | `17` | Mini Envios | Correios | | `3` | Jadlog | Jadlog | | `31` | Loggi | Loggi | | `33` | J&T | J&T Express | Na cotação, envie os códigos separados por vírgula no campo `services`, por exemplo `"1,2,17"`. Na criação da etiqueta, envie um único código no campo `service`. ## Cotar: caixa pronta ou lista de produtos Você descreve o que vai ser enviado de um destes dois jeitos: - **`package`**: quando você já sabe as dimensões da caixa. Informe `weight` (kg), `height`, `width` e `length` (cm). - **`products`**: quando você tem só os produtos. Informe `quantity`, `weight` (kg), `height`, `width` e `length` (cm) de cada um, e a API calcula a caixa ideal para acomodar todos. Se você enviar os dois, `package` tem prioridade. Os opcionais vão em `options`: `own_hand` (Mão Própria), `receipt` (Aviso de Recebimento), `insurance_value` (valor declarado para o seguro) e `use_insurance_value` (envie `true` para incluir o seguro no cálculo). > **Use a caixa que a cotação devolveu** > > Quando você cota por `products`, a resposta traz a caixa ideal calculada pela API. Ao criar a etiqueta, envie **essa caixa** em `volumes`, e não as dimensões dos produtos individuais. Assim o envio sai com as mesmas medidas que foram cotadas e você evita divergências com a transportadora. > > Exemplo: dois produtos de 2 kg, com 2 × 16 × 20 cm cada. A cotação devolve uma caixa de 4 kg com 6 cm de altura, 16 cm de largura e 24 cm de comprimento. São esses os valores que vão em `volumes` na criação da etiqueta. ## Criar a etiqueta Com o serviço escolhido, envie remetente (`from`), destinatário (`to`), `service`, `volumes` e o nome da sua plataforma em `platform`. A etiqueta nasce com status `pending` (aguardando pagamento). Alguns cuidados que evitam erros de validação: - **Nome do remetente** com nome e sobrenome, até 50 caracteres. Se for uma loja com nome de uma palavra só, adicione "Loja" antes: `Loja SuperFrete`. - **`state_abbr`** com as duas letras em caixa alta: `SP`, não `sp`. - **`postal_code`** com 8 dígitos. - **`district`** é obrigatório. Se o endereço não tiver bairro, envie `"NA"`. - **`number`** é opcional. Sem número, envie uma string vazia. - **CPF ou CNPJ do destinatário** (`to.document`) é obrigatório para Jadlog, Loggi e J&T, e para os Correios sempre que houver declaração de conteúdo. - **Telefone do destinatário** (`to.phone`), quando enviado, precisa ter 10 ou 11 dígitos, sem formatação. Na J&T, se ele não vier, usamos o telefone do remetente. - **Na Loggi**, o remetente precisa de e-mail: envie `from.email` ou cadastre um e-mail na conta. ### Declaração de conteúdo ou nota fiscal - Para usar **declaração de conteúdo**, envie a lista de `products` (`name`, `quantity`, `unitary_value`). Sempre que `products` vem na requisição, a etiqueta sai com declaração de conteúdo. - Para usar **nota fiscal**, **não envie `products`** e informe `options.invoice.number` com a chave de 44 dígitos. Você ainda precisa imprimir a nota e colocar no pacote. - O campo `options.non_commercial` é ignorado e pode ser omitido. ## Pagar a etiqueta Você pode pagar de dois jeitos: - **Pelo painel**, em [Minhas etiquetas](https://web.superfrete.com/#/minhas-etiquetas), com cartão de crédito ou saldo. - **Pela API**, com o saldo da carteira: envie o `id` da etiqueta para [`POST /api/v0/checkout`](https://developers.superfrete.com/referencia/md/checkoutShipment.md). Com saldo suficiente, a etiqueta passa para `released` e está pronta para postagem. Para recarregar a carteira de produção, acesse o painel, vá em **Perfil > Carteira**, clique em **Recarregue com Pix**, escolha o valor e pague o Pix pelo seu banco. No sandbox o pagamento é simulado: veja [Adicione saldo no sandbox](https://developers.superfrete.com/guias/primeiros-passos.md#5-adicione-saldo-no-sandbox). ## Status da etiqueta | Status | Significado | | --- | --- | | `pending` | Aguardando pagamento | | `released` | Paga, aguardando postagem. O código de rastreio já está disponível | | `posted` | Postada | | `delivered` | Entregue | | `canceled` | Cancelada | O código de rastreio aparece no campo `tracking` de [Consultar pedido](https://developers.superfrete.com/referencia/md/getOrder.md) a partir do status `released`. Para não precisar consultar, assine os [webhooks](https://developers.superfrete.com/guias/webhooks.md). Só é possível [cancelar](https://developers.superfrete.com/referencia/md/cancelOrder.md) etiquetas que ainda não foram postadas. O valor volta para a carteira da conta. ## Imprimir [`POST /api/v0/tag/print`](https://developers.superfrete.com/referencia/md/getLabelPrintUrl.md) devolve a URL do PDF das etiquetas informadas. Para receber no formato **A6 (Zebra)**, ative a opção na sua conta: menu [Etiquetas](https://web.superfrete.com/#/minhas-etiquetas) > **Configurações** > **A6 (Zebra)**. A partir daí, todas as etiquetas saem nesse formato. ## Regras por transportadora Jadlog, Loggi e J&T só aparecem na cotação quando existe um ponto de postagem da transportadora perto do CEP de origem. ### Loggi A Loggi só é cotada quando as duas condições valem: o código `31` está em `services` **e** a Loggi está ativada nas configurações do token usado na requisição. Para conferir a configuração: 1. Acesse [web.superfrete.com/#/integrations](https://web.superfrete.com/#/integrations). 2. Clique em **Configurações do token** (o ícone de engrenagem). 3. Ative ou desative a Loggi. 4. Clique em **Salvar**. ![Configurações do token no painel SuperFrete, com a opção de desativar a Loggi](https://developers.superfrete.com/img/reference/calculateShipping-1.png) - Cada lado com no máximo 100 cm, e a soma dos lados até 200 cm. - Peso máximo: 30 kg com Loggi Pontos, 11 kg sem. - Seguro máximo: R$ 3.000,00. ### Jadlog | Ponto de postagem | Medidas máximas | Peso máximo | | --- | --- | --- | | Franquias (unidades oficiais da Jadlog) | 80 × 80 × 80 cm | 120 kg | | Lojas parceiras (comércios que recebem pacotes da Jadlog) | 60 × 60 × 60 cm | 30 kg | Seguro máximo: R$ 1.500,00. Nota fiscal (NF-e) não é aceita para Jadlog e Loggi: use declaração de conteúdo. ### J&T - Cada lado com no máximo 100 cm, e a soma de largura, comprimento e altura até 200 cm. - Peso máximo: 11 kg. - Telefone do destinatário obrigatório. ### Correios Os limites de PAC, SEDEX e Mini Envios (peso, dimensões, soma dos lados e faixa de seguro) são devolvidos por [`GET /api/v0/services/info`](https://developers.superfrete.com/referencia/md/getServicesInfo.md). Consulte esse endpoint em vez de fixar os valores no seu código, porque eles podem mudar. Quer ver tudo funcionando de ponta a ponta? Siga [Sua primeira etiqueta](https://developers.superfrete.com/guias/primeira-etiqueta.md). Se uma chamada falhar, as mensagens mais comuns estão em [Erros e limites](https://developers.superfrete.com/guias/erros.md). Ficou com alguma dúvida? Escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com) ou fale com a gente pelo chat (assistente de chat do site). --- # Webhooks Com webhooks, a SuperFrete avisa a sua aplicação quando algo acontece com um pedido: criado, pago, etiqueta gerada, postado, entregue ou cancelado. Você cadastra um **Webhook App** com a URL que vai receber as notificações, e nós enviamos um `POST` com os dados do evento. ## Gerenciando Webhook Apps | Ação | Endpoint | | --- | --- | | Criar | [`POST /api/v0/webhook`](https://developers.superfrete.com/referencia/md/createWebhook.md) | | Listar | [`GET /api/v0/webhook`](https://developers.superfrete.com/referencia/md/listWebhooks.md) | | Atualizar | [`PUT /api/v0/webhook/{id}`](https://developers.superfrete.com/referencia/md/updateWebhook.md) | | Remover | [`DELETE /api/v0/webhook/{id}`](https://developers.superfrete.com/referencia/md/deleteWebhook.md) | Todos exigem o mesmo token das outras chamadas, no header `Authorization: Bearer {token}`. Gere o token em [produção](https://web.superfrete.com/#/integrations) ou no [sandbox](https://sandbox.superfrete.com/#/integrations). Ao criar um Webhook App, guarde o `secret_token` da resposta: é com ele que você valida a assinatura de cada notificação. ```bash curl --request POST \ --url https://sandbox.superfrete.com/api/v0/webhook \ --header "Authorization: Bearer $SUPERFRETE_TOKEN" \ --header "User-Agent: MinhaLoja/1.0 (dev@minhaloja.com.br)" \ --header "Content-Type: application/json" \ --data '{ "name": "Minha loja", "url": "https://minhaloja.com.br/webhooks/superfrete", "events": ["order.posted", "order.delivered"] }' ``` ## Eventos | Evento | Quando é disparado | `data.status` | | --- | --- | --- | | `order.created` | A etiqueta foi criada e aguarda pagamento | `created` | | `order.released` | A etiqueta foi paga | `released` | | `order.generated` | A etiqueta foi gerada na transportadora e já tem código de rastreio | `generated` | | `order.posted` | O pacote foi postado (ou coletado) | `posted` | | `order.delivered` | O pacote foi entregue no destino | `delivered` | | `order.cancelled` | A etiqueta foi cancelada | `cancelled` | Assine só os eventos que você usa, no campo `events` do Webhook App. Um Webhook App sem eventos não recebe nada. > **cancelled ou canceled?** > > Nos webhooks, o evento e o status de cancelamento são escritos com dois "l": `order.cancelled` e `cancelled`. Na API de pedidos, o status é `canceled`, com um "l". Compare com o valor certo em cada lugar. ## Formato da notificação Cada notificação é um `POST` com estes headers: ```http Content-Type: application/json User-Agent: SuperFrete Webhooks/1.0 X-ME-Signature: 5d41402abc4b2a76b9719d911017c592... ``` E este corpo: ```json { "event": "order.delivered", "data": { "id": "r8p3dhqjn4I0tBpnvkpU", "order_id": "r8p3dhqjn4I0tBpnvkpU", "protocol": null, "status": "delivered", "tracking": "AA923452383BR", "tracking_url": "rastreio.superfrete.com/#/tracking/AA923452383BR", "self_tracking": null, "user_id": "[uid]", "tags": [{ "name": "order_id", "value": "3101481", "url": "https://minhaloja.com.br/pedidos/3101481" }], "created_at": "2024-03-29T23:49:26+00:00", "paid_at": "2024-03-29T23:50:47+00:00", "generated_at": "2024-03-29T23:51:47+00:00", "posted_at": "2024-03-29T23:55:00+00:00", "delivered_at": "2024-03-29T23:57:47+00:00", "canceled_at": null, "expired_at": null } } ``` Alguns detalhes dos campos: - `id` e `order_id` trazem o mesmo valor: o ID da etiqueta na SuperFrete, o mesmo devolvido na criação. - `tracking` vem preenchido a partir do evento `order.generated`. `tracking_url` só vem nos eventos `order.posted` e `order.delivered`. - `tags` traz as identificações que você enviou em `options.tags` ao criar a etiqueta: cada `tag` enviada volta em `value`, com `name` igual a `order_id` e a `url`, se você informou uma. - `canceled_at` é preenchido quando a etiqueta é cancelada por alguém. `expired_at`, quando o próprio sistema cancela a etiqueta, por exemplo por falta de pagamento. ## Entrega - A sua URL precisa começar com `https://` (ou `http://`), ser pública e aceitar `POST`. Endereços `localhost`, de rede interna ou atrás de login não recebem as notificações. - Responda com qualquer status `2xx` em até **10 segundos**. Se precisar de processamento pesado, confirme o recebimento primeiro e processe depois, em uma fila. - Cada evento é enviado **uma vez por pedido** para cada Webhook App. Mesmo assim, trate o recebimento como idempotente: use `event` + `data.id` para ignorar uma notificação que você já processou. - Os eventos podem chegar fora de ordem. Antes de atualizar o pedido, compare as datas (`paid_at`, `posted_at`, `delivered_at`) com o que você já tem. > **Não dependa só do webhook** > > Se a sua URL estiver fora do ar ou responder com erro quando o evento for enviado, aquela notificação pode não ser reenviada. Para não perder nenhuma mudança, rode de tempos em tempos uma conciliação: para os pedidos que ainda não foram entregues nem cancelados, consulte [`GET /api/v0/order/info/{id}`](https://developers.superfrete.com/referencia/md/getOrder.md) e atualize o status. ## Testando 1. Gere uma URL pública temporária em um serviço como [webhook.site](https://webhook.site), ou exponha o seu servidor local com uma ferramenta de túnel. 2. Cadastre um Webhook App **no sandbox** apontando para essa URL. 3. Crie e pague uma etiqueta no sandbox, seguindo [Sua primeira etiqueta](https://developers.superfrete.com/guias/primeira-etiqueta.md). Os eventos de criação, pagamento e geração chegam em poucos instantes. No sandbox, nenhum pacote é postado nem entregue de verdade, então `order.posted` e `order.delivered` normalmente não chegam por lá. ## Validando a assinatura Toda notificação traz o header `X-ME-Signature`: um HMAC-SHA256, em hexadecimal, calculado sobre o corpo da requisição com o `secret_token` do seu Webhook App. Validar essa assinatura garante que a notificação veio da SuperFrete e não foi alterada no caminho. 1. **Capture o corpo bruto** da requisição, exatamente como chegou. Não faça parse e re-serialize o JSON antes de validar: qualquer diferença, até um espaço, muda a assinatura. 2. **Calcule o HMAC-SHA256** desse corpo usando o `secret_token` como chave. 3. **Compare** o resultado com o header `X-ME-Signature` usando uma comparação de tempo constante. Se forem iguais, a notificação é válida. ```js const app = express(); // express.raw mantém o corpo exatamente como chegou, sem parse. app.post('/webhooks/superfrete', express.raw({ type: 'application/json' }), (req, res) => { const received = req.get('X-ME-Signature') ?? ''; const expected = crypto .createHmac('sha256', process.env.SUPERFRETE_WEBHOOK_SECRET) .update(req.body) .digest('hex'); const valid = received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected)); if (!valid) return res.status(401).end(); const { event, data } = JSON.parse(req.body.toString('utf8')); // Enfileire o processamento e responda logo. res.status(200).end(); }); ``` ```python from flask import Flask, abort, request app = Flask(__name__) SECRET = os.environ["SUPERFRETE_WEBHOOK_SECRET"].encode() @app.post("/webhooks/superfrete") def superfrete_webhook(): body = request.get_data() # corpo bruto, sem parse expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest() received = request.headers.get("X-ME-Signature", "") if not hmac.compare_digest(received, expected): abort(401) payload = request.get_json() # Enfileire o processamento e responda logo. return "", 200 ``` ```php **Mantenha o secret_token no servidor** > > O `secret_token` é uma chave secreta. Guarde em variável de ambiente ou cofre de segredos, nunca no front-end nem no repositório. O contrato completo da notificação, com todos os campos, está na [referência da API](https://developers.superfrete.com/referencia/md/orderEvent.md). Ficou com alguma dúvida? Escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com) ou fale com a gente pelo chat (assistente de chat do site). --- # Erros e limites Quando uma chamada falha, a API responde com um status HTTP de erro e um corpo JSON explicando o motivo. Esta página mostra os formatos possíveis, as mensagens que mais aparecem e o que fazer em cada caso. ## Formatos de erro ### Erro de validação Campos ausentes ou inválidos voltam com status `400` e o objeto `errors`, com a lista de problemas de cada campo: ```json { "message": "Ocorreu um ou mais erros.", "errors": { "to.postal_code": ["(to.postal_code) é inválido."] } } ``` Use as chaves de `errors` para destacar o campo certo na sua tela ou no seu log. A mensagem geral (`message`) é sempre a mesma; o detalhe está dentro de `errors`. ### Erro de regra de negócio ou autenticação Token inválido, saldo insuficiente, pedido que não pode mais ser cancelado e situações parecidas voltam com `message` e `error`, que trazem o mesmo texto: ```json { "message": "Sem saldo na carteira! Utilize o app para recarregar a carteira ou pagar a etiqueta com cartão de crédito.", "error": "Sem saldo na carteira! Utilize o app para recarregar a carteira ou pagar a etiqueta com cartão de crédito." } ``` ### Erro no OAuth O endpoint de troca de código por token segue o formato do OAuth 2.0, com `error`, `http_status`, `error_description` e `message`. A lista completa está no [guia do fluxo OAuth 2.0](https://developers.superfrete.com/guias/oauth.md#erros-comuns). > **Não dependa do texto da mensagem** > > As mensagens vêm em português, mas algumas poucas ainda chegam em inglês (como `user.phone is required.`) e os textos podem mudar. Trate o erro pelo status HTTP e pelo campo em `errors`, e não pelo texto exato da mensagem. ## Status HTTP | Status | Quando acontece | O que fazer | | --- | --- | --- | | `400` | Campo obrigatório ausente, valor inválido ou regra de validação não atendida. | Corrija a requisição conforme `errors`. Não adianta repetir igual. | | `401` | Token ausente, inválido, revogado ou de outro ambiente. | Confira o header `Authorization: Bearer {token}` e se o token é do ambiente certo. | | `403` | A operação não é permitida para essa conta. | Fale com o suporte se achar que deveria ter acesso. | | `404` | Webhook App inexistente ou de outra conta. Pedidos inexistentes **não** voltam `404`: a consulta e o cancelamento respondem `400`, e o checkout responde `500`. | Confira o ID enviado. | | `409` | A ação conflita com o estado atual: pedido já pago, pedido que não pode mais ser cancelado, saldo insuficiente, limite de etiquetas atingido. | Leia a mensagem e resolva a pendência antes de tentar de novo. | | `429` | Muitas requisições em pouco tempo. | Espere alguns segundos e tente de novo, com intervalo crescente entre as tentativas. | | `500`, `503`, `504` | Falha temporária do nosso lado ou de uma transportadora. | Tente de novo com intervalo crescente. Se persistir, fale com a gente informando o horário e o endpoint. | ## Mensagens mais comuns | Status | Mensagem | O que significa | | --- | --- | --- | | `401` | `Token inválida!` | O token não existe, foi revogado (a integração foi excluída no painel) ou é de outro ambiente. Gere um novo token ou refaça o OAuth. | | `401` | `Não autenticado!` | O header `Authorization` veio sem o token depois de `Bearer`. | | `400` | `user.phone is required.` | A conta SuperFrete não tem celular cadastrado. Cadastre no perfil do painel e tente de novo. | | `400` | `Cadastro incompleto. Por favor, finalizar o cadastro para prosseguir` | O cadastro da conta está incompleto. Finalize no painel. | | `400` | `Campo CPF/CNPJ do destinatário é obrigatório para seguir com geração da etiqueta.` | Envie `to.document` com o CPF ou CNPJ do destinatário. | | `400` | `O telefone do destinatário deve ter 10 ou 11 dígitos` | Envie `to.phone` só com números, com DDD. | | `400` | `Nota fiscal habilitada apenas para Correios.` | Para Jadlog, Loggi e J&T, use declaração de conteúdo (envie `products`). | | `400` | `Serviço Jadlog não está habilitado para este usuário.` | A transportadora está desativada nas configurações do token. O mesmo vale para Loggi e J&T. Veja [Regras por transportadora](https://developers.superfrete.com/guias/frete.md#regras-por-transportadora). | | `400` | `Essa modalidade do Correios não está habilitada.` | O serviço dos Correios pedido não está disponível para a conta. | | `409` | `Sem saldo na carteira! …` | O saldo da carteira não cobre a etiqueta. Recarregue e chame o checkout de novo. | | `409` | `Não foi possível realizar a compra pois o limite de etiquetas disponível para esta conta foi atingido. …` | A conta atingiu o limite de etiquetas aguardando postagem. Veja abaixo. | | `409` | `Pedido já comprado.` | A etiqueta já foi paga. Não chame o checkout de novo. | | `409` | `Este pedido está cancelado e não pode ser pago. …` | A etiqueta foi cancelada. Crie uma nova. | | `409` | `Esse pedido não pode mais ser cancelado!` | A etiqueta já foi postada ou concluída e não pode mais ser cancelada. | | `404` | `Aplicação Webhook não encontrada ou permissões insuficientes para acesso` | O ID do Webhook App não existe ou é de outra conta. | ## Limites ### Etiquetas aguardando postagem Cada conta tem um limite de etiquetas pagas que ainda não foram postadas. Quando ele é atingido, o checkout responde `409` com a mensagem "Não foi possível realizar a compra pois o limite de etiquetas disponível para esta conta foi atingido…". Consulte a situação em [`GET /api/v0/user`](https://developers.superfrete.com/referencia/md/getUser.md), no objeto `limits`: ```json { "limits": { "shipments": 48, "shipments_available": 2 } } ``` - `shipments`: etiquetas aguardando postagem. - `shipments_available`: quantas ainda cabem no limite. Para liberar espaço, poste os pacotes pendentes ou cancele as etiquetas que não vai usar. Se o volume da sua operação precisa de um limite maior, escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com). ### Requisições Não há um limite de requisições por minuto publicado. Mesmo assim, evite rajadas desnecessárias: - **Guarde a cotação** do carrinho por alguns minutos em vez de cotar de novo a cada clique. - **Use webhooks** para saber quando o status muda, em vez de consultar o pedido em loop. - **Tente de novo com intervalo crescente** (1 s, 2 s, 4 s...) quando receber `429` ou `5xx`. ### Outros limites | O quê | Limite | | --- | --- | | Resposta da sua URL de webhook | 10 segundos para responder `2xx` | | Validade do `code` do OAuth | 10 minutos, uso único | | Validade do token de acesso | Não expira. Deixa de valer se a integração for excluída no painel | | Peso, medidas e seguro dos Correios | Devolvidos por [`GET /api/v0/services/info`](https://developers.superfrete.com/referencia/md/getServicesInfo.md) | | Jadlog, Loggi e J&T | Veja [Regras por transportadora](https://developers.superfrete.com/guias/frete.md#regras-por-transportadora) | ## Quando falar com a gente Se o erro não estiver aqui ou continuar depois da correção, escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com) com: - o endpoint e o ambiente (sandbox ou produção); - o horário aproximado da chamada; - o corpo da requisição, **sem o token**; - a resposta completa que você recebeu. Ou fale com a gente pelo chat (assistente de chat do site). --- # Perguntas frequentes As dúvidas que mais aparecem de quem integra com a SuperFrete. Não achou a sua? fale com a gente pelo chat (assistente de chat do site) ou escreva para [integracao@superfrete.com](mailto:integracao@superfrete.com). ## Conta e token ### Onde eu gero o meu token? No painel da SuperFrete, no menu **Integrações**. Clique em **Nova integração**, escolha a opção da **API** e clique em **Gerar token**. O token aparece na tela seguinte, com um botão para copiar. Cada ambiente tem o seu painel e o seu token: [sandbox.superfrete.com](https://sandbox.superfrete.com/#/integrations) para testes e [web.superfrete.com](https://web.superfrete.com/#/integrations) para produção. O passo a passo com imagens está em [Primeiros passos](https://developers.superfrete.com/guias/primeiros-passos.md#2-gere-o-seu-token). ### Perdi o token. Consigo ver de novo? Sim. Em **Integrações**, clique na integração da API na lista de integrações conectadas: o token aparece de novo, pronto para copiar. Isso vale para tokens gerados no painel. Tokens obtidos por OAuth ficam só com a plataforma que fez a integração. ### O token expira? Não. O token manual e o token obtido por OAuth não têm data de validade. Ele só deixa de funcionar se a integração for excluída em **Integrações** no painel. Nesse caso, a API responde `401` com `Token inválida!` e é preciso gerar um token novo. ### Devo usar token manual ou OAuth? Se você está integrando **a sua própria loja** (site ou sistema próprio), use o token manual. Se você tem uma **plataforma com várias lojas** (e-commerce, ERP, hub), use OAuth, para que cada seller conecte a própria conta sem copiar token. A comparação completa está em [Autenticação](https://developers.superfrete.com/guias/autenticacao.md). ### Recebo `401` com `Token inválida!` mesmo com o token certo. O que pode ser? Os motivos mais comuns, na ordem: 1. **Ambiente trocado.** Token de sandbox só funciona em `https://sandbox.superfrete.com`, e o de produção só em `https://api.superfrete.com`. 2. **Header mal formado.** O formato é `Authorization: Bearer {token}`, com a palavra `Bearer`, um espaço e o token, sem aspas. 3. **Integração excluída.** Se alguém excluiu a integração no painel, o token deixou de valer. 4. **Token cortado** ao copiar. Copie de novo pelo botão de copiar do painel. ### Qual User-Agent eu devo enviar? O nome e a versão da sua aplicação, seguidos de um e-mail de contato técnico entre parênteses: `MinhaLoja/1.0 (dev@minhaloja.com.br)`. Usamos esse e-mail para avisar sobre mudanças e problemas na sua integração. Envie em todas as requisições. ## Sandbox e produção ### Qual a diferença entre sandbox e produção? O sandbox é um ambiente de testes completo, com painel, carteira e API próprios. Etiquetas emitidas lá **não têm validade** nas transportadoras e o pagamento é simulado. | | Sandbox | Produção | | --- | --- | --- | | API | `https://sandbox.superfrete.com` | `https://api.superfrete.com` | | Painel | [sandbox.superfrete.com](https://sandbox.superfrete.com) | [web.superfrete.com](https://web.superfrete.com) | Conta, token, saldo e Webhook Apps são separados em cada ambiente. ### Como coloco saldo na carteira do sandbox? Pelo painel do sandbox, em [Carteira](https://sandbox.superfrete.com/#/account/credits): clique em **Recarregue com Pix**, escolha um valor, copie o código Pix e cole na barra de endereço do navegador. O pagamento é simulado e o crédito aparece na hora. O passo a passo está em [Primeiros passos](https://developers.superfrete.com/guias/primeiros-passos.md#5-adicione-saldo-no-sandbox). ### A integração tem custo? Não. Usar a API é gratuito e não tem mensalidade. Você paga só as etiquetas que emitir, com o mesmo preço do painel. ## Cotação e serviços ### Quais serviços de entrega estão disponíveis? | Código | Serviço | | --- | --- | | `1` | PAC (Correios) | | `2` | SEDEX (Correios) | | `17` | Mini Envios (Correios) | | `3` | Jadlog | | `31` | Loggi | | `33` | J&T Express | Na cotação, envie vários códigos separados por vírgula em `services`. Jadlog, Loggi e J&T só aparecem quando há um ponto de postagem da transportadora perto do CEP de origem e quando estão ativas nas configurações do token. Veja [Regras por transportadora](https://developers.superfrete.com/guias/frete.md#regras-por-transportadora). ### Por que uma transportadora não aparece na minha cotação? Confira três coisas: 1. **A transportadora está ativa no token?** Em **Integrações**, abra as configurações da integração e veja se Jadlog, Loggi ou J&T estão ligadas, com ponto de postagem escolhido. 2. **Existe ponto de postagem perto da origem?** Sem ponto próximo, a transportadora não entra na cotação. 3. **O pacote cabe nos limites dela?** Cada transportadora tem peso e medidas máximas. Quando o pacote passa do limite, o serviço volta com `has_error: true` e o motivo em `error`. ### Qual a dimensão mínima de um pacote? Nos serviços dos Correios, medidas menores que o mínimo são ajustadas automaticamente para o mínimo do formato, então você não recebe erro por pacote pequeno demais. Para uma caixa comum, o mínimo é 16 cm de largura, 24 cm de comprimento, 4 cm de altura e 0,3 kg. Os limites atualizados de cada serviço, mínimos e máximos, estão em [`GET /api/v0/services/info`](https://developers.superfrete.com/referencia/md/getServicesInfo.md). ### Devo cotar com a caixa ou com os produtos? Se você já sabe a caixa em que o pedido vai ser enviado, cote com `package`. Se só conhece os produtos, cote com `products` e a API calcula a menor caixa que acomoda todos. Nesse caso, **use a caixa devolvida pela cotação em `volumes` ao criar a etiqueta**, para que a etiqueta saia com as mesmas medidas que foram cotadas. Se enviar os dois, `package` tem prioridade. ### O CEP pode ir com traço? Pode. A API remove pontos, traços e espaços de `from.postal_code` e `to.postal_code` antes de calcular. O importante é que sobrem 8 dígitos. ## Etiquetas, pagamento e cancelamento ### Como pago uma etiqueta pela API? Com o saldo da carteira, chamando [`POST /api/v0/checkout`](https://developers.superfrete.com/referencia/md/checkoutShipment.md) com o ID da etiqueta. Se o saldo não cobrir o valor, a API responde `409` com `Sem saldo na carteira!`. Também é possível pagar pelo painel, em **Etiquetas**, com cartão de crédito ou saldo. Para recarregar a carteira de produção, vá em **Perfil > Carteira** no painel, clique em **Recarregue com Pix**, escolha o valor e pague o Pix no app do seu banco. No sandbox, o Pix é simulado. ### O checkout diz que o limite de etiquetas foi atingido. O que fazer? A API responde `409` com a mensagem "Não foi possível realizar a compra pois o limite de etiquetas disponível para esta conta foi atingido…": a conta chegou ao limite de etiquetas pagas aguardando postagem. Consulte `limits.shipments_available` em [`GET /api/v0/user`](https://developers.superfrete.com/referencia/md/getUser.md) para acompanhar. Para liberar espaço, poste os pacotes pendentes ou cancele as etiquetas que não vai usar. Se a sua operação precisa de um limite maior, fale com [integracao@superfrete.com](mailto:integracao@superfrete.com). ### Recebo `user.phone is required.` ao criar a etiqueta: por quê? A conta SuperFrete dona do token não tem um número de celular cadastrado. Cadastre o celular no perfil do painel (do mesmo ambiente do token) e tente de novo. ### Posso cancelar uma etiqueta? Sim, enquanto ela **não tiver sido postada**. Chame [`POST /api/v0/order/cancel`](https://developers.superfrete.com/referencia/md/cancelOrder.md) com o ID e o motivo. O valor pago volta para a carteira da conta. Se a etiqueta já foi postada ou concluída, a API responde `409` com `Esse pedido não pode mais ser cancelado!`. ### Quando o código de rastreio fica disponível? A partir do momento em que a etiqueta é paga (status `released`). Ele aparece no campo `tracking` de [Consultar pedido](https://developers.superfrete.com/referencia/md/getOrder.md) e no evento `order.generated` dos webhooks. ### Posso usar nota fiscal em vez de declaração de conteúdo? Nos Correios, sim: **não envie `products`** e informe a chave de 44 dígitos em `options.invoice.number`. Sempre que `products` vem na requisição, a etiqueta sai com declaração de conteúdo, mesmo com nota fiscal. A nota impressa ainda precisa ir dentro do pacote. Para Jadlog, Loggi e J&T, use declaração de conteúdo (envie `products`). ### Como imprimo a etiqueta no formato A6 (impressora térmica)? Ative uma vez na conta: no painel, vá em **Etiquetas > Configurações** e ative **A6 (Zebra)**. A partir daí, a URL devolvida por [`POST /api/v0/tag/print`](https://developers.superfrete.com/referencia/md/getLabelPrintUrl.md) traz as etiquetas nesse formato. ## Webhooks ### Os webhooks não estão chegando. O que eu verifico? 1. **O evento está assinado?** Liste os seus Webhook Apps com [`GET /api/v0/webhook`](https://developers.superfrete.com/referencia/md/listWebhooks.md) e confira o array `events`. Só chegam os eventos que estão nele. 2. **A URL é pública e aceita `POST`?** Endereços `localhost`, de rede interna ou atrás de login não recebem as notificações. 3. **A sua URL responde em até 10 segundos com `2xx`?** Respostas lentas ou com erro contam como falha. 4. **O ambiente é o mesmo?** O Webhook App do sandbox só recebe eventos do sandbox, e o de produção só de produção. 5. **No sandbox**, os pacotes não são postados de verdade, então `order.posted` e `order.delivered` normalmente não chegam. ### Como sei que a notificação veio mesmo da SuperFrete? Toda notificação traz o header `X-ME-Signature`, um HMAC-SHA256 em hexadecimal do corpo da requisição, assinado com o `secret_token` do seu Webhook App. Calcule o mesmo HMAC sobre o corpo bruto e compare. Os exemplos em Node.js, Python e PHP estão no [guia de webhooks](https://developers.superfrete.com/guias/webhooks.md#validando-a-assinatura). ### Posso receber o mesmo evento duas vezes? Cada evento é enviado uma vez por pedido para cada Webhook App. Mesmo assim, trate o recebimento como idempotente: use o par `event` + `data.id` para ignorar uma notificação que você já processou. ## Ajuda ### Com quem eu falo sobre a integração? Dúvidas técnicas sobre a API: [integracao@superfrete.com](mailto:integracao@superfrete.com). Respostas rápidas sobre esta documentação: chat da SuperFrete (assistente de chat do site), no canto da tela. Ao escrever, mande o endpoint, o ambiente, o horário da chamada, o corpo da requisição (sem o token) e a resposta completa. Isso encurta muito a conversa. --- # API SuperFrete - **OpenAPI Version:** `3.1.0` - **API Version:** `0` - **Terms of service:**  - **Contact:** Time de Integrações SuperFrete API REST da SuperFrete para cotar fretes, criar e pagar etiquetas, acompanhar pedidos e receber eventos por webhook. - **Sandbox:** `https://sandbox.superfrete.com`. Use para testar; etiquetas geradas aqui não valem para postagem. - **Produção:** `https://api.superfrete.com`. Toda requisição autenticada leva o header `Authorization: Bearer {token}`. Envie também um `User-Agent` com o nome da sua aplicação e um e-mail de contato técnico, no formato `MinhaLoja (dev@minhaloja.com.br)`. Erros de validação voltam com status `400` e o corpo `{ "message": "Ocorreu um ou mais erros.", "errors": { "campo": ["mensagem"] } }`. Erros de regra de negócio voltam com `{ "message": "...", "error": "..." }`, as duas chaves com o mesmo texto. Guias, exemplos e perguntas frequentes: [developers.superfrete.com](https://developers.superfrete.com). Dúvidas: . ## Contents **Operations** - [POST /api/v0/oauth/token](#scalar-operation-post-apiv0oauthtoken) - [POST /api/v0/calculator](#scalar-operation-post-apiv0calculator) - [GET /api/v0/services/info](#scalar-operation-get-apiv0servicesinfo) - [POST /api/v0/cart](#scalar-operation-post-apiv0cart) - [POST /api/v0/checkout](#scalar-operation-post-apiv0checkout) - [POST /api/v0/tag/print](#scalar-operation-post-apiv0tagprint) - [GET /api/v0/order/info/{id}](#scalar-operation-get-apiv0orderinfoid) - [GET /api/v0/me/orders](#scalar-operation-get-apiv0meorders) - [POST /api/v0/order/cancel](#scalar-operation-post-apiv0ordercancel) - [GET /api/v0/user](#scalar-operation-get-apiv0user) - [GET /api/v0/user/addresses](#scalar-operation-get-apiv0useraddresses) - [POST /api/v0/webhook](#scalar-operation-post-apiv0webhook) - [GET /api/v0/webhook](#scalar-operation-get-apiv0webhook) - [PUT /api/v0/webhook/{id}](#scalar-operation-put-apiv0webhookid) - [DELETE /api/v0/webhook/{id}](#scalar-operation-delete-apiv0webhookid) **Webhooks** - [POST orderEvent](#scalar-webhook-post-orderevent) - [POST trackingUpdated](#scalar-webhook-post-trackingupdated) **Schemas** - [Error](#scalar-schema-error) - [ValidationError](#scalar-schema-validationerror) - [UserAgentError](#scalar-schema-useragenterror) - [OAuthError](#scalar-schema-oautherror) - [OAuthTokenRequest](#scalar-schema-oauthtokenrequest) - [PostalCode](#scalar-schema-postalcode) - [PackageDimensions](#scalar-schema-packagedimensions) - [ServiceId](#scalar-schema-serviceid) - [ShippingQuote](#scalar-schema-shippingquote) - [ShippingQuoteError](#scalar-schema-shippingquoteerror) - [QuoteCompany](#scalar-schema-quotecompany) - [ServiceInfo](#scalar-schema-serviceinfo) - [MinMax](#scalar-schema-minmax) - [ShipmentSender](#scalar-schema-shipmentsender) - [ShipmentRecipient](#scalar-schema-shipmentrecipient) - [StateAbbr](#scalar-schema-stateabbr) - [OrderStatus](#scalar-schema-orderstatus) - [OrderTags](#scalar-schema-ordertags) - [OrderAddress](#scalar-schema-orderaddress) - [Order](#scalar-schema-order) - [OrderListAddress](#scalar-schema-orderlistaddress) - [OrderListItem](#scalar-schema-orderlistitem) - [UserAddress](#scalar-schema-useraddress) - [FirestoreTimestamp](#scalar-schema-firestoretimestamp) - [WebhookEvent](#scalar-schema-webhookevent) - [OrderWebhookPayload](#scalar-schema-orderwebhookpayload) - [TrackingWebhookPayload](#scalar-schema-trackingwebhookpayload) ## Servers - **URL:** `https://sandbox.superfrete.com` - **Description:** Sandbox (testes, etiquetas sem validade) - **URL:** `https://api.superfrete.com` - **Description:** Produção ## Authentication - **bearerAuth**: HTTP bearer (`JWT`) Token de acesso da SuperFrete, no header `Authorization: Bearer {token}`. - **Integração da própria loja:** gere o token no painel, em **Integrações** ([produção](https://web.superfrete.com/#/integrations) ou [sandbox](https://sandbox.superfrete.com/#/integrations)). - **Plataforma que conecta várias contas:** obtenha um token por seller com o fluxo OAuth 2.0 ([`exchangeOAuthToken`](https://developers.superfrete.com/referencia/md/exchangeOAuthToken.md)). O token não expira e dá acesso a todas as rotas da API para a conta; não há escopos. Ele deixa de valer quando a integração é removida no painel. O token de sandbox só funciona no sandbox, e o de produção só em produção. ## Tags ### Autenticação Fluxo OAuth 2.0 para plataformas que conectam várias contas SuperFrete. Quem integra a própria loja usa o token gerado no painel e não precisa deste fluxo. ### Frete Cotação de frete e regras de cada serviço (Correios, Jadlog, Loggi e J\&T). ### Etiquetas Criação, pagamento e impressão de etiquetas. ### Pedidos Consulta, listagem e cancelamento dos pedidos (etiquetas) da conta. ### Usuário Dados da conta autenticada: perfil, saldo, limites e endereços. ### Webhooks Cadastro dos Webhook Apps que recebem eventos dos pedidos e do rastreio. ## Operations ### Trocar código OAuth por token - **Method:** `POST` - **Path:** `/api/v0/oauth/token` - **Operation ID:** `exchangeOAuthToken` - **Tags:** Autenticação O fluxo OAuth tem duas etapas: o seller autoriza a sua aplicação no navegador e você recebe um **código de autorização**; depois o seu backend troca esse código por um **token de acesso** nesta rota. Para usar o fluxo, peça ao time de integrações o `client_id` e o `client_secret` da sua aplicação e informe a `redirect_uri` que vai receber o código. #### Passo 1: pedir a autorização do seller Abra no navegador do seller a página de autorização do ambiente: - **Sandbox:** `https://sandbox.superfrete.com/#/auth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&state={state}` - **Produção:** `https://web.superfrete.com/#/auth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&state={state}` | Parâmetro | Obrigatório | Descrição | | -------------- | ----------- | ------------------------------------------------------------------------------------------------------ | | `client_id` | Sim | Identificador numérico da sua aplicação. | | `redirect_uri` | Sim | URL de retorno. Precisa começar com a URL cadastrada na SuperFrete. | | `state` | Não | Valor aleatório gerado por você. Volta igual no retorno; confira do seu lado para se proteger de CSRF. | Quando o seller clica em **Autorizar**, a SuperFrete faz um `GET` **do servidor dela** para a URL cadastrada com `?code={code}&state={state}` e, em seguida, leva o navegador do seller para o mesmo endereço. A sua URL precisa responder `2xx` a esse `GET` servidor a servidor; se ela falhar, a autorização falha. > **Atenção** > > O `code` vale por **10 minutos** e só pode ser trocado uma vez. Ele não é o token de acesso. #### Passo 2: trocar o código pelo token Chame esta rota a partir do seu backend, nunca do navegador, porque ela leva o `client_secret`. O corpo pode ir em JSON ou em `application/x-www-form-urlencoded`. A `redirect_uri` precisa ser **idêntica** à cadastrada. O token devolvido **não expira** e não há refresh token. Ele deixa de valer quando o seller remove a integração no painel. O token dá acesso a todas as rotas desta API para a conta do seller; não existem escopos. **Servers:** [Inherited servers](#scalar-context-global-servers) #### Authentication No authentication required. #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` [OAuthTokenRequest](#scalar-schema-oauthtokenrequest) **Example:** ```json { "grant_type": "authorization_code", "client_id": "1700000000", "client_secret": "9f86d081884c7d659a2feaa0c55ad015", "code": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIza0NLR3F6bFBGZkNFajJYMVRrb2IwN1NOMk0yIiwiaWF0IjoxNzMwMjk0MDAwLCJleHAiOjE3MzAyOTQ2MDB9.c2lnbmF0dXJl", "state": "a1b2c3d4", "redirect_uri": "https://minhaplataforma.com.br/superfrete/callback" } ``` **Content type:** `application/x-www-form-urlencoded` [OAuthTokenRequest](#scalar-schema-oauthtokenrequest) **Generated example:** ```json { "grant_type": "authorization_code", "client_id": "", "client_secret": "", "redirect_uri": "", "code": "", "state": "" } ``` #### Responses ##### 200 Token emitido. **Content type:** `application/json` - **`access_token` (required)**: `string` Token de acesso. Use no header `Authorization: Bearer {access_token}`. - **`expires_in` (required)**: `string`, possible values: `"never"` Sempre `never`. O token não expira. - **`token_type` (required)**: `string`, possible values: `"Bearer"` **Example:** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIza0NLR3F6bFBGZkNFajJYMVRrb2IwN1NOMk0yIiwiaWF0IjoxNzMwMjk0MTIwfQ.c2lnbmF0dXJl", "expires_in": "never", "token_type": "Bearer" } ``` ##### 400 Parâmetro ausente ou inválido. Os valores de `error` possíveis são: - `invalid_request`: falta `grant_type`, `client_id`, `client_secret`, `redirect_uri` ou `code`, ou o `code` não pôde ser lido. - `unsupported_grant_type`: `grant_type` diferente de `authorization_code`. **Content type:** `application/json` [OAuthError](#scalar-schema-oautherror) **Example: missingParameter** Parâmetro ausente ```json { "error": "invalid_request", "http_status": 400, "error_description": "Required parameter 'client_id' was missing", "message": "Required parameter 'client_id' was missing" } ``` **Example: unsupportedGrantType** grant_type não suportado ```json { "error": "unsupported_grant_type", "http_status": 400, "error_description": "The grant type requested was not recognized", "message": "The grant type requested was not recognized" } ``` ##### 401 Credenciais ou código recusados. Os valores de `error` possíveis são: - `invalid_client`: `client_id`, `client_secret` ou `redirect_uri` não conferem com o cadastro. - `unauthorized_client`: o `code` passou dos 10 minutos de validade. **Content type:** `application/json` [OAuthError](#scalar-schema-oautherror) **Example: invalidClient** Credenciais do cliente inválidas ```json { "error": "invalid_client", "http_status": 401, "error_description": "Client authentication failed", "message": "Client authentication failed" } ``` **Example: expiredCode** Código expirado ```json { "error": "unauthorized_client", "http_status": 401, "error_description": "Authorization code has expired", "message": "Authorization code has expired" } ``` ##### 500 Erro interno. Também acontece quando o mesmo `code` é trocado pela segunda vez. **Content type:** `application/json` [OAuthError](#scalar-schema-oautherror) **Example:** ```json { "error": "server_error", "http_status": 500, "error_description": "An server error has occurred", "message": "An server error has occurred" } ``` ### Cotar frete - **Method:** `POST` - **Path:** `/api/v0/calculator` - **Operation ID:** `calculateShipping` - **Tags:** Frete Calcula preço e prazo de entrega a partir do CEP de origem, do CEP de destino e da caixa ou dos produtos. A resposta traz uma opção por serviço disponível, ordenada pelo código do serviço. #### Caixa pronta ou lista de produtos Descreva a encomenda de um destes dois jeitos: - **`package`**: dimensões (cm) e peso (kg) da caixa, quando você já sabe quais são. - **`products`**: dimensões, peso e quantidade de cada produto. A API calcula uma caixa que acomode todos e devolve essa caixa em `packages`. Se você enviar os dois, `package` tem prioridade. > **Use a caixa devolvida pela cotação** > > Ao cotar por `products`, envie a caixa calculada pela API em `volumes` quando for [criar a etiqueta](https://developers.superfrete.com/referencia/md/createShipment.md), e não as dimensões dos produtos. Exemplo: dois produtos de 2 kg com 2 × 16 × 20 cm geram uma caixa de 4 kg com 6 × 16 × 24 cm. São esses os valores da etiqueta. #### Serviços Informe em `services` os códigos separados por vírgula: `1` PAC, `2` SEDEX, `17` Mini Envios, `3` Jadlog, `31` Loggi e `33` J\&T. Exemplo: `"1,2,17,3,31"`. Um serviço só é cotado quando o código está em `services` **e** está ligado nas configurações do token, em [Integrações](https://web.superfrete.com/#/integrations). Os Correios, a Jadlog e a Loggi vêm ligados; a J\&T vem desligada e precisa ser ativada para a conta. Jadlog, Loggi e J\&T só aparecem quando há um ponto de postagem da transportadora perto do CEP de origem. #### Serviços adicionais e seguro `own_hand` (mão própria) e `receipt` (aviso de recebimento) valem para os Correios. O seguro usa `insurance_value` quando `use_insurance_value` é `true`; se `use_insurance_value` não for enviado, basta `insurance_value` maior que zero. As configurações do token podem ligar esses serviços para todas as cotações. #### Limites por transportadora | Transportadora | Medidas máximas | Peso máximo | | -------------- | ------------------------------------------ | ----------------------------------------------- | | Loggi | 100 cm por lado, soma dos lados até 200 cm | 30 kg com Loggi Pontos, 11 kg sem | | Jadlog | 80 × 80 × 80 cm | 120 kg nas franquias, 30 kg nas lojas parceiras | | J\&T | 100 cm por lado, soma dos lados até 200 cm | 11 kg | Os limites dos Correios (PAC, SEDEX e Mini Envios) vêm de [Consultar regras dos serviços](https://developers.superfrete.com/referencia/md/getServicesInfo.md). O passo a passo completo, da cotação à impressão, está no [guia de frete](https://developers.superfrete.com/guias/frete). **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` **Any of:** - `object` **Required fields:** `package` - `object` **Required fields:** `products` * **`from` (required)**: `object` Origem. - **`postal_code` (required)**: `string` CEP com 8 dígitos. Pontos e traços são removidos. * **`services` (required)**: `string | (array of string)` Códigos dos serviços a cotar, separados por vírgula. `1` PAC · `2` SEDEX · `17` Mini Envios · `3` Jadlog · `31` Loggi · `33` J\&T. Também aceita um array de códigos. * **`to` (required)**: `object` Destino. - **`postal_code` (required)**: `string` CEP com 8 dígitos. Pontos e traços são removidos. * **`options`**: `object` Serviços adicionais. - **`insurance_value`**: `number` Valor da mercadoria, em reais, para o seguro. - **`own_hand`**: `boolean`, default: `false` Mão própria (Correios). - **`receipt`**: `boolean`, default: `false` Aviso de recebimento (Correios). - **`use_insurance_value`**: `boolean` Contrata o seguro com `insurance_value`. Sem este campo, o seguro é contratado sempre que `insurance_value` for maior que zero. * **`package`**: [PackageDimensions](#scalar-schema-packagedimensions) Dimensões da caixa já conhecida. Tem prioridade sobre `products`. * **`products`**: `array` Produtos da encomenda. A API calcula uma caixa que acomode todos. **Items:** - **`height` (required)**: `number` Altura, em cm. - **`length` (required)**: `number` Comprimento, em cm. - **`quantity` (required)**: `integer`, minimum: `1` - **`weight` (required)**: `number` Peso, em kg. - **`width` (required)**: `number` Largura, em cm. **Example: byPackage** Cotação com as dimensões da caixa ```json { "from": { "postal_code": "01153000" }, "to": { "postal_code": "20020050" }, "services": "1,2,17", "options": { "own_hand": false, "receipt": false, "insurance_value": 0, "use_insurance_value": false }, "package": { "height": 2, "width": 11, "length": 16, "weight": 0.3 } } ``` **Example: byProducts** Cotação a partir dos produtos ```json { "from": { "postal_code": "01153000" }, "to": { "postal_code": "20020050" }, "services": "1,2,17,3,31", "options": { "own_hand": false, "receipt": false, "insurance_value": 150, "use_insurance_value": true }, "products": [ { "quantity": 2, "height": 2, "width": 16, "length": 20, "weight": 2 } ] } ``` #### Responses ##### 200 Uma opção por serviço. Quando um serviço não pode ser usado nessa encomenda (por exemplo, seguro acima do limite), ele volta com o campo `error` e sem preço. **Content type:** `application/json` array of ([ShippingQuote](#scalar-schema-shippingquote) | [ShippingQuoteError](#scalar-schema-shippingquoteerror)) **Example:** ```json [ { "id": 1, "name": "PAC", "price": 18.61, "discount": "5.59", "currency": "R$", "delivery_time": 5, "delivery_range": { "min": 5, "max": 5 }, "packages": [ { "price": 18.61, "discount": "5.59", "format": "box", "dimensions": { "height": "2", "width": "11", "length": "16" }, "weight": "0.3", "insurance_value": 0 } ], "additional_services": { "receipt": false, "own_hand": false }, "company": { "id": 1, "name": "Correios", "picture": "https://storage.googleapis.com/sandbox-api-superfrete.appspot.com/logos/correios.png" }, "has_error": false }, { "id": 2, "name": "SEDEX", "price": 10.77, "discount": "13.43", "currency": "R$", "delivery_time": 1, "delivery_range": { "min": 1, "max": 1 }, "packages": [ { "price": 10.77, "discount": "13.43", "format": "box", "dimensions": { "height": "2", "width": "11", "length": "16" }, "weight": "0.3", "insurance_value": 0 } ], "additional_services": { "receipt": false, "own_hand": false }, "company": { "id": 1, "name": "Correios", "picture": "https://storage.googleapis.com/sandbox-api-superfrete.appspot.com/logos/correios.png" }, "has_error": false }, { "id": 3, "name": "JADLOG.PACKAGE", "price": 14.4, "discount": "7.2", "currency": "R$", "delivery_time": 2, "delivery_range": { "min": 2, "max": 3 }, "packages": [ { "price": 14.4, "discount": "7.2", "format": "box", "dimensions": { "height": "2", "width": "11", "length": "16" }, "weight": "0.3", "insurance_value": 0 } ], "additional_services": { "receipt": false, "own_hand": false }, "company": { "id": 2, "name": "jadlog", "picture": "" }, "has_error": false }, { "id": 31, "name": "LOGGI Econômico", "error": "Valor segurado ultrapassa o limite máximo de R$ 3000,00", "company": { "id": 14, "name": "loggi", "picture": "" } } ] ``` ##### 400 Requisição inválida ou nenhum serviço disponível para a encomenda. Quando nenhum serviço pôde ser cotado, a chave do erro é `freight.calculator.no_result`; quando a caixa passa dos limites das transportadoras parceiras, é `package_dimensions_sum_or_weight`. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) Um ou mais campos da requisição são inválidos. **Example: noResult** Nenhum serviço disponível ```json { "message": "Ocorreu um ou mais erros.", "errors": { "freight.calculator.no_result": [ "Nenhum frete válido encontrado para esse serviço." ] } } ``` **Example: missingField** Campo obrigatório ausente ```json { "message": "Ocorreu um ou mais erros.", "errors": { "to.postal_code": [ "(to.postal_code) é obrigatório." ] } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` **Generated example:** ```json { "message": "", "error": "" } ``` ### Consultar regras dos serviços - **Method:** `GET` - **Path:** `/api/v0/services/info` - **Operation ID:** `getServicesInfo` - **Tags:** Frete Devolve as regras de cada serviço disponível para a conta, indexadas pelo código do serviço: `1` PAC, `2` SEDEX, `17` Mini Envios, `3` Jadlog e `31` Loggi. O `33` (J\&T) só aparece quando a J\&T está liberada para a conta. Para os Correios, `restrictions` traz os limites de seguro (`insurance_value`, em reais) e da caixa (`formats.package`: peso em kg, largura, altura e comprimento em cm, e `sum`, a soma máxima dos lados). `requirements` lista os dados obrigatórios da etiqueta e `optionals` os serviços adicionais aceitos: `AR` (aviso de recebimento), `MP` (mão própria) e `VD` (valor declarado). Para Jadlog, Loggi e J\&T, `restrictions` vem vazio. Os limites dessas transportadoras estão em [Cotar frete](https://developers.superfrete.com/referencia/md/calculateShipping.md). **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 200 Regras de cada serviço, indexadas pelo código. **Content type:** `application/json` `object` **Additional properties:** [ServiceInfo](#scalar-schema-serviceinfo) **Example:** ```json { "1": { "name": "PAC", "type": "express", "range": "interstate", "restrictions": { "insurance_value": { "min": 25.63, "max": 38057.59 }, "formats": { "package": { "weight": { "min": 0.3, "max": 30 }, "width": { "min": 16, "max": 150 }, "height": { "min": 4, "max": 150 }, "length": { "min": 24, "max": 150 }, "sum": 200 } } }, "requirements": [ "names", "addresses" ], "optionals": [ "AR", "MP", "VD" ], "company": { "name": "Correios", "picture": "https://firebasestorage.googleapis.com/v0/b/freight-calculator-8d6c1.appspot.com/o/images%2Ftransporters%2Flogo-correios-transporter.png?alt=media&token=8b0aa041-d5c6-4a46-9f06-d9b7e38e26d4" } }, "2": { "name": "SEDEX", "type": "express", "range": "interstate", "restrictions": { "insurance_value": { "min": 25.63, "max": 38057.59 }, "formats": { "package": { "weight": { "min": 0.3, "max": 30 }, "width": { "min": 16, "max": 150 }, "height": { "min": 4, "max": 150 }, "length": { "min": 24, "max": 150 }, "sum": 200 } } }, "requirements": [ "names", "addresses" ], "optionals": [ "AR", "MP", "VD" ], "company": { "name": "Correios", "picture": "https://firebasestorage.googleapis.com/v0/b/freight-calculator-8d6c1.appspot.com/o/images%2Ftransporters%2Flogo-correios-transporter.png?alt=media&token=8b0aa041-d5c6-4a46-9f06-d9b7e38e26d4" } }, "3": { "name": "Jadlog", "type": "express", "range": "interstate", "restrictions": {}, "company": { "name": "Jadlog", "picture": "https://firebasestorage.googleapis.com/v0/b/freight-calculator-8d6c1.appspot.com/o/images%2Ftransporters%2Flogo-jadlog-transporter.png?alt=media&token=491fb210-ff10-4821-9c12-564f032ace8c" } }, "17": { "name": "MiniEnvios", "type": "express", "range": "interstate", "restrictions": { "insurance_value": { "min": 12.25, "max": 104.62 }, "formats": { "package": { "weight": { "min": 0.0001, "max": 0.3 }, "width": { "min": 10, "max": 16 }, "height": { "min": 1, "max": 4 }, "length": { "min": 15, "max": 24 }, "sum": 200 } } }, "requirements": [ "names", "addresses" ], "optionals": [ "AR", "MP", "VD" ], "company": { "name": "Correios", "picture": "https://firebasestorage.googleapis.com/v0/b/freight-calculator-8d6c1.appspot.com/o/images%2Ftransporters%2Flogo-correios-transporter.png?alt=media&token=8b0aa041-d5c6-4a46-9f06-d9b7e38e26d4" } }, "31": { "name": "Loggi", "type": "express", "range": "interstate", "restrictions": {}, "company": { "name": "Loggi", "picture": "https://firebasestorage.googleapis.com/v0/b/freight-calculator-8d6c1.appspot.com/o/images%2Ftransporters%2Flogo-loggi-transporter.png?alt=media&token=a508ac0d-f191-413c-b3b1-e4a23b06c78e" } } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Criar etiqueta (pedido) - **Method:** `POST` - **Path:** `/api/v0/cart` - **Operation ID:** `createShipment` - **Tags:** Etiquetas Cria uma etiqueta com o status `pending` (aguardando pagamento). O preço é recalculado na hora com os dados enviados. Depois de criar, pague a etiqueta de um destes jeitos: - [Pela API](https://developers.superfrete.com/referencia/md/checkoutShipment.md), com o saldo da carteira SuperFrete. - [Pelo painel](https://web.superfrete.com/#/minhas-etiquetas), com cartão de crédito ou saldo. #### Remetente e destinatário - `name` precisa ter nome e sobrenome e é cortado em 32 caracteres. Se o nome da loja tiver uma palavra só, envie algo como `Loja SuperFrete`. Se o token tiver um nome de remetente configurado no painel, ele substitui `from.name`. - `postal_code` é o CEP com 8 dígitos, como texto. Pontos e traços são removidos. - `state_abbr` é a UF em maiúsculas, por exemplo `SP`. - `number` aceita até 10 caracteres; vazio vira `S/N`. `complement` é cortado em 20 caracteres. - `from.document` e `from.phone` são opcionais: sem eles, usamos o CPF/CNPJ e o telefone da conta. A conta precisa ter telefone cadastrado. - `to.document` (CPF ou CNPJ válido) é obrigatório para Jadlog, Loggi e J\&T, e para os Correios sempre que houver declaração de conteúdo (`products`). - `to.phone` precisa ter 10 ou 11 dígitos. Na J\&T, se ele não vier, usamos o telefone do remetente. - Na Loggi, o remetente precisa de e-mail: envie `from.email` ou cadastre um e-mail na conta. #### Declaração de conteúdo ou nota fiscal - **Declaração de conteúdo:** envie `products`, com `name`, `quantity` e `unitary_value` em cada item. - **Nota fiscal:** envie `options.invoice` com a chave de acesso. Nota fiscal vale para os Correios; para as outras transportadoras, só quando liberada para a conta. #### Caixa Envie em `volumes` a caixa devolvida pela [cotação](https://developers.superfrete.com/referencia/md/calculateShipping.md): altura, largura e comprimento em cm e peso em kg. #### Status da etiqueta `pending` (aguardando pagamento) · `released` (paga, aguardando postagem) · `posted` (postada) · `delivered` (entregue) · `canceled` (cancelada). > **Código de rastreio** > > O rastreio só existe depois do pagamento. Consulte o campo `tracking` em [Consultar pedido](https://developers.superfrete.com/referencia/md/getOrder.md) ou assine o evento `order.generated` nos [webhooks](https://developers.superfrete.com/referencia/md/orderEvent.md). **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` - **`from` (required)**: [ShipmentSender](#scalar-schema-shipmentsender) Remetente. - **`service` (required)**: `integer`, possible values: `1, 2, 3, 17, 31, 33` Serviço escolhido na cotação. O `33` (J\&T) só é aceito quando a J\&T está liberada para a conta. - **`to` (required)**: [ShipmentRecipient](#scalar-schema-shipmentrecipient) Destinatário. - **`volumes` (required)** Caixa da encomenda. Use a caixa devolvida pela cotação. Também aceita um array; nesse caso, só o primeiro item é usado. **One of:** - [PackageDimensions](#scalar-schema-packagedimensions) Caixa da encomenda. - array of [PackageDimensions](#scalar-schema-packagedimensions), minItems: `1` - **`options`**: `object` Serviços adicionais, nota fiscal e identificação do pedido. Envie sempre este objeto, mesmo vazio (`{}`), quando não enviar `platform`. - **`insurance_value`**: `number | null` Valor da mercadoria, sem o frete, para contratar seguro. Envie `null` ou `0` para não contratar. - **`invoice`**: `object` Nota fiscal da encomenda. - **`key`**: `string` Número da nota fiscal. Se você inverter os dois campos, a API corrige. - **`number`**: `string` Chave de acesso da nota fiscal, com 44 dígitos. - **`non_commercial`**: `boolean`, deprecated Ignorado. A declaração de conteúdo é usada sempre que `products` é enviado. - **`own_hand`**: `boolean` Mão própria (Correios). - **`receipt`**: `boolean` Aviso de recebimento (Correios). - **`tags`**: `array` Identificação do pedido na sua plataforma. Volta em `tags` nas respostas e nos webhooks. **Items:** - **`tag` (required)**: `string` Número do pedido na sua plataforma. - **`url`**: `string` Link do pedido na sua plataforma. - **`use_insurance_value`**: `boolean` Contrata o seguro com `insurance_value`. Com `false`, o seguro não é contratado mesmo que `insurance_value` venha preenchido. - **`platform`**: `string` Nome da sua plataforma ou aplicação. Sem ele, usamos a plataforma cadastrada no token. - **`products`**: `array` Itens da declaração de conteúdo. Quando enviado, a etiqueta sai com declaração de conteúdo. **Items:** - **`name` (required)**: `string` Nome do produto. - **`quantity` (required)**: `integer`, minimum: `1` Quantidade. - **`unitary_value` (required)**: `number` Valor unitário, em reais. **Example: contentDeclaration** PAC com declaração de conteúdo ```json { "from": { "name": "Loja Aurora", "address": "Rua Barão de Itapetininga", "number": "255", "complement": "Sala 12", "district": "República", "city": "São Paulo", "state_abbr": "SP", "postal_code": "01042001" }, "to": { "name": "Mariana Souza", "address": "Avenida Rio Branco", "number": "156", "district": "Centro", "city": "Rio de Janeiro", "state_abbr": "RJ", "postal_code": "20040003", "email": "mariana.souza@example.com", "phone": "21987654321", "document": "52998224725" }, "service": 1, "products": [ { "name": "Camiseta básica", "quantity": 2, "unitary_value": 49.9 } ], "volumes": { "height": 4, "width": 16, "length": 24, "weight": 0.6 }, "options": { "insurance_value": 99.8, "use_insurance_value": true, "receipt": false, "own_hand": false, "tags": [ { "tag": "3101481", "url": "https://minhaloja.com.br/pedidos/3101481" } ] }, "platform": "MinhaLoja" } ``` #### Responses ##### 200 Etiqueta criada com status `pending` (aguardando pagamento). **Content type:** `application/json` - **`id` (required)**: `string` ID do pedido na SuperFrete. Use nas outras rotas. - **`price` (required)**: `number` Preço da etiqueta, em reais. - **`status` (required)**: `string`, possible values: `"pending"` - **`protocol`**: `string` Igual a `id`. - **`self_tracking`**: `string` Vazio na criação. O rastreio só existe depois do pagamento. - **`tags`**: [OrderTags](#scalar-schema-ordertags) Identificação do pedido na sua plataforma, enviada em `options.tags`. **Example:** ```json { "id": "r8p3dhqjn4I0tBpnvkpU", "protocol": "r8p3dhqjn4I0tBpnvkpU", "price": 22.38, "self_tracking": "", "status": "pending", "tags": [ { "tag": "3101481", "url": "https://minhaloja.com.br/pedidos/3101481" } ] } ``` ##### 400 Dados inválidos, serviço indisponível para a encomenda ou cadastro da conta incompleto. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) | [Error](#scalar-schema-error) **Example: invalidPostalCode** Campo inválido ```json { "message": "Ocorreu um ou mais erros.", "errors": { "to.postal_code": [ "O CEP do destinatário deve ter 8 dígitos" ] } } ``` **Example: missingDocument** CPF/CNPJ do destinatário ausente ```json { "message": "Ocorreu um ou mais erros.", "errors": { "to.document": [ "Campo CPF/CNPJ do destinatário é obrigatório para seguir com geração da etiqueta." ] } } ``` **Example: serviceUnavailable** Serviço indisponível ```json { "message": "Ocorreu um ou mais erros.", "errors": { "service": [ "Serviço indisponível para esse carrinho." ] } } ``` **Example: incompleteRegistration** Conta sem CPF/CNPJ ```json { "message": "Cadastro incompleto. Por favor, finalizar o cadastro para prosseguir", "error": "Cadastro incompleto. Por favor, finalizar o cadastro para prosseguir" } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. Nos Correios, também é a resposta quando o valor do seguro fica fora da faixa aceita pelo serviço. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` **Example:** ```json { "message": "Houve um erro ao verificar o valor do pedido: Valor segurado ultrapassa o limite máximo de R$ 38.057,59", "error": "Houve um erro ao verificar o valor do pedido: Valor segurado ultrapassa o limite máximo de R$ 38.057,59" } ``` ### Pagar etiqueta com saldo - **Method:** `POST` - **Path:** `/api/v0/checkout` - **Operation ID:** `checkoutShipment` - **Tags:** Etiquetas Paga uma etiqueta criada em [Criar etiqueta](https://developers.superfrete.com/referencia/md/createShipment.md) com o saldo da carteira SuperFrete. Com o pagamento aprovado, a etiqueta passa para `released` (aguardando postagem) e a resposta já traz o link de impressão. > **Uma etiqueta por chamada** > > `orders` é um array, mas só o primeiro ID é pago. Para pagar várias etiquetas, faça uma chamada para cada uma. O código de rastreio é gerado logo depois do pagamento e pode ainda vir vazio nesta resposta. Busque-o em [Consultar pedido](https://developers.superfrete.com/referencia/md/getOrder.md) ou pelo evento `order.generated` dos [webhooks](https://developers.superfrete.com/referencia/md/orderEvent.md). #### Como colocar saldo na carteira **Produção:** no [painel](https://web.superfrete.com), abra **Perfil > Carteira**, clique em **Recarregue com Pix**, escolha o valor e pague o Pix gerado no app do seu banco. O saldo entra assim que o pagamento é confirmado. **Sandbox:** no [sandbox](https://sandbox.superfrete.com), abra **Perfil > Carteira**, clique em **Recarregue com Pix**, escolha o valor e clique em **Copiar código PIX**. Cole o código na barra de endereço do navegador e tecle Enter: isso simula o pagamento e credita o saldo de teste. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` - **`orders` (required)** ID do pedido devolvido em Criar etiqueta. Só o primeiro item é pago. **One of:** - `array of string`, minItems: `1` - `string` **Example:** ```json { "orders": [ "r8p3dhqjn4I0tBpnvkpU" ] } ``` #### Responses ##### 200 Pagamento aprovado. **Content type:** `application/json` - **`purchase`**: `object` - **`id`**: `string` ID do pedido pago. - **`orders`**: `array` **Items:** - **`discount`**: `number` Desconto em relação ao preço de balcão, em reais. - **`id`**: `string` - **`price`**: `number` Valor cobrado, em reais. - **`print`**: `object` - **`url`**: `string`, format: `uri` PDF da etiqueta. - **`protocol`**: `string` Igual a `id`. - **`self_tracking`**: `string` Igual a `tracking`. - **`service_id`**: `integer`, possible values: `1, 2, 3, 17, 31, 33` `1` PAC · `2` SEDEX · `17` Mini Envios · `3` Jadlog · `31` Loggi · `33` J\&T. - **`tags`**: [OrderTags](#scalar-schema-ordertags) Identificação do pedido na sua plataforma, enviada em `options.tags`. - **`tracking`**: `string` Código de rastreio. Pode vir vazio; ele é gerado logo após o pagamento. - **`status`**: `string`, possible values: `"paid"` - **`success`**: `boolean` **Example:** ```json { "success": true, "purchase": { "id": "r8p3dhqjn4I0tBpnvkpU", "status": "paid", "orders": [ { "id": "r8p3dhqjn4I0tBpnvkpU", "protocol": "r8p3dhqjn4I0tBpnvkpU", "service_id": 1, "price": 22.38, "discount": 7.41, "tracking": "", "self_tracking": "", "print": { "url": "https://etiqueta.superfrete.com/_etiqueta/pdf/eyJvcmRlcl9pZCI6InI4cDNkaHFqbjRJMHRCcG52a3BVIiwidWlkIjoiTTJ0RFMwZHgifQ==?format=A4" }, "tags": [ { "tag": "3101481", "url": "https://minhaloja.com.br/pedidos/3101481" } ] } ] } } ``` ##### 400 Requisição inválida. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) | [Error](#scalar-schema-error) **Example:** ```json { "message": "Ocorreu um ou mais erros.", "errors": { "orders": [ "(orders) é obrigatório." ] } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 409 A etiqueta não pode ser paga agora. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example: noBalance** Saldo insuficiente ```json { "message": "Sem saldo na carteira! Utilize o app para recarregar a carteira ou pagar a etiqueta com cartão de crédito.", "error": "Sem saldo na carteira! Utilize o app para recarregar a carteira ou pagar a etiqueta com cartão de crédito." } ``` **Example: alreadyPaid** Etiqueta já paga ```json { "message": "Pedido já comprado.", "error": "Pedido já comprado." } ``` **Example: cancelled** Etiqueta cancelada ```json { "message": "Este pedido está cancelado e não pode ser pago. Para emitir uma nova etiqueta, crie um novo pedido.", "error": "Este pedido está cancelado e não pode ser pago. Para emitir uma nova etiqueta, crie um novo pedido." } ``` **Example: labelLimitReached** Limite de etiquetas em aberto ```json { "message": "Não foi possível realizar a compra pois o limite de etiquetas disponível para esta conta foi atingido. Faça a postagem das etiquetas emitidas ou entre em contato com nosso suporte.", "error": "Não foi possível realizar a compra pois o limite de etiquetas disponível para esta conta foi atingido. Faça a postagem das etiquetas emitidas ou entre em contato com nosso suporte." } ``` ##### 500 O pagamento não foi concluído. Também é a resposta quando o ID do pedido não existe. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Não foi possível finalizar o pagamento tente pelo app ou entre em contato com o suporte", "error": "Não foi possível finalizar o pagamento tente pelo app ou entre em contato com o suporte" } ``` ### Gerar link de impressão - **Method:** `POST` - **Path:** `/api/v0/tag/print` - **Operation ID:** `getLabelPrintUrl` - **Tags:** Etiquetas Gera um link para um PDF com as etiquetas dos pedidos informados. Entram no PDF só as etiquetas pagas que ainda aguardam postagem (`released`); as outras são ignoradas. O tamanho do papel segue a preferência da conta: A4 por padrão. Para imprimir em A6 (impressora térmica, como a Zebra), ative a opção na conta: 1. Abra [Etiquetas](https://web.superfrete.com/#/minhas-etiquetas) no painel. 2. Clique em **Configurações**. 3. Ative **A6 (Zebra)**. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` - **`orders` (required)**: `array of string`, minItems: `1` IDs dos pedidos cujas etiquetas vão para o PDF. **Example:** ```json { "orders": [ "r8p3dhqjn4I0tBpnvkpU", "01JK6D99A7SVYXV03C3ZFS7CXA" ] } ``` #### Responses ##### 200 Link do PDF. **Content type:** `application/json` - **`url` (required)**: `string`, format: `uri` **Example:** ```json { "url": "https://etiqueta.superfrete.com/_etiqueta/pdf/Jx8kq2LmN4pR7sT1vW3y?format=A4" } ``` ##### 400 `orders` ausente. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) Um ou mais campos da requisição são inválidos. **Example:** ```json { "message": "Ocorreu um ou mais erros.", "errors": { "orders": [ "É necessário enviar ao menos uma etiqueta para gerar o PDF" ] } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Consultar pedido - **Method:** `GET` - **Path:** `/api/v0/order/info/{id}` - **Operation ID:** `getOrder` - **Tags:** Pedidos Devolve os dados de um pedido pelo ID: remetente, destinatário, caixa, preço, status, rastreio e link de impressão. - `tracking` fica vazio até o pagamento e a geração da etiqueta. - `print.url` é o PDF da etiqueta. Ele só tem conteúdo enquanto a etiqueta está paga e aguardando postagem. - `service_id` só vem preenchido para PAC (`1`) e SEDEX (`2`). Para saber o serviço dos outros pedidos, use a [listagem de pedidos](https://developers.superfrete.com/referencia/md/listOrders.md). - `generated_at` e `posted_at` só aparecem depois que a etiqueta é gerada e postada. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Path parameters - **`id` (required)**: `string` ID do pedido na SuperFrete. **Example:** ```json "r8p3dhqjn4I0tBpnvkpU" ``` #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 200 Dados do pedido. **Content type:** `application/json` [Order](#scalar-schema-order) **Example:** ```json { "id": "r8p3dhqjn4I0tBpnvkpU", "protocol": "r8p3dhqjn4I0tBpnvkpU", "format": "box", "delivery": 5, "delivery_min": 5, "delivery_max": 5, "discount": 7.41, "height": "4", "width": "16", "length": "24", "weight": "0.6", "from": { "address": "Rua Barão de Itapetininga", "city": "São Paulo", "complement": "Sala 12", "district": "República", "document": "11222333000181", "name": "Loja Aurora", "location_number": "255", "phone": "11912345678", "postal_code": "01042001", "state_abbr": "SP", "country_id": "BR" }, "to": { "address": "Avenida Rio Branco", "city": "Rio de Janeiro", "complement": null, "email": "mariana.souza@example.com", "district": "Centro", "document": "52998224725", "name": "Mariana Souza", "location_number": "156", "phone": "21987654321", "postal_code": "20040003", "state_abbr": "RJ", "country_id": "BR" }, "invoice": null, "own_hand": false, "receipt": false, "price": 22.38, "tracking": "AA123456785BR", "status": "released", "service_id": 1, "products": [ { "name": "Camiseta básica", "quantity": "2", "unitary_value": "49.9" } ], "insurance_value": "99.8", "generated_at": "2026-10-29T14:03:12.511Z", "created_at": "2026-10-29T14:01:47.399Z", "updated_at": "2026-10-29T14:03:12.902Z", "print": { "url": "https://etiqueta.superfrete.com/_etiqueta/pdf/Jx8kq2LmN4pR7sT1vW3y?format=A4" }, "tags": [ { "tag": "3101481", "url": "https://minhaloja.com.br/pedidos/3101481" } ] } ``` ##### 400 Pedido não encontrado. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) Um ou mais campos da requisição são inválidos. **Example:** ```json { "message": "Ocorreu um ou mais erros.", "errors": { "orders.0": [ "Pedido não encontrado." ] } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Listar pedidos - **Method:** `GET` - **Path:** `/api/v0/me/orders` - **Operation ID:** `listOrders` - **Tags:** Pedidos Lista os pedidos da conta, com filtro por status, busca, paginação e ordenação. Sem filtros, traz todos os status, 20 por página, do mais recente para o mais antigo. > **User-Agent obrigatório** > > Esta rota recusa a requisição se o `User-Agent` não seguir o formato `Aplicação (email@exemplo.com)`. `sort_by` escolhe a data usada na ordenação (`created_at` ou `updated_at`) e `order` escolhe a direção. `meta` traz o total de pedidos, o tamanho da página, a página atual e a última página. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Query parameters - **`status`**: `string`, possible values: `"pending", "released", "posted", "delivered", "canceled"` Filtra por status. Sem ele, traz todos. `pending`: aguardando pagamento · `released`: paga, aguardando postagem · `posted`: postada · `delivered`: entregue · `canceled`: cancelada. - **`q`**: `string` Busca exata pelo ID do pedido ou pelo número da etiqueta. - **`page`**: `integer`, default: `1`, minimum: `1` Página, a partir de 1. - **`per_page`**: `integer`, default: `20`, minimum: `1`, maximum: `100` Pedidos por página, de 1 a 100. - **`sort_by`**: `string`, possible values: `"created_at", "updated_at"`, default: `"created_at"` Data usada na ordenação. - **`order`**: `string`, possible values: `"asc", "desc"`, default: `"desc"` `desc` (mais recentes primeiro) ou `asc`. #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 200 Página de pedidos. **Content type:** `application/json` - **`data` (required)**: array of [OrderListItem](#scalar-schema-orderlistitem) - **`meta` (required)**: `object` - **`last_page`**: `integer` - **`page`**: `integer` - **`per_page`**: `integer` - **`total`**: `integer` Total de pedidos que atendem ao filtro. **Example:** ```json { "data": [ { "order_id": "r8p3dhqjn4I0tBpnvkpU", "status": "released", "carrier": "correios", "tracking": null, "price": { "total": 22.38, "discount": 7.41, "without_discount": 29.79 }, "delivery": { "min": 5, "max": 5 }, "from": { "name": "Loja Aurora", "phone": "11912345678", "document": "11222333000181", "postal_code": "01042001", "address": "Rua Barão de Itapetininga", "number": "255", "complement": "Sala 12", "state": "SP", "city": "São Paulo", "district": "República" }, "to": { "name": "Mariana Souza", "phone": "21987654321", "email": "mariana.souza@example.com", "document": "52998224725", "postal_code": "20040003", "address": "Avenida Rio Branco", "number": "156", "complement": null, "state": "RJ", "city": "Rio de Janeiro", "district": "Centro" }, "pudo": { "unit": null, "postal_code": null, "address": null, "number": null, "complement": null, "state": null, "city": null, "district": null }, "created_at": "2026-10-29T14:01:47.399Z", "updated_at": "2026-10-29T14:03:12.902Z" } ], "meta": { "total": 1, "per_page": 20, "page": 1, "last_page": 1 } } ``` ##### 400 `User-Agent` ausente ou fora do formato, ou filtro inválido. **Content type:** `application/json` [UserAgentError](#scalar-schema-useragenterror) | [Error](#scalar-schema-error) **Example: userAgent** User-Agent fora do formato ```json { "error": "User-Agent deve seguir o formato: Aplicação (email@exemplo.com)", "code": "invalid-argument" } ``` **Example: pagination** per_page fora da faixa ```json { "message": "Quantidade de itens por página deve estar entre 1 e 100", "error": "Quantidade de itens por página deve estar entre 1 e 100" } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Cancelar pedido - **Method:** `POST` - **Path:** `/api/v0/order/cancel` - **Operation ID:** `cancelOrder` - **Tags:** Pedidos Pede o cancelamento de uma etiqueta. Envie o ID do pedido e o motivo. - Etiquetas já postadas, entregues ou canceladas não podem ser canceladas. - O cancelamento é processado em segundo plano: o pedido passa a aparecer como `canceled` na hora, e o valor pago volta para a carteira da conta quando o processamento termina. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` - **`order` (required)**: `object` - **`description` (required)**: `string` Motivo do cancelamento. - **`id` (required)**: `string` ID do pedido na SuperFrete. **Example:** ```json { "order": { "id": "r8p3dhqjn4I0tBpnvkpU", "description": "Cliente desistiu da compra" } } ``` #### Responses ##### 200 Cancelamento aceito. **Content type:** `application/json` `object` Mapa do ID do pedido para o resultado. **Additional properties:** - **`canceled`**: `boolean` **Example:** ```json { "r8p3dhqjn4I0tBpnvkpU": { "canceled": true } } ``` ##### 400 Campo ausente ou pedido não encontrado. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) Um ou mais campos da requisição são inválidos. **Example: notFound** Pedido não encontrado ```json { "message": "Ocorreu um ou mais erros.", "errors": { "orders.0": [ "Order not found!" ] } } ``` **Example: missingReason** Motivo ausente ```json { "message": "Ocorreu um ou mais erros.", "errors": { "order.description": [ "(order.description) é obrigatório." ] } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 409 O pedido não pode mais ser cancelado (já postado, entregue ou cancelado). **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Esse pedido não pode mais ser cancelado!", "error": "Esse pedido não pode mais ser cancelado!" } ``` ##### 500 Falha ao verificar ou registrar o cancelamento. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Um error ocorreu ao tentar cancelar o pedido, tente novamente ou entre em contato com o suporte", "error": "Um error ocorreu ao tentar cancelar o pedido, tente novamente ou entre em contato com o suporte" } ``` ### Consultar conta - **Method:** `GET` - **Path:** `/api/v0/user` - **Operation ID:** `getUser` - **Tags:** Usuário Devolve os dados da conta dona do token: nome, telefone, e-mail, CPF, saldo da carteira e limite de etiquetas em aberto. - `limits.shipments`: etiquetas emitidas que ainda aguardam postagem. - `limits.shipments_available`: quantas etiquetas ainda podem ser pagas antes de atingir o limite. Com `0`, o pagamento por API é recusado até que alguma etiqueta seja postada. - `balance`: saldo da carteira, em reais. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 200 Dados da conta. **Content type:** `application/json` - **`balance`**: `number` Saldo da carteira, em reais. - **`document`**: `string | null` CPF da conta. - **`email`**: `string` - **`firstname`**: `string` Primeiro nome. - **`id`**: `string` ID da conta. - **`lastname`**: `string` Restante do nome. - **`limits`**: `object` - **`shipments`**: `integer` Etiquetas aguardando postagem. - **`shipments_available`**: `integer` Etiquetas que ainda podem ser pagas. - **`phone`**: `string` Telefone cadastrado. **Example:** ```json { "id": "3kCKGqzlPFfCEj2X1Tkob07SN2M2", "firstname": "Mariana", "lastname": "Souza Lima", "phone": "+5511912345678", "email": "mariana@lojaaurora.com.br", "document": "52998224725", "limits": { "shipments": 3, "shipments_available": 47 }, "balance": 1763.04 } ``` ##### 400 Conta não encontrada. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Usuário não encontrado!", "error": "Usuário não encontrado!" } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Listar endereços - **Method:** `GET` - **Path:** `/api/v0/user/addresses` - **Operation ID:** `listUserAddresses` - **Tags:** Usuário Devolve os endereços cadastrados em **Perfil > Meus Endereços**, dentro de `data`. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 200 Endereços da conta. **Content type:** `application/json` - **`data` (required)**: array of [UserAddress](#scalar-schema-useraddress) **Example:** ```json { "data": [ { "id": "Qm3xT7pLs9VdK2aYfR1c", "uid": "3kCKGqzlPFfCEj2X1Tkob07SN2M2", "label": "Loja", "postal_code": "01042001", "address": "Rua Barão de Itapetininga", "number": "255", "complement": "Sala 12", "district": "República", "city": "São Paulo", "state": "SP", "latitude": "", "longitude": "", "is_default": true, "created_at": "2025-03-14T18:22:05.000Z", "updated_at": "2025-03-14T18:22:05.000Z" } ] } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Criar Webhook App - **Method:** `POST` - **Path:** `/api/v0/webhook` - **Operation ID:** `createWebhook` - **Tags:** Webhooks Cadastra uma URL para receber eventos da conta. A SuperFrete envia um `POST` com JSON para essa URL a cada evento assinado em `events`. O formato de cada evento está em [Evento de pedido](https://developers.superfrete.com/referencia/md/orderEvent.md) e [Atualização de rastreio](https://developers.superfrete.com/referencia/md/trackingUpdated.md). > **Guarde o `secret_token`** > > Ele só aparece nesta resposta e serve para validar a assinatura `X-ME-Signature` de cada evento. #### Eventos | Evento | Quando é enviado | | ------------------ | ------------------------------------------------------------------------ | | `order.created` | Etiqueta criada, aguardando pagamento. | | `order.released` | Etiqueta paga. | | `order.generated` | Etiqueta gerada na transportadora. A partir daqui há código de rastreio. | | `order.posted` | Encomenda postada. | | `order.delivered` | Encomenda entregue. | | `order.cancelled` | Etiqueta cancelada. | | `tracking.updated` | Nova movimentação no rastreio. | Os nomes não são validados na criação: um evento escrito errado é aceito e nunca dispara. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` - **`name` (required)**: `string` Nome para identificar o Webhook App. - **`url` (required)**: `string`, format: `uri` URL `http` ou `https` que vai receber os eventos. - **`events`**: `array` Eventos assinados. Sem este campo, o Webhook App não recebe nada. **Items:** `string`, possible values: `"order.created", "order.released", "order.generated", "order.posted", "order.delivered", "order.cancelled", "tracking.updated"` Evento que dispara a notificação. **Example:** ```json { "name": "Loja Aurora", "url": "https://lojaaurora.com.br/webhooks/superfrete", "events": [ "order.generated", "order.posted", "order.delivered", "tracking.updated" ] } ``` #### Responses ##### 201 Webhook App criado. **Content type:** `application/json` - **`created_at`**: `string`, format: `date-time` - **`events`**: `array of string` - **`id`**: `string` - **`is_active`**: `boolean` - **`name`**: `string` - **`secret_token`**: `string` Chave da assinatura `X-ME-Signature`. Só aparece nesta resposta. - **`url`**: `string` **Example:** ```json { "id": "7hQ2mZk4LwXy9RtB3sVc", "name": "Loja Aurora", "url": "https://lojaaurora.com.br/webhooks/superfrete", "secret_token": "4f9c2a1e8b7d6c5f4e3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e", "events": [ "order.generated", "order.posted", "order.delivered", "tracking.updated" ], "is_active": true, "created_at": "2026-10-29T14:00:00.000Z" } ``` ##### 400 `name` ou `url` ausente, URL inválida ou `events` que não é um array. **Content type:** `application/json` [ValidationError](#scalar-schema-validationerror) Um ou mais campos da requisição são inválidos. **Generated example:** ```json { "message": "", "errors": { "additionalProperty": [ "" ] } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Listar Webhook Apps - **Method:** `GET` - **Path:** `/api/v0/webhook` - **Operation ID:** `listWebhooks` - **Tags:** Webhooks Lista os Webhook Apps da conta. Webhook Apps removidos não aparecem. O `secret_token` não é devolvido aqui. `created_at` vem no formato de data do Firestore: `_seconds` é o horário em segundos desde 1970 (UTC). **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 200 Webhook Apps da conta. **Content type:** `application/json` **Array of:** - **`created_at`**: [FirestoreTimestamp](#scalar-schema-firestoretimestamp) Data no formato do Firestore. - **`events`**: `array of string` - **`id`**: `string` - **`is_active`**: `boolean` - **`name`**: `string` - **`url`**: `string` **Example:** ```json [ { "id": "7hQ2mZk4LwXy9RtB3sVc", "name": "Loja Aurora", "url": "https://lojaaurora.com.br/webhooks/superfrete", "events": [ "order.generated", "order.posted", "order.delivered", "tracking.updated" ], "is_active": true, "created_at": { "_seconds": 1761746400, "_nanoseconds": 0 } } ] ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Atualizar Webhook App - **Method:** `PUT` - **Path:** `/api/v0/webhook/{id}` - **Operation ID:** `updateWebhook` - **Tags:** Webhooks Atualiza um Webhook App da conta. Só os campos enviados mudam. Para pausar o envio de eventos sem remover o cadastro, envie `is_active: false`. A URL não é validada nesta rota: confira o endereço antes de enviar. `updated_at` vem no formato de data do Firestore: `_seconds` é o horário em segundos desde 1970 (UTC). **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Path parameters - **`id` (required)**: `string` ID do Webhook App. **Example:** ```json "7hQ2mZk4LwXy9RtB3sVc" ``` #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Request body **Required:** `true` **Content type:** `application/json` - **`events`**: `array` Nova lista de eventos. Substitui a lista anterior. **Items:** `string`, possible values: `"order.created", "order.released", "order.generated", "order.posted", "order.delivered", "order.cancelled", "tracking.updated"` Evento que dispara a notificação. - **`is_active`**: `boolean` Liga ou desliga o envio de eventos. - **`name`**: `string` Novo nome. - **`url`**: `string`, format: `uri` Nova URL de recebimento. **Example:** ```json { "events": [ "order.posted", "order.delivered" ], "is_active": true } ``` #### Responses ##### 200 Webhook App atualizado. **Content type:** `application/json` - **`events`**: `array of string` - **`id`**: `string` - **`is_active`**: `boolean` - **`name`**: `string` - **`updated_at`**: [FirestoreTimestamp](#scalar-schema-firestoretimestamp) Data no formato do Firestore. - **`url`**: `string` **Example:** ```json { "id": "7hQ2mZk4LwXy9RtB3sVc", "name": "Loja Aurora", "url": "https://lojaaurora.com.br/webhooks/superfrete", "events": [ "order.posted", "order.delivered" ], "is_active": true, "updated_at": { "_seconds": 1761750000, "_nanoseconds": 0 } } ``` ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 404 Webhook App não encontrado ou de outra conta. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Aplicação Webhook não encontrada ou permissões insuficientes para acesso", "error": "Aplicação Webhook não encontrada ou permissões insuficientes para acesso" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ### Remover Webhook App - **Method:** `DELETE` - **Path:** `/api/v0/webhook/{id}` - **Operation ID:** `deleteWebhook` - **Tags:** Webhooks Remove um Webhook App da conta. Ele para de receber eventos e some da listagem. A remoção não pode ser desfeita. **Servers:** [Inherited servers](#scalar-context-global-servers) **Authentication:** [Global authentication](#scalar-context-global-authentication) #### Path parameters - **`id` (required)**: `string` ID do Webhook App. **Example:** ```json "7hQ2mZk4LwXy9RtB3sVc" ``` #### Header parameters - **`User-Agent` (required)**: `string` Nome da sua aplicação com um e-mail de contato técnico, no formato `Aplicação (email@exemplo.com)`. Hoje só [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md) recusa um valor fora desse formato, mas envie em todas as rotas. **Example:** ```json "MinhaLoja (dev@minhaloja.com.br)" ``` #### Responses ##### 204 Removido. Sem corpo de resposta. ##### 401 Token ausente, inválido, removido ou de outro ambiente. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Token inválida!", "error": "Token inválida!" } ``` ##### 404 Webhook App não encontrado ou de outra conta. **Content type:** `application/json` [Error](#scalar-schema-error) Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. **Example:** ```json { "message": "Aplicação Webhook não encontrada ou permissões insuficientes para acesso", "error": "Aplicação Webhook não encontrada ou permissões insuficientes para acesso" } ``` ##### 500 Erro interno. O corpo pode ser um objeto de erro ou só um texto JSON com a mensagem. **Content type:** `application/json` [Error](#scalar-schema-error) | `string` [Generated example](#scalar-example-2) ## Webhooks ### Evento de pedido - **Method:** `POST` - **Webhook:** `orderEvent` - **Operation ID:** `orderEvent` - **Tags:** Webhooks Enviado para a URL do Webhook App quando o pedido muda de etapa: `order.created`, `order.released`, `order.generated`, `order.posted`, `order.delivered` ou `order.cancelled`. #### Assinatura Todo envio leva o header `X-ME-Signature`: o HMAC-SHA256, em hexadecimal, do corpo **exatamente como recebido**, usando o `secret_token` devolvido na criação do Webhook App. Calcule o HMAC sobre os bytes crus do corpo, antes de interpretar o JSON, e compare com o header. Exemplos em Node.js, Python e PHP estão no [guia de webhooks](https://developers.superfrete.com/guias/webhooks). #### Entrega - Responda com qualquer status `2xx` em até **10 segundos**. Acima disso, ou com outro status, a entrega conta como falha. - Cada evento é enviado **uma única vez** por pedido e por Webhook App. Hoje não há reenvio automático depois de uma falha: para reconciliar, consulte o pedido em [Consultar pedido](https://developers.superfrete.com/referencia/md/getOrder.md) ou [Listar pedidos](https://developers.superfrete.com/referencia/md/listOrders.md). - Os eventos podem chegar fora de ordem, e uma etapa pode ser pulada. Use `data.status` e as datas do payload, não a ordem de chegada. - `tracking` e `tracking_url` aparecem a partir de `order.generated`; `tracking_url` só vem em `order.posted` e `order.delivered`. - `tags` traz a identificação do pedido enviada em `options.tags` na criação da etiqueta, no formato `{ "name": "order_id", "value": "...", "url": "..." }`. **Servers:** [Inherited servers](#scalar-context-global-servers) #### Authentication No authentication required. #### Header parameters - **`X-ME-Signature` (required)**: `string` HMAC-SHA256 do corpo, em hexadecimal, assinado com o `secret_token` do Webhook App. **Example:** ```json "5d41402abc4b2a76b9719d911017c5925d41402abc4b2a76b9719d911017c592" ``` - **`User-Agent` (required)**: `string` Sempre `SuperFrete Webhooks/1.0`. **Example:** ```json "SuperFrete Webhooks/1.0" ``` #### Request body **Required:** `true` **Content type:** `application/json` [OrderWebhookPayload](#scalar-schema-orderwebhookpayload) **Example:** ```json { "event": "order.delivered", "data": { "id": "r8p3dhqjn4I0tBpnvkpU", "order_id": "r8p3dhqjn4I0tBpnvkpU", "protocol": null, "status": "delivered", "tracking": "AA123456785BR", "self_tracking": null, "user_id": "3kCKGqzlPFfCEj2X1Tkob07SN2M2", "tags": [ { "name": "order_id", "value": "3101481", "url": "https://minhaloja.com.br/pedidos/3101481" } ], "created_at": "2026-10-29T14:01:47.399Z", "paid_at": "2026-10-29T14:02:50.120Z", "generated_at": "2026-10-29T14:03:12.511Z", "posted_at": "2026-10-30T10:15:00.000Z", "delivered_at": "2026-11-03T16:42:00.000Z", "canceled_at": null, "expired_at": null, "tracking_url": "https://rastreio.superfrete.com/#/tracking/AA123456785BR" } } ``` #### Responses ##### 200 Recebido. Qualquer status `2xx` confirma a entrega. ### Atualização de rastreio - **Method:** `POST` - **Webhook:** `trackingUpdated` - **Operation ID:** `trackingUpdated` - **Tags:** Webhooks Enviado a cada nova movimentação no rastreio de um pedido, para os Webhook Apps ativos que assinam `tracking.updated`. - A assinatura `X-ME-Signature` e o prazo de 10 segundos são os mesmos do [Evento de pedido](https://developers.superfrete.com/referencia/md/orderEvent.md). - `data.status` é o código da movimentação como a transportadora informa, sem tradução. - Cada movimentação é enviada uma vez por Webhook App. Movimentações internas da transportadora não geram evento. **Servers:** [Inherited servers](#scalar-context-global-servers) #### Authentication No authentication required. #### Header parameters - **`X-ME-Signature` (required)**: `string` HMAC-SHA256 do corpo, em hexadecimal, assinado com o `secret_token` do Webhook App. **Example:** ```json "9b74c9897bac770ffc029102a200c5de9b74c9897bac770ffc029102a200c5de" ``` - **`User-Agent` (required)**: `string` Sempre `SuperFrete Webhooks/1.0`. **Example:** ```json "SuperFrete Webhooks/1.0" ``` #### Request body **Required:** `true` **Content type:** `application/json` [TrackingWebhookPayload](#scalar-schema-trackingwebhookpayload) **Example:** ```json { "event": "tracking.updated", "data": { "order_id": "r8p3dhqjn4I0tBpnvkpU", "code": "AA123456785BR", "status": "01" } } ``` #### Responses ##### 200 Recebido. Qualquer status `2xx` confirma a entrega. ## Schemas ### Error **Type:** `object` Erro de regra de negócio ou de autenticação. `message` e `error` trazem o mesmo texto. - **`error` (required)**: `string` Mesmo conteúdo de `message`, mantido por compatibilidade. - **`message` (required)**: `string` Mensagem legível do erro. **Generated example:** ```json { "message": "", "error": "" } ``` ### ValidationError **Type:** `object` Um ou mais campos da requisição são inválidos. - **`errors` (required)**: `object` Mapa do campo (ex.: `to.postal_code`) para a lista de mensagens de erro desse campo. **Additional properties:** `array of string` - **`message` (required)**: `string` Sempre `Ocorreu um ou mais erros.` **Generated example:** ```json { "message": "", "errors": { "additionalProperty": [ "" ] } } ``` ### UserAgentError **Type:** `object` `User-Agent` ausente ou fora do formato `Aplicação (email@exemplo.com)`. - **`code` (required)**: `string`, possible values: `"invalid-argument"` - **`error` (required)**: `string` **Generated example:** ```json { "error": "", "code": "invalid-argument" } ``` ### OAuthError **Type:** `object` - **`error` (required)**: `string`, possible values: `"invalid_request", "unsupported_grant_type", "invalid_client", "unauthorized_client", "server_error"` Código do erro OAuth. - **`error_description` (required)**: `string` - **`http_status` (required)**: `integer` - **`message` (required)**: `string` **Generated example:** ```json { "error": "invalid_request", "http_status": 1, "error_description": "", "message": "" } ``` ### OAuthTokenRequest **Type:** `object` - **`client_id` (required)**: `string` Identificador numérico da sua aplicação, fornecido pela SuperFrete. - **`client_secret` (required)**: `string` Segredo da sua aplicação, fornecido pela SuperFrete. Nunca exponha no front-end. - **`code` (required)**: `string` Código recebido no Passo 1. Vale por 10 minutos e só pode ser trocado uma vez. - **`grant_type` (required)**: `string`, possible values: `"authorization_code"` - **`redirect_uri` (required)**: `string` Idêntica à URL cadastrada na SuperFrete. - **`state`**: `string` Aceito e ignorado pela SuperFrete. Valide o `state` do seu lado no retorno do Passo 1. **Generated example:** ```json { "grant_type": "authorization_code", "client_id": "", "client_secret": "", "redirect_uri": "", "code": "", "state": "" } ``` ### PostalCode **Type:** `string` CEP com 8 dígitos. Pontos e traços são removidos. **Example:** ```json "01153000" ``` ### PackageDimensions **Type:** `object` Caixa da encomenda. - **`height` (required)**: `number` Altura, em cm. - **`length` (required)**: `number` Comprimento, em cm. - **`weight` (required)**: `number` Peso, em kg. - **`width` (required)**: `number` Largura, em cm. **Generated example:** ```json { "height": 4, "width": 16, "length": 24, "weight": 0.6 } ``` ### ServiceId **Type:** `integer`, possible values: `1, 2, 3, 17, 31, 33` `1` PAC · `2` SEDEX · `17` Mini Envios · `3` Jadlog · `31` Loggi · `33` J\&T. ### ShippingQuote **Type:** `object` Opção de frete cotada. - **`company` (required)**: [QuoteCompany](#scalar-schema-quotecompany) Transportadora. - **`id` (required)**: `integer`, possible values: `1, 2, 3, 17, 31, 33` `1` PAC · `2` SEDEX · `17` Mini Envios · `3` Jadlog · `31` Loggi · `33` J\&T. - **`name` (required)**: `string` Nome do serviço, como `PAC`, `SEDEX`, `Mini Envios` ou o nome do serviço da transportadora. - **`price` (required)**: `number` Preço com o desconto SuperFrete, em reais. - **`additional_services`**: `object` - **`own_hand`**: `boolean` - **`receipt`**: `boolean` - **`currency`**: `string`, possible values: `"R$"` - **`delivery_range`**: `object` Prazo mínimo e máximo, em dias úteis. - **`max`**: `integer` - **`min`**: `integer` - **`delivery_time`**: `integer` Prazo de entrega, em dias úteis. - **`discount`**: `string` Desconto em relação ao preço de balcão, em reais, como texto. - **`has_error`**: `boolean`, const: `false` - **`packages`**: `array` Caixa considerada na cotação. Use estes valores em `volumes` ao criar a etiqueta. **Items:** - **`dimensions`**: `object` Medidas em cm, como texto. - **`height`**: `string` - **`length`**: `string` - **`width`**: `string` - **`discount`**: `string` - **`format`**: `string` Formato da embalagem. - **`insurance_value`**: `number` Valor segurado considerado, em reais. - **`price`**: `number` - **`weight`**: `string` Peso em kg, como texto. **Generated example:** ```json { "id": 1, "name": "", "price": 1, "discount": "", "currency": "R$", "delivery_time": 1, "delivery_range": { "min": 1, "max": 1 }, "packages": [ { "price": 1, "discount": "", "format": "", "dimensions": { "height": "", "width": "", "length": "" }, "weight": "", "insurance_value": 1 } ], "additional_services": { "receipt": true, "own_hand": true }, "company": { "id": 1, "name": "", "picture": "" }, "has_error": false } ``` ### ShippingQuoteError **Type:** `object` Serviço que não pode ser usado nesta encomenda. Traz o motivo em `error` e não tem preço. - **`error` (required)**: `string` Motivo, pronto para exibir. - **`id` (required)**: `integer`, possible values: `1, 2, 3, 17, 31, 33` `1` PAC · `2` SEDEX · `17` Mini Envios · `3` Jadlog · `31` Loggi · `33` J\&T. - **`name` (required)**: `string` - **`company`**: [QuoteCompany](#scalar-schema-quotecompany) Transportadora. **Generated example:** ```json { "id": 1, "name": "", "error": "", "company": { "id": 1, "name": "", "picture": "" } } ``` ### QuoteCompany **Type:** `object` Transportadora. - **`id`**: `integer` `1` Correios · `2` Jadlog · `14` Loggi. - **`name`**: `string` - **`picture`**: `string` URL do logotipo. Pode vir vazia. **Generated example:** ```json { "id": 1, "name": "", "picture": "" } ``` ### ServiceInfo **Type:** `object` - **`company`**: `object` - **`name`**: `string` - **`picture`**: `string` - **`name`**: `string` - **`optionals`**: `array` `AR` aviso de recebimento · `MP` mão própria · `VD` valor declarado. Só nos Correios. **Items:** `string`, possible values: `"AR", "MP", "VD"` - **`range`**: `string` Abrangência. Sempre `interstate` (envios nacionais). - **`requirements`**: `array` Dados obrigatórios na etiqueta. Só nos Correios. **Items:** `string`, possible values: `"names", "addresses"` - **`restrictions`**: `object` Limites do serviço. Vazio para Jadlog, Loggi e J\&T. - **`formats`**: `object` - **`package`**: `object` - **`height`**: [MinMax](#scalar-schema-minmax) - **`length`**: [MinMax](#scalar-schema-minmax) - **`sum`**: `number` Soma máxima de altura, largura e comprimento, em cm. - **`weight`**: [MinMax](#scalar-schema-minmax) - **`width`**: [MinMax](#scalar-schema-minmax) - **`insurance_value`**: [MinMax](#scalar-schema-minmax) - **`type`**: `string` **Generated example:** ```json { "name": "", "type": "express", "range": "", "restrictions": { "insurance_value": { "min": 1, "max": 1 }, "formats": { "package": { "weight": { "min": 1, "max": 1 }, "width": { "min": 1, "max": 1 }, "height": { "min": 1, "max": 1 }, "length": { "min": 1, "max": 1 }, "sum": 1 } } }, "requirements": [ "names" ], "optionals": [ "AR" ], "company": { "name": "", "picture": "" } } ``` ### MinMax **Type:** `object` - **`max`**: `number` - **`min`**: `number` **Generated example:** ```json { "min": 1, "max": 1 } ``` ### ShipmentSender **Type:** `object` Remetente. - **`address` (required)**: `string`, maxLength: `50` Logradouro. - **`city` (required)**: `string`, maxLength: `50` - **`district` (required)**: `string`, maxLength: `60` Bairro. - **`name` (required)**: `string` Nome e sobrenome. Cortado em 32 caracteres. - **`postal_code` (required)**: `string` CEP com 8 dígitos. Pontos e traços são removidos. - **`state_abbr` (required)**: `string`, pattern: `^[A-Z]{2}$` UF em maiúsculas. - **`complement`**: `string | null` Complemento. Cortado em 20 caracteres. - **`document`**: `string | null` CPF ou CNPJ. Sem ele, usamos o documento da conta. - **`email`**: `string` E-mail do remetente. Usado na Loggi; sem ele, usamos o e-mail da conta. - **`number`**: `string | null`, maxLength: `10` Número. Vazio vira `S/N`. - **`phone`**: `string | null` Telefone. Sem ele, usamos o telefone da conta. **Generated example:** ```json { "name": "Loja Aurora", "address": "", "number": null, "complement": null, "district": "", "city": "", "state_abbr": "SP", "postal_code": "01153000", "document": null, "phone": null, "email": "" } ``` ### ShipmentRecipient **Type:** `object` Destinatário. - **`address` (required)**: `string`, maxLength: `50` Logradouro. - **`city` (required)**: `string`, maxLength: `50` - **`district` (required)**: `string`, maxLength: `50` Bairro. - **`name` (required)**: `string` Nome e sobrenome. Cortado em 32 caracteres. - **`postal_code` (required)**: `string` CEP com 8 dígitos. Pontos e traços são removidos. - **`state_abbr` (required)**: `string`, pattern: `^[A-Z]{2}$` UF em maiúsculas. - **`complement`**: `string | null` Complemento. Cortado em 20 caracteres. - **`document`**: `string | null` CPF ou CNPJ válido. Obrigatório para Jadlog, Loggi e J\&T, e para os Correios quando houver declaração de conteúdo. - **`email`**: `string | null`, maxLength: `100` E-mail do destinatário, para avisos de rastreio. Envie `null` ou vazio se não tiver. - **`number`**: `string | null`, maxLength: `10` Número. Vazio vira `S/N`. - **`phone`**: `string | null` Telefone com DDD, 10 ou 11 dígitos. Na J\&T, sem ele usamos o telefone do remetente. **Generated example:** ```json { "name": "Mariana Souza", "address": "", "number": null, "complement": null, "district": "", "city": "", "state_abbr": "SP", "postal_code": "01153000", "email": null, "phone": "21987654321", "document": "52998224725" } ``` ### StateAbbr **Type:** `string`, pattern: `^[A-Z]{2}$` UF em maiúsculas. **Example:** ```json "SP" ``` ### OrderStatus **Type:** `string`, possible values: `"pending", "released", "posted", "delivered", "canceled"` `pending`: aguardando pagamento · `released`: paga, aguardando postagem · `posted`: postada · `delivered`: entregue · `canceled`: cancelada. ### OrderTags **Type:** `array` Identificação do pedido na sua plataforma, enviada em `options.tags`. **Array of:** - **`tag`**: `string` - **`url`**: `string` ### OrderAddress **Type:** `object` - **`address`**: `string` - **`city`**: `string` - **`complement`**: `string | null` - **`country_id`**: `string`, possible values: `"BR"` - **`district`**: `string` - **`document`**: `string | null` CPF ou CNPJ. - **`email`**: `string | null` - **`location_number`**: `string` Número do endereço. - **`name`**: `string` - **`phone`**: `string | null` - **`postal_code`**: `string` - **`state_abbr`**: `string` **Generated example:** ```json { "address": "", "city": "", "complement": null, "email": null, "district": "", "document": null, "name": "", "location_number": "", "phone": null, "postal_code": "", "state_abbr": "", "country_id": "BR" } ``` ### Order **Type:** `object` - **`created_at`**: `string`, format: `date-time` - **`delivery`**: `integer` Prazo de entrega informado na cotação, em dias úteis. - **`delivery_max`**: `integer` - **`delivery_min`**: `integer` - **`discount`**: `number` Desconto em relação ao preço de balcão, em reais. - **`format`**: `string`, possible values: `"box"` - **`from`**: [OrderAddress](#scalar-schema-orderaddress) - **`generated_at`**: `string`, format: `date-time` Quando a etiqueta foi gerada. Ausente antes disso. - **`height`**: `string` Altura da caixa, em cm. - **`id`**: `string` ID do pedido. - **`insurance_value`**: `string | number | null` Valor segurado, em reais. - **`invoice`**: `string | null` Chave de acesso da nota fiscal, ou `null` quando a etiqueta usa declaração de conteúdo. - **`length`**: `string` Comprimento da caixa, em cm. - **`own_hand`**: `boolean` - **`posted_at`**: `string`, format: `date-time` Quando a encomenda foi postada. Ausente antes disso. - **`price`**: `number` Preço pago, em reais. - **`print`**: `object` - **`url`**: `string`, format: `uri` PDF da etiqueta. - **`products`**: `array` Itens da declaração de conteúdo. Os valores vêm como texto. **Items:** - **`name`**: `string` - **`quantity`**: `string` - **`unitary_value`**: `string` - **`protocol`**: `string` Igual a `id`. - **`receipt`**: `boolean` - **`service_calculated`**: `object` Detalhes internos do cálculo do frete. Podem mudar sem aviso; não dependa deles. - **`service_id`**: `integer`, possible values: `1, 2` `1` PAC ou `2` SEDEX. Ausente para os outros serviços. - **`service_posted`**: `any` Detalhes internos da postagem. Podem mudar sem aviso; não dependa deles. - **`status`**: `string`, possible values: `"pending", "released", "posted", "delivered", "canceled"` `pending`: aguardando pagamento · `released`: paga, aguardando postagem · `posted`: postada · `delivered`: entregue · `canceled`: cancelada. - **`tags`**: [OrderTags](#scalar-schema-ordertags) Identificação do pedido na sua plataforma, enviada em `options.tags`. - **`to`**: [OrderAddress](#scalar-schema-orderaddress) - **`tracking`**: `string` Código de rastreio. Vazio até a etiqueta ser gerada. - **`updated_at`**: `string`, format: `date-time` - **`weight`**: `string` Peso da caixa, em kg. - **`width`**: `string` Largura da caixa, em cm. **Generated example:** ```json { "id": "", "protocol": "", "format": "box", "delivery": 1, "delivery_min": 1, "delivery_max": 1, "discount": 1, "height": "", "width": "", "length": "", "weight": "", "from": { "address": "", "city": "", "complement": null, "email": null, "district": "", "document": null, "name": "", "location_number": "", "phone": null, "postal_code": "", "state_abbr": "", "country_id": "BR" }, "to": { "address": "", "city": "", "complement": null, "email": null, "district": "", "document": null, "name": "", "location_number": "", "phone": null, "postal_code": "", "state_abbr": "", "country_id": "BR" }, "invoice": null, "own_hand": true, "receipt": true, "price": 1, "tracking": "", "status": "pending", "service_id": 1, "products": [ { "name": "", "quantity": "", "unitary_value": "" } ], "insurance_value": null, "generated_at": "", "posted_at": "", "created_at": "", "updated_at": "", "print": { "url": "" }, "tags": [ { "tag": "", "url": "" } ], "service_calculated": {}, "service_posted": null } ``` ### OrderListAddress **Type:** `object` - **`address`**: `string | null` - **`city`**: `string | null` - **`complement`**: `string | null` - **`district`**: `string | null` - **`document`**: `string | null` - **`name`**: `string | null` - **`number`**: `string | null` - **`phone`**: `string | null` - **`postal_code`**: `string | null` - **`state`**: `string | null` UF. **Generated example:** ```json { "name": null, "phone": null, "document": null, "postal_code": null, "address": null, "number": null, "complement": null, "state": null, "city": null, "district": null } ``` ### OrderListItem **Type:** `object` - **`carrier`**: `string`, possible values: `"correios", "jadlog", "loggi", "jt"` Transportadora. - **`created_at`**: `string | null`, format: `date-time` - **`delivery`**: `object` Prazo de entrega, em dias úteis. - **`max`**: `integer` - **`min`**: `integer` - **`from`**: [OrderListAddress](#scalar-schema-orderlistaddress) - **`order_id`**: `string` ID do pedido. - **`price`**: `object` Valores em reais. - **`discount`**: `number` Desconto SuperFrete. - **`total`**: `number` Valor com desconto. - **`without_discount`**: `number` Preço de balcão da transportadora. - **`pudo`**: `object` Ponto de postagem escolhido, quando houver. Campos `null` quando a postagem é numa agência. - **`address`**: `string | null` - **`city`**: `string | null` - **`complement`**: `string | null` - **`district`**: `string | null` - **`number`**: `string | null` - **`postal_code`**: `string | null` - **`state`**: `string | null` - **`unit`**: `string | null` Nome do ponto. - **`status`**: `string`, possible values: `"pending", "released", "posted", "delivered", "canceled"` `pending`: aguardando pagamento · `released`: paga, aguardando postagem · `posted`: postada · `delivered`: entregue · `canceled`: cancelada. - **`to`** **All of:** - [OrderListAddress](#scalar-schema-orderlistaddress) - `object` - **`email`**: `string | null` - **`tracking`**: `string | null` Código de rastreio, quando já existe. - **`updated_at`**: `string | null`, format: `date-time` **Generated example:** ```json { "order_id": "", "status": "pending", "carrier": "correios", "tracking": null, "price": { "total": 1, "discount": 1, "without_discount": 1 }, "delivery": { "min": 1, "max": 1 }, "from": { "name": null, "phone": null, "document": null, "postal_code": null, "address": null, "number": null, "complement": null, "state": null, "city": null, "district": null }, "to": { "name": null, "phone": null, "document": null, "postal_code": null, "address": null, "number": null, "complement": null, "state": null, "city": null, "district": null, "email": null }, "pudo": { "unit": null, "postal_code": null, "address": null, "number": null, "complement": null, "state": null, "city": null, "district": null }, "created_at": null, "updated_at": null } ``` ### UserAddress **Type:** `object` - **`address`**: `string` Logradouro. - **`city`**: `string` - **`complement`**: `string` - **`created_at`**: `string`, format: `date-time` - **`district`**: `string` - **`id`**: `string` - **`is_default`**: `boolean` Endereço principal da conta. - **`label`**: `string` Nome dado ao endereço. - **`latitude`**: `string` - **`longitude`**: `string` - **`number`**: `string` - **`postal_code`**: `string` - **`state`**: `string` UF. - **`uid`**: `string` ID da conta. - **`updated_at`**: `string`, format: `date-time` **Generated example:** ```json { "id": "", "uid": "", "label": "", "postal_code": "", "address": "", "number": "", "complement": "", "district": "", "city": "", "state": "", "latitude": "", "longitude": "", "is_default": true, "created_at": "", "updated_at": "" } ``` ### FirestoreTimestamp **Type:** `object` Data no formato do Firestore. - **`_nanoseconds`**: `integer` - **`_seconds`**: `integer` Segundos desde 1970-01-01T00:00:00Z. **Generated example:** ```json { "_seconds": 1, "_nanoseconds": 1 } ``` ### WebhookEvent **Type:** `string`, possible values: `"order.created", "order.released", "order.generated", "order.posted", "order.delivered", "order.cancelled", "tracking.updated"` Evento que dispara a notificação. ### OrderWebhookPayload **Type:** `object` - **`data` (required)**: `object` - **`canceled_at`**: `string | null`, format: `date-time` Cancelamento pedido pela conta. - **`created_at`**: `string | null`, format: `date-time` - **`delivered_at`**: `string | null`, format: `date-time` - **`expired_at`**: `string | null`, format: `date-time` Cancelamento automático feito pelo sistema, por exemplo etiqueta não paga. - **`generated_at`**: `string | null`, format: `date-time` - **`id`**: `string` ID do pedido na SuperFrete. - **`order_id`**: `string` Igual a `id`. - **`paid_at`**: `string | null`, format: `date-time` - **`posted_at`**: `string | null`, format: `date-time` - **`protocol`**: `null` - **`self_tracking`**: `null` - **`status`**: `string`, possible values: `"created", "released", "generated", "posted", "delivered", "cancelled"` Etapa do pedido, derivada do evento. Note a grafia `cancelled`, diferente do `canceled` das rotas de pedido. - **`tags`**: `array` Identificação do pedido enviada em `options.tags`. **Items:** - **`name`**: `string`, possible values: `"order_id"` - **`url`**: `string` - **`value`**: `string` Número do pedido na sua plataforma. - **`tracking`**: `string | null` Código de rastreio. Disponível a partir de `order.generated`. - **`tracking_url`**: `string | null` Página de rastreio. Só em `order.posted` e `order.delivered`. - **`user_id`**: `string` ID da conta dona do pedido. - **`event` (required)**: `string`, possible values: `"order.created", "order.released", "order.generated", "order.posted", "order.delivered", "order.cancelled"` **Generated example:** ```json { "event": "order.created", "data": { "id": "", "order_id": "", "protocol": null, "status": "created", "tracking": null, "self_tracking": null, "user_id": "", "tags": [ { "name": "order_id", "value": "", "url": "" } ], "created_at": null, "paid_at": null, "generated_at": null, "posted_at": null, "delivered_at": null, "canceled_at": null, "expired_at": null, "tracking_url": null } } ``` ### TrackingWebhookPayload **Type:** `object` - **`data` (required)**: `object` - **`code` (required)**: `string` Código de rastreio. - **`order_id` (required)**: `string` ID do pedido na SuperFrete. - **`status` (required)**: `string` Código da movimentação, como a transportadora informa. - **`event` (required)**: `string`, possible values: `"tracking.updated"` **Generated example:** ```json { "event": "tracking.updated", "data": { "order_id": "", "code": "", "status": "" } } ```