API B2B
Cote, reserve e acompanhe seus fretes direto do seu ERP ou TMS, sem falar com ninguém. A especificação completa está em OpenAPI 3.1.
Autenticação e ambientes
Envie a chave no header Authorization: Bearer <chave>. Chaves mv_test_ operam no sandbox: os pedidos ficam marcados como teste, nenhum transportador é contatado e nenhum documento fiscal é emitido. Chaves mv_live_ operam em produção. Cada chave tem escopos e um limite por minuto; ao passar do limite a resposta é 429. Uma chave revogada recebe 401 na hora.
Idempotência
Todo POST aceita Idempotency-Key. Repetir a mesma chave com o mesmo corpo devolve a resposta original com Idempotent-Replayed: true; com outro corpo devolve 422; enquanto a primeira chamada ainda roda, 409. Use o ID do pedido no seu sistema como chave.
Endpoints
| Método | Caminho | Escopo | O que faz |
|---|---|---|---|
| POST | /b2b/quotes | quotes:write | Cota um frete. Retorna preço firme (contrato ou histórico suficiente) ou indicativo com faixa. |
| GET | /b2b/quotes/{id} | quotes:write | Consulta uma cotação e se já foi reservada. |
| POST | /b2b/quotes/{id}/book | orders:write | Reserva a cotação e cria o pedido. Cotação firme com contrato de aceite automático já fica confirmada. |
| GET | /b2b/orders/{id} | orders:read | Estado do pedido (stage), preço e transportador. |
| POST | /b2b/orders/{id}/accept | orders:write | Aceita o preço firme enviado para um pedido reservado com preço indicativo. |
| POST | /b2b/orders/{id}/nfe | orders:write | Anexa a NF-e (XML ou chave de acesso). Completa peso, valor, remetente e destinatário. |
| GET | /b2b/orders/{id}/tracking | orders:read | Posições e ETA. |
| GET | /b2b/orders/{id}/documents | orders:read | Documentos do pedido (CT-e, MDF-e, comprovantes). |
| POST | /b2b/orders/{id}/cancel | orders:write | Cancela enquanto o transporte não começou. |
Cotar
curl -X POST https://api.moveraglobal.com.br/v1/b2b/quotes \
-H "Authorization: Bearer mv_test_xxxxxxxx_..." \
-H "Idempotency-Key: pedido-erp-4711" \
-H "Content-Type: application/json" \
-d '{
"origin": "Campinas - SP",
"destination": "Curitiba - PR",
"vehicle_type": "truck",
"weight_kg": 9000,
"cargo_value": 80000
}'{
"id": "34bb41ab-...",
"mode": "indicative",
"price": 3100,
"price_low": 2860,
"price_high": 3440,
"currency": "BRL",
"distance_km": 423.1,
"components": { "ad_valorem": 150, "gris": 100, "tde": 0, "tolls": 0 },
"contracted": false,
"expires_at": "2026-10-06T11:28:49-03:00"
}mode: "firm" é um preço garantido até expires_at. indicative é uma estimativa: ao reservar, enviamos o preço firme pelo webhook quote.firm e você confirma com POST /b2b/orders/{id}/accept.
Webhooks
Cadastre a URL (HTTPS) no painel. Cada evento chega como POST JSON assinado com HMAC-SHA256 no header X-Movera-Signature: t=<unix>,v1=<assinatura>. Responda 2xx em até 10 s. Falhas são reenviadas até 8 vezes com espera crescente (até 6 h); após 20 eventos seguidos sem sucesso o webhook é desativado. Use o id do evento para descartar duplicados.
{
"id": "evt_9b1d...",
"type": "order.confirmed",
"created_at": "2026-10-05T14:28:49Z",
"sandbox": true,
"data": { "order_id": "16577105-...", "previous_state": "proposal_sent", "state": "confirmed" }
}quote.firm— O preço firme de um pedido reservado como indicativo ficou pronto.order.confirmed— Pedido confirmado.carrier.assigned— Transportador definido.pickup.confirmed— Coleta confirmada; a carga está em trânsito.delivered— Entrega registrada.delivery.confirmed— Entrega confirmada com comprovante.payment.confirmed— Pagamento recebido.order.completed— Pedido encerrado.order.cancelled— Pedido cancelado.
Validação da assinatura em Go:
package movera
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
"time"
)
// VerifySignature checks X-Movera-Signature: t=<unix>,v1=<hex hmac-sha256(secret, "<t>.<body>")>.
func VerifySignature(secret, header string, body []byte) bool {
var ts, sig string
for _, part := range strings.Split(header, ",") {
if v, ok := strings.CutPrefix(part, "t="); ok {
ts = v
} else if v, ok := strings.CutPrefix(part, "v1="); ok {
sig = v
}
}
unix, err := strconv.ParseInt(ts, 10, 64)
if err != nil || time.Since(time.Unix(unix, 0)).Abs() > 5*time.Minute {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "." + string(body)))
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(sig))
}Erros
Os erros têm o formato {"code": "...", "error": "..."}. Os mais comuns: unauthorized (401), forbidden (403), not_found (404), quote_unavailable (409, cotação vencida ou já reservada), duplicate_nfe (409), place_not_resolved e no_price (422).