Pular para conteúdo

Frete e Cotação

A Gubee mantém um motor interno de cotação de frete que combina regras configuradas pelo seller (tabela de fretes próprios) com integrações diretas a transportadoras (Correios, Intelipost e outras). Esta página descreve como um parceiro pode cotar frete para um conjunto de SKUs destino um CEP.

Modelo de dados

Entidade Descrição
Carrier (Transportadora) Empresa que faz a entrega física (Correios, Jadlog, Intelipost como agregador, etc)
Region (Região) Faixa de CEPs atendida por um conjunto de transportadoras
Freight Value (Valor de Frete) Combinação Carrier × Region × Faixa de peso que define preço e prazo
Freight Type Categoria de serviço: STANDARD, EXPRESS, ECONOMIC, etc
Quote (Cotação) Resultado de um cálculo de frete para 1+ SKUs destino um CEP

A configuração de carriers/regions/values é feita via UI interna do admin (Fretes > Transportadora, Fretes > Região, Fretes > Valores de Frete). Esses endpoints estão em /bffweb/* e não são expostos na API de integração pública — parceiros só consomem cotações via /integration/quotes.

Endpoint de cotação

A API pública de cotação é aberta (não requer JWT do seller):

POST /integration/quotes/{sellerId}/{postalCode}
  • {sellerId}: identificador do seller cuja tabela de frete será usada
  • {postalCode}: CEP de destino (apenas dígitos, ex.: 01310100)
  • Body: lista de quotationItems (skuId + quantidade)
  • Response: lista de shippingQuotes com carrier, service, cost, deliveryTime (prazo em horas) e deadlineDays (prazo em dias úteis)

Por que esse endpoint é público (sem JWT)?

Cotação de frete acontece tipicamente no checkout de e-commerce, antes do login do comprador. Exigir JWT do seller seria impraticável nesse cenário. Por isso o sellerId é recebido como path variable — qualquer comprador pode cotar frete para qualquer seller.

Não abuse da exceção

O sellerId no path é exclusivo deste endpoint. Todos os outros endpoints /integration/* ignoram qualquer sellerId enviado no body e usam o sellerId extraído do JWT. Veja Autenticação.

Request

curl -X POST 'https://api.gubee.com.br/integration/quotes/SEU_SELLER_ID/01310100' \
  -H 'Content-Type: application/json' \
  -d '{
    "quotationItems": [
      { "skuId": "ABCD-1234-EFGH-5678", "qty": 2 },
      { "skuId": "IJKL-9012-MNOP-3456", "qty": 1 }
    ]
  }'

Response (200 OK)

{
  "postalCode": 1310100,
  "shippingQuotes": [
    {
      "valueFreightId": "vf-001",
      "freightServiceId": "sedex",
      "freightServiceName": "Sedex",
      "freightType": "STANDARD",
      "deliveryTime": 48,
      "deadlineDays": 2,
      "carrierId": "correios",
      "carrierName": "Correios",
      "weight": { "value": 2.5, "unit": "KILOGRAM" },
      "shippingCost": 32.90,
      "skuQty": { "skuId": "ABCD-1234-EFGH-5678", "qty": 2 }
    },
    {
      "valueFreightId": "vf-002",
      "freightServiceId": "pac",
      "freightServiceName": "PAC",
      "freightType": "ECONOMIC",
      "deliveryTime": 96,
      "deadlineDays": 5,
      "carrierId": "correios",
      "carrierName": "Correios",
      "weight": { "value": 2.5, "unit": "KILOGRAM" },
      "shippingCost": 22.50,
      "skuQty": { "skuId": "ABCD-1234-EFGH-5678", "qty": 2 }
    }
  ]
}

Campos da resposta

Campo Tipo Descrição
postalCode Long CEP de destino (echo do path)
shippingQuotes Set Lista de opções de frete (uma por combinação carrier × serviço)
shippingQuotes[].valueFreightId String ID interno da regra de frete que produziu a cotação
shippingQuotes[].freightServiceId String ID do serviço na transportadora (ex.: sedex, pac, jadlog-package)
shippingQuotes[].freightServiceName String Nome amigável do serviço
shippingQuotes[].freightType Enum Tipo: STANDARD, EXPRESS, ECONOMIC, etc
shippingQuotes[].deliveryTime Int Prazo em horas (para o SKU descrito em skuQty)
shippingQuotes[].deadlineDays Int Prazo em dias úteis (echo amigável)
shippingQuotes[].carrierId String ID da transportadora (ex.: correios, intelipost)
shippingQuotes[].carrierName String Nome amigável da transportadora
shippingQuotes[].weight Object Peso considerado no cálculo (value + unit)
shippingQuotes[].shippingCost BigDecimal Custo do frete em BRL
shippingQuotes[].skuQty Object SKU ao qual esta cotação se refere (pode haver uma quote por SKU)

Erros comuns

HTTP Causa
404 skuId não encontrado para o seller, ou SKU sem dimensão/peso cadastrado
400 postalCode inválido, qty <= 0, ou quotationItems vazio
422 Região do CEP não atendida por nenhuma transportadora do seller

Tipos de frete (FreightType)

Valor Significado
STANDARD Padrão — equilíbrio custo vs prazo
EXPRESS Entrega expressa (ex.: Sedex)
ECONOMIC Mais barato, prazo mais longo (ex.: PAC)
SAME_DAY Mesmo dia (apenas cidades selecionadas)
NEXT_DAY Próximo dia
PICKUP Retirada no CD (sem entrega física)

A disponibilidade de cada tipo depende da configuração de carriers e regions do seller.

Integrações com transportadoras

A Gubee integra nativamente com as seguintes transportadoras:

Integração Categoria Como configurar
Correios Logística essencial Admin > Fretes > Transportadora > adicionar Correios com contrato/serviços
Intelipost Agregador multi-carrier Admin > Fretes > Transportadora > adicionar Intelipost com API key

Quando o seller usa Intelipost, a Gubee delega o cálculo de frete para o motor da Intelipost, que retorna múltiplas opções de carriers reais (Jadlog, Azul Cargo, Braspress, etc). Nesse caso, o campo carrierName na resposta pode ser diferente de "Correios".

Quando o seller usa tabela própria (sem agregador), a cotação é calculada internamente pela Gubee a partir das Freight Values configuradas.

Quando uma cotação falha silenciosamente

Às vezes a resposta vem com shippingQuotes vazio ([]) mesmo para um CEP válido. Causas típicas:

  1. SKU sem dimensão: o cálculo de frete precisa de peso + dimensões. Verifique dimension.weight/height/width/length na variação (GET /integration/products/v2/bySkuId/{skuId}).
  2. Região não atendida: o CEP destino não cai em nenhuma Region configurada pelo seller. Adicione a região no admin.
  3. Faixa de peso sem valor: o SKU excede o peso máximo de todas as faixas configuradas para a region.
  4. Carrier inativa: a transportadora foi desativada no admin mas a region continua apontando pra ela.

Em todos os casos, a cotação não retorna erro explícito — apenas []. O checkout do parceiro deve tratar isso como "frete indisponível" e sugerir CEP alternativo ou contato com o seller.

Performance

  • A cotação é síncrona (sem plataforma de streaming de eventos): chamou, recebeu a resposta na mesma requisição.
  • Latência típica: 100-300ms para tabela própria; 500-2000ms para integração externa (Correios/Intelipost).
  • Rate limit recomendado: máx 10 cotações/segundo por seller. Acima disso, considere cachear resultados por (skuId, qty, CEP) por 5 minutos.

Próximos passos


Apêndice: Contrato de cotação externa (Gubee chama você)

Além da cotação interna (motor da Gubee), existe um cenário em que o seller quer usar seu próprio motor de frete (ERP, transportadora própria, etc). Nesse caso, a Gubee chama o endpoint do seller durante o checkout e usa a resposta para exibir opções de frete ao comprador.

Este é o fluxo reverso: ao invés de o parceiro chamar POST /integration/quotes, o parceiro implementa um endpoint HTTP e a Gubee o chama como cliente.

Como ativar

Painel da Gubee > Fretes > Transportadora:

  1. Adicionar uma nova transportadora
  2. Marcar a flag "Utilizar integração"
  3. Selecionar a opção "Api"
  4. Informar a URL do seu endpoint no campo "Url para cotação"
  5. Adicionar pelo menos 1 tipo de serviço (Standard, Express, etc) à transportadora
  6. Em Fretes > Região, criar uma região (faixa de CEP) e associar essa transportadora — sempre que um checkout cair nessa região, a Gubee chama seu endpoint

Contrato do endpoint

Implemente um endpoint público (sem autenticação) com as características abaixo. A Gubee é o cliente; você é o servidor.

POST https://{seu-endpoint}/{postalCode}
Content-Type: application/json
  • {postalCode}: CEP de destino, apenas dígitos (path parameter)
  • Tempo máximo aceito pela Gubee: 650ms — acima disso, a Gubee descarta a resposta e usa apenas cotações internas. Otimize agressivamente.

Payload de entrada (Gubee envia para você)

{
  "orderTotalPrice": 1999.90,
  "platform": "MERCADOLIVRE",
  "quotationItems": [
    {
      "dimension": {
        "depthInCm": 54.0,
        "heightInCm": 23.0,
        "weightInGr": 2500.0,
        "widthInCm": 41.0
      },
      "price": 199.99,
      "qty": 10,
      "skuId": "28"
    }
  ]
}
Campo Tipo Descrição
orderTotalPrice BigDecimal Total do pedido (informado pelo marketplace OU soma de itens × preço cadastrado)
platform String Nome do marketplace (MERCADOLIVRE, AMAZON, B2W, SHOPEE, ALIEXPRESS, etc). Pode ser null — não conte com ele
quotationItems[] Array Lista de SKUs a cotar
quotationItems[].dimension.depthInCm Double Profundidade unitária em cm
quotationItems[].dimension.heightInCm Double Altura unitária em cm
quotationItems[].dimension.weightInGr Double Peso unitário em gramas
quotationItems[].dimension.widthInCm Double Largura unitária em cm
quotationItems[].price BigDecimal Preço unitário do SKU
quotationItems[].qty Int Quantidade no pedido
quotationItems[].skuId String Identificador interno da variação na Gubee

Payload de resposta (você retorna para a Gubee)

{
  "postalCode": 1310100,
  "shippingQuotes": [
    {
      "carrierId": "5f43cb7085609a3239a38cfc",
      "carrierName": "TRANSPORTADORA",
      "deadlineDays": 8,
      "deliveryTime": 8,
      "freightServiceId": "fd8f7ae0-77f5-4cda-89ab-88dbb8ab0ed7",
      "freightServiceName": "ECONOMICO",
      "freightType": "ECONOMIC",
      "shippingCost": 32.8125,
      "skuQty": {
        "crossDockingTime": 0,
        "handlingTime": 0,
        "qty": 2,
        "skuId": "28",
        "stockQty": 6
      },
      "valueFreightId": "62b3507361f0761d57391ed0",
      "weight": {
        "type": "GRAM",
        "value": 5000.0
      }
    }
  ]
}
Campo Tipo Descrição
postalCode Long Echo do CEP de destino
shippingQuotes[] Array Lista de opções de frete
shippingQuotes[].carrierId String ID interno da sua transportadora
shippingQuotes[].carrierName String Nome amigável
shippingQuotes[].deadlineDays Int Prazo em dias úteis
shippingQuotes[].deliveryTime Int Prazo em dias (echo)
shippingQuotes[].freightServiceId String ID do serviço na transportadora
shippingQuotes[].freightServiceName String Nome amigável
shippingQuotes[].freightType Enum Obrigatório: ECONOMIC, NORMAL ou EXPRESS
shippingQuotes[].shippingCost BigDecimal Custo em BRL
shippingQuotes[].skuQty.crossDockingTime Int Tempo de cross-docking em dias
shippingQuotes[].skuQty.handlingTime Int Tempo de manipulação em dias
shippingQuotes[].skuQty.qty Int Quantidade cotada (pode diferir da entrada se vocês vendem em pack múltiplo)
shippingQuotes[].skuQty.skuId String Echo do skuId
shippingQuotes[].skuQty.stockQty Int Estoque disponível (opcional — usado para UX no checkout)
shippingQuotes[].valueFreightId String ID interno da sua regra de frete
shippingQuotes[].weight.type String Unidade: GRAM, KILOGRAM
shippingQuotes[].weight.value Double Peso total considerado

Validações importantes

  • freightType DEVE ser um destes: ECONOMIC, NORMAL, EXPRESS. Outros valores são rejeitados silenciosamente (a cotação some do checkout).
  • HTTP status 2xx com body JSON válido. Qualquer outro status (4xx, 5xx) descarta a cotação inteira.
  • Latência ≤ 650ms. Use cache agressivo por (skuId, qty, CEP). Se você precisa de mais tempo, não use este contrato — crie uma cotação assíncrona na sua ponta e use um webhook pra depois atualizar via PUT /prices.

Exemplo de implementação (pseudocódigo)

# Flask / FastAPI / qualquer framework
@app.post("/freight/<int:postal_code>")
def quote_freight(postal_code: int):
    body = request.json
    items = body.get("quotationItems", [])

    # Sua lógica: consulta tabela propria, chama transportadora, etc
    quotes = []
    for item in items:
        # Exemplo simples: R$ 0.50 por grama + taxa fixa R$ 10
        weight_kg = item["dimension"]["weightInGr"] / 1000
        cost = (weight_kg * 0.50 * item["qty"]) + 10
        quotes.append({
            "carrierId": "my-carrier-001",
            "carrierName": "Minha Transportadora",
            "deadlineDays": 5,
            "deliveryTime": 5,
            "freightServiceId": "std",
            "freightServiceName": "Padrão",
            "freightType": "NORMAL",
            "shippingCost": round(cost, 2),
            "skuQty": {
                "crossDockingTime": 1,
                "handlingTime": 1,
                "qty": item["qty"],
                "skuId": item["skuId"],
                "stockQty": 999
            },
            "valueFreightId": "rule-001",
            "weight": {
                "type": "GRAM",
                "value": item["dimension"]["weightInGr"] * item["qty"]
            }
        })

    return jsonify({
        "postalCode": postal_code,
        "shippingQuotes": quotes
    })

Quando usar este contrato vs /integration/quotes

Cenário Use
Você tem tabela de frete interna na Gubee (Correios, Intelipost ou manual) POST /integration/quotes (cotação interna)
Você quer usar seu ERP como motor de frete durante o checkout na Gubee Este contrato (cotação externa)
Você só precisa de uma cotação para exibir no seu site/app, sem ser parte do checkout POST /integration/quotes

A diferença fundamental: /integration/quotes é o parceiro pedindo cotação pra Gubee usar; este contrato é a Gubee pedindo cotação pro parceiro durante o fluxo de compra real na Gubee.