> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.erp.olist.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook - Cotação de Fretes

> Implemente cálculo de frete personalizado em tempo real para seu e-commerce.

## 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:

```json theme={null}
{
  "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:

```json theme={null}
{
  "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

| Campo                   | Tipo    | Descrição                            |
| ----------------------- | ------- | ------------------------------------ |
| evento                  | string  | Sempre "cotacao\_fretes"             |
| data\_hora              | string  | Data e hora da solicitação           |
| cotacao\_id             | string  | ID único da cotação (use no retorno) |
| origem.cep              | string  | CEP de origem (sua loja)             |
| origem.cidade           | string  | Cidade de origem                     |
| origem.estado           | string  | Estado de origem (UF)                |
| destino.cep             | string  | CEP de destino (cliente)             |
| destino.cidade          | string  | Cidade de destino                    |
| destino.estado          | string  | Estado de destino (UF)               |
| produtos\[]             | array   | Lista de produtos do carrinho        |
| produtos\[].id          | int     | ID do produto                        |
| produtos\[].sku         | string  | SKU do produto                       |
| produtos\[].nome        | string  | Nome do produto                      |
| produtos\[].quantidade  | int     | Quantidade                           |
| produtos\[].peso        | decimal | Peso unitário em kg                  |
| produtos\[].altura      | decimal | Altura em cm                         |
| produtos\[].largura     | decimal | Largura em cm                        |
| produtos\[].comprimento | decimal | Comprimento em cm                    |
| produtos\[].valor       | decimal | Valor unitário                       |
| valor\_total\_produtos  | decimal | Soma total dos produtos              |
| peso\_total             | decimal | Peso total em kg                     |
| volume\_total           | object  | Dimensões totais da encomenda        |

## Campos da Resposta Obrigatórios

| Campo                           | Tipo    | Descrição                           |
| ------------------------------- | ------- | ----------------------------------- |
| status                          | string  | "OK" ou "ERRO"                      |
| cotacao\_id                     | string  | Mesmo ID recebido na requisição     |
| opcoes\_frete\[]                | array   | Lista de opções de frete (mínimo 1) |
| opcoes\_frete\[].nome           | string  | Nome do serviço de frete            |
| opcoes\_frete\[].valor          | decimal | Valor do frete                      |
| opcoes\_frete\[].prazo\_entrega | int     | Prazo de entrega em dias            |

## Campos da Resposta Opcionais

| Campo                            | Tipo   | Descrição                                 |
| -------------------------------- | ------ | ----------------------------------------- |
| opcoes\_frete\[].descricao       | string | Descrição detalhada                       |
| opcoes\_frete\[].prazo\_minimo   | int    | Prazo mínimo em dias                      |
| opcoes\_frete\[].prazo\_maximo   | int    | Prazo máximo em dias                      |
| opcoes\_frete\[].codigo\_servico | string | Código interno do serviço                 |
| opcoes\_frete\[].transportadora  | string | Nome da transportadora                    |
| opcoes\_frete\[].observacoes     | string | Observações adicionais                    |
| erro                             | string | Mensagem de erro (quando status = "ERRO") |

## Exemplo de Implementação

### PHP

```php theme={null}
<?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

```python theme={null}
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

```javascript theme={null}
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:

```json theme={null}
{
  "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
