Skip to main content

Descrição

Este webhook permite que você implemente seu próprio sistema de cálculo de frete personalizado. Quando um cliente solicita uma cotação de frete no checkout, o Olist ERP enviará os dados do carrinho para sua URL e aguardará o retorno com as opções e valores de frete.

Quando é Acionado

  • Cliente solicita cálculo de frete no checkout
  • Alteração de CEP de entrega
  • Modificação de produtos no carrinho
  • Recálculo de frete solicitado manualmente

Diferença dos Outros Webhooks

Este webhook funciona de forma síncrona e bidirecional:
  • O sistema aguarda sua resposta em tempo real (timeout: 10 segundos)
  • Você deve retornar as opções de frete calculadas
  • O cliente vê as opções que você retornou

Estrutura da Requisição Recebida

O Olist ERP enviará via POST:
{
  "evento": "cotacao_fretes",
  "data_hora": "20/05/2024 21:30:15",
  "cotacao_id": "COT-123456",
  "origem": {
    "cep": "01310-100",
    "cidade": "São Paulo",
    "estado": "SP"
  },
  "destino": {
    "cep": "22041-001",
    "cidade": "Rio de Janeiro",
    "estado": "RJ"
  },
  "produtos": [
    {
      "id": 123456,
      "sku": "PROD-001",
      "nome": "Produto Exemplo",
      "quantidade": 2,
      "peso": 0.5,
      "altura": 10,
      "largura": 15,
      "comprimento": 20,
      "valor": 99.90
    },
    {
      "id": 123457,
      "sku": "PROD-002",
      "nome": "Outro Produto",
      "quantidade": 1,
      "peso": 1.2,
      "altura": 15,
      "largura": 20,
      "comprimento": 25,
      "valor": 149.90
    }
  ],
  "valor_total_produtos": 349.70,
  "peso_total": 2.2,
  "volume_total": {
    "altura": 25,
    "largura": 35,
    "comprimento": 45
  }
}

Estrutura da Resposta Esperada

Você deve retornar em até 10 segundos:
{
  "status": "OK",
  "cotacao_id": "COT-123456",
  "opcoes_frete": [
    {
      "nome": "PAC",
      "descricao": "Correios - Entrega econômica",
      "valor": 25.50,
      "prazo_entrega": 8,
      "prazo_minimo": 7,
      "prazo_maximo": 10,
      "codigo_servico": "04510",
      "transportadora": "Correios",
      "observacoes": "Dias úteis"
    },
    {
      "nome": "SEDEX",
      "descricao": "Correios - Entrega expressa",
      "valor": 45.90,
      "prazo_entrega": 3,
      "prazo_minimo": 2,
      "prazo_maximo": 4,
      "codigo_servico": "04014",
      "transportadora": "Correios",
      "observacoes": "Dias úteis"
    },
    {
      "nome": "Transportadora Própria",
      "descricao": "Entrega com frota própria",
      "valor": 35.00,
      "prazo_entrega": 5,
      "prazo_minimo": 5,
      "prazo_maximo": 5,
      "codigo_servico": "PROPRIO",
      "transportadora": "Empresa Transportes Ltda",
      "observacoes": "Entrega com agendamento"
    }
  ]
}

Campos da Requisição Recebida

CampoTipoDescrição
eventostringSempre “cotacao_fretes”
data_horastringData e hora da solicitação
cotacao_idstringID único da cotação (use no retorno)
origem.cepstringCEP de origem (sua loja)
origem.cidadestringCidade de origem
origem.estadostringEstado de origem (UF)
destino.cepstringCEP de destino (cliente)
destino.cidadestringCidade de destino
destino.estadostringEstado de destino (UF)
produtos[]arrayLista de produtos do carrinho
produtos[].idintID do produto
produtos[].skustringSKU do produto
produtos[].nomestringNome do produto
produtos[].quantidadeintQuantidade
produtos[].pesodecimalPeso unitário em kg
produtos[].alturadecimalAltura em cm
produtos[].larguradecimalLargura em cm
produtos[].comprimentodecimalComprimento em cm
produtos[].valordecimalValor unitário
valor_total_produtosdecimalSoma total dos produtos
peso_totaldecimalPeso total em kg
volume_totalobjectDimensões totais da encomenda

Campos da Resposta Obrigatórios

CampoTipoDescrição
statusstring”OK” ou “ERRO”
cotacao_idstringMesmo ID recebido na requisição
opcoes_frete[]arrayLista de opções de frete (mínimo 1)
opcoes_frete[].nomestringNome do serviço de frete
opcoes_frete[].valordecimalValor do frete
opcoes_frete[].prazo_entregaintPrazo de entrega em dias

Campos da Resposta Opcionais

CampoTipoDescrição
opcoes_frete[].descricaostringDescrição detalhada
opcoes_frete[].prazo_minimointPrazo mínimo em dias
opcoes_frete[].prazo_maximointPrazo máximo em dias
opcoes_frete[].codigo_servicostringCódigo interno do serviço
opcoes_frete[].transportadorastringNome da transportadora
opcoes_frete[].observacoesstringObservações adicionais
errostringMensagem de erro (quando status = “ERRO”)

Exemplo de Implementação

PHP

<?php
header('Content-Type: application/json');

// Recebe a requisição
$json = file_get_contents('php://input');
$data = json_decode($json, true);

if ($data['evento'] === 'cotacao_fretes') {
    $cotacao_id = $data['cotacao_id'];
    $cep_origem = $data['origem']['cep'];
    $cep_destino = $data['destino']['cep'];
    $peso_total = $data['peso_total'];
    $valor_total = $data['valor_total_produtos'];

    // Array para armazenar opções de frete
    $opcoes_frete = [];

    // 1. Calcula frete Correios via API
    $frete_pac = calcularFreteCorreios($cep_origem, $cep_destino, $peso_total, '04510');
    $opcoes_frete[] = [
        'nome' => 'PAC',
        'descricao' => 'Correios - Entrega econômica',
        'valor' => $frete_pac['valor'],
        'prazo_entrega' => $frete_pac['prazo'],
        'transportadora' => 'Correios',
        'codigo_servico' => '04510'
    ];

    $frete_sedex = calcularFreteCorreios($cep_origem, $cep_destino, $peso_total, '04014');
    $opcoes_frete[] = [
        'nome' => 'SEDEX',
        'descricao' => 'Correios - Entrega expressa',
        'valor' => $frete_sedex['valor'],
        'prazo_entrega' => $frete_sedex['prazo'],
        'transportadora' => 'Correios',
        'codigo_servico' => '04014'
    ];

    // 2. Frete grátis para valores acima de R$ 200
    if ($valor_total >= 200) {
        array_unshift($opcoes_frete, [
            'nome' => 'Frete Grátis',
            'descricao' => 'Promoção - Frete grátis para compras acima de R$ 200',
            'valor' => 0.00,
            'prazo_entrega' => 10,
            'transportadora' => 'Correios',
            'observacoes' => 'Aproveite!'
        ]);
    }

    // 3. Frete próprio para mesma região
    if ($data['origem']['estado'] === $data['destino']['estado']) {
        $opcoes_frete[] = [
            'nome' => 'Entrega Regional',
            'descricao' => 'Frota própria - Mesmo estado',
            'valor' => 25.00,
            'prazo_entrega' => 3,
            'transportadora' => 'Transportadora Própria'
        ];
    }

    // Retorna as opções
    echo json_encode([
        'status' => 'OK',
        'cotacao_id' => $cotacao_id,
        'opcoes_frete' => $opcoes_frete
    ]);
}

function calcularFreteCorreios($cep_origem, $cep_destino, $peso, $servico) {
    // Implementação da consulta aos Correios
    // Retorna ['valor' => 25.50, 'prazo' => 8]
    // ... código de integração com Correios ...
}
?>

Python

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

@app.route('/webhook/cotacao-frete', methods=['POST'])
def cotacao_frete():
    data = request.get_json()

    if data.get('evento') == 'cotacao_fretes':
        cotacao_id = data['cotacao_id']
        cep_origem = data['origem']['cep']
        cep_destino = data['destino']['cep']
        peso_total = data['peso_total']
        valor_total = data['valor_total_produtos']

        opcoes_frete = []

        # 1. Consulta Melhor Envio
        melhor_envio = consultar_melhor_envio(cep_origem, cep_destino, peso_total)
        opcoes_frete.extend(melhor_envio)

        # 2. Regra de negócio: frete grátis
        if valor_total >= 200:
            opcoes_frete.insert(0, {
                'nome': 'Frete Grátis',
                'descricao': 'Parabéns! Você ganhou frete grátis',
                'valor': 0.00,
                'prazo_entrega': 10,
                'observacoes': 'Compras acima de R$ 200'
            })

        # 3. Frete customizado por região
        frete_regional = calcular_frete_regional(
            data['origem']['estado'],
            data['destino']['estado'],
            peso_total
        )
        if frete_regional:
            opcoes_frete.append(frete_regional)

        return jsonify({
            'status': 'OK',
            'cotacao_id': cotacao_id,
            'opcoes_frete': opcoes_frete
        })

    return jsonify({'status': 'ERRO', 'erro': 'Evento não reconhecido'}), 400

def consultar_melhor_envio(origem, destino, peso):
    # Integração com API do Melhor Envio
    # Retorna lista de opções
    return [
        {
            'nome': 'PAC',
            'valor': 25.50,
            'prazo_entrega': 8,
            'transportadora': 'Correios'
        },
        {
            'nome': 'SEDEX',
            'valor': 45.90,
            'prazo_entrega': 3,
            'transportadora': 'Correios'
        }
    ]

def calcular_frete_regional(uf_origem, uf_destino, peso):
    if uf_origem == uf_destino:
        return {
            'nome': 'Regional',
            'valor': 20.00,
            'prazo_entrega': 5,
            'transportadora': 'Frota Própria'
        }
    return None

if __name__ == '__main__':
    app.run(port=3000)

Node.js

const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

app.post('/webhook/cotacao-frete', async (req, res) => {
    const data = req.body;

    if (data.evento === 'cotacao_fretes') {
        const { cotacao_id, origem, destino, peso_total, valor_total_produtos } = data;

        try {
            const opcoesFrete = [];

            // 1. Consulta API de transportadoras
            const correios = await consultarCorreios(origem.cep, destino.cep, peso_total);
            opcoesFrete.push(...correios);

            // 2. Tabela própria de frete
            const freteTabela = calcularFretePorTabela(origem.estado, destino.estado, peso_total);
            if (freteTabela) {
                opcoesFrete.push(freteTabela);
            }

            // 3. Regras promocionais
            if (valor_total_produtos >= 200) {
                opcoesFrete.unshift({
                    nome: 'Frete Grátis',
                    descricao: 'Promoção especial!',
                    valor: 0.00,
                    prazo_entrega: 10,
                    observacoes: 'Válido para compras acima de R$ 200'
                });
            }

            // 4. Frete expresso (se disponível)
            if (isFreteExpressoDisponivel(origem, destino)) {
                opcoesFrete.push({
                    nome: 'Entrega Expressa 24h',
                    descricao: 'Receba amanhã',
                    valor: 89.90,
                    prazo_entrega: 1,
                    transportadora: 'Loggi',
                    observacoes: 'Apenas capitais'
                });
            }

            res.status(200).json({
                status: 'OK',
                cotacao_id: cotacao_id,
                opcoes_frete: opcoesFrete
            });

        } catch (error) {
            console.error('Erro ao calcular frete:', error);
            res.status(200).json({
                status: 'ERRO',
                cotacao_id: cotacao_id,
                erro: 'Não foi possível calcular o frete no momento'
            });
        }
    } else {
        res.status(400).json({ status: 'ERRO', erro: 'Evento inválido' });
    }
});

async function consultarCorreios(cepOrigem, cepDestino, peso) {
    // Integração com API dos Correios
    // Retorna array de opções
    return [
        {
            nome: 'PAC',
            valor: 25.50,
            prazo_entrega: 8,
            transportadora: 'Correios',
            codigo_servico: '04510'
        },
        {
            nome: 'SEDEX',
            valor: 45.90,
            prazo_entrega: 3,
            transportadora: 'Correios',
            codigo_servico: '04014'
        }
    ];
}

function calcularFretePorTabela(ufOrigem, ufDestino, peso) {
    // Lógica de tabela própria
    const tabelaFrete = {
        'SP-SP': { valor: 15.00, prazo: 3 },
        'SP-RJ': { valor: 20.00, prazo: 5 },
        'SP-MG': { valor: 18.00, prazo: 4 }
    };

    const chave = `${ufOrigem}-${ufDestino}`;
    const frete = tabelaFrete[chave];

    if (frete) {
        return {
            nome: 'Transportadora Regional',
            valor: frete.valor + (peso * 2), // R$ 2 por kg adicional
            prazo_entrega: frete.prazo,
            transportadora: 'Regional Transportes'
        };
    }

    return null;
}

function isFreteExpressoDisponivel(origem, destino) {
    const capitais = ['São Paulo', 'Rio de Janeiro', 'Belo Horizonte', 'Curitiba'];
    return capitais.includes(origem.cidade) && capitais.includes(destino.cidade);
}

app.listen(3000, () => {
    console.log('Webhook de cotação de frete rodando na porta 3000');
});

Configuração

Para ativar este webhook:
  1. Acesse Configurações > Integrações > Webhooks
  2. Clique em Novo Webhook
  3. Selecione o evento Cotação de Fretes
  4. Informe a URL do seu endpoint
  5. Configure o timeout (padrão: 10 segundos)
  6. Salve as configurações

Casos de Uso

  • Integração com transportadoras: Consulte APIs de múltiplas transportadoras
  • Regras personalizadas: Implemente frete grátis, progressivo, etc.
  • Tabelas próprias: Use suas próprias tabelas de frete
  • Frete dinâmico: Calcule baseado em promoções e regras de negócio
  • Multi-transportadora: Ofereça múltiplas opções ao cliente
  • Frete inteligente: Escolha a melhor opção automaticamente

Boas Práticas

  • Performance: Responda em menos de 5 segundos (timeout: 10s)
  • Cache: Use cache para CEPs consultados recentemente
  • Fallback: Tenha uma opção padrão se APIs externas falharem
  • Múltiplas opções: Ofereça pelo menos 2-3 opções de frete
  • Valores claros: Seja transparente com prazos e valores
  • Tratamento de erro: Sempre retorne uma resposta válida
  • Log de cotações: Registre todas as cotações para análise

Tratamento de Erros

Se não conseguir calcular o frete:
{
  "status": "ERRO",
  "cotacao_id": "COT-123456",
  "erro": "CEP de destino inválido ou não atendido"
}
O sistema então usará as regras de frete padrão configuradas no Olist ERP.

Timeout

  • Timeout máximo: 10 segundos
  • Recomendado: Responder em até 5 segundos
  • Se exceder: Sistema usa cálculo padrão do Olist ERP

Observações

  • Este é o único webhook síncrono da API v2
  • O cliente aguarda a resposta para ver as opções de frete
  • Performance é crítica - otimize consultas e use cache
  • Sempre retorne pelo menos uma opção de frete válida
  • Valores devem ser positivos (use 0.00 para frete grátis)
  • Prazos são sempre em dias úteis (exceto se especificado nas observações)
  • O cotacao_id deve ser retornado exatamente como recebido