store.loog.ai — API v1

API HTTP+JSON para integração programática com os serviços da Loog. Esta v1 cobre o produto vpo-sem-parar (Vale-Pedágio Obrigatório via Sem Parar). Próximos produtos: ciot-ailog, cte-*, mdfe-* — todos seguirão a mesma taxonomia.

Base URL

https://store.loog.ai/api/v1

Não há subdomain por ambiente — o ambiente (HMLG ou PROD) é definido pelo token.

Autenticação

Todas as requests precisam de um API token no header:

Authorization: Bearer loog_prod_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Ou, opcionalmente:

X-Loog-Token: loog_prod_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Como obter um token

  1. Acesse https://store.loog.ai/configuracoes/api-tokens com um usuário gestor/admin do seu tenant
  2. Clique "Novo token", dê um nome descritivo (ex: "ERP SAP", "Querocarga", "Mobile App") e escolha o ambiente
  3. Copie o token imediatamente — ele só aparece uma vez. Depois fica só o hash no banco.

Estrutura do token

loog_{env}_{base64url(32 bytes aleatórios)}
  • loog_hmlg_* — só atua em HMLG (homologação, dados fake, sem cobrança)
  • loog_prod_* — atua em PROD (emite VPO real, debita saldo)

Um token PROD nunca consegue acessar dados/viagens de HMLG e vice-versa — isolamento estrito.

Códigos de erro de autenticação

Status Significado
401 Token ausente Header não veio ou está malformatado
401 Token inválido Token não bate com nenhum hash ativo
401 Token revogado Token foi revogado pelo admin do tenant
401 Token expirado Passou da data expires_at
403 Token sem permissão pro serviço "X" Token foi criado sem scope para esse produto

Taxonomia de endpoints

/api/v1/{produto}/{ação}[/{id}]
  • {produto} é o serviço Loog: vpo-sem-parar, ciot-ailog (futuro), etc.
  • {ação} é o verbo: route-preview, emit, status, cancel, etc.
  • {id} opcional dependendo da ação (status e cancel precisam, emit e preview não)

Endpoints disponíveis na v1

Método Endpoint Descrição
POST /v1/vpo/sem-parar/route-preview Calcula praças+custo de uma rota (sem emitir)
POST /v1/vpo/sem-parar/verificar-placa Verifica se a placa está apta (TAG Sem Parar ativa)
POST /v1/vpo/sem-parar/emit Emite um VPO completo
GET /v1/vpo/sem-parar/list Lista VPOs do tenant (paginado, filtros)
GET /v1/vpo/sem-parar/status/{viagemId} Consulta status + recibo de uma viagem emitida
POST /v1/vpo/sem-parar/cancel/{viagemId} Cancela uma viagem (estorna créditos em prod)
GET /v1/vpo/sem-parar/saldo Saldo de créditos pré-pagos Loog do tenant

1. Preview de rota — calcular custo antes de emitir

POST /api/v1/vpo/sem-parar/route-preview

Útil pra mostrar ao usuário do seu sistema (ERP, marketplace) quanto vai custar o VPO antes de pagar. Não emite nada, não debita saldo.

Request

{
  "origem":  { "lat": -18.9186, "lng": -48.2772 },
  "destino": { "lat": -23.5505, "lng": -46.6333 },
  "paradas": [
    { "lat": -19.7472, "lng": -47.9381 }
  ],
  "eixos": 6
}

Campos:

  • origem (obrigatório): { lat, lng } numéricos
  • destino (obrigatório): idem
  • paradas (opcional): array de { lat, lng } — paradas intermediárias na ordem da viagem
  • eixos (opcional, default 6): número de eixos do veículo (2 a 10)

Response 200

{
  "ok": true,
  "env": "prod",
  "totalKm": 870,
  "custoTotal": 235.67,
  "eixos": 6,
  "pracas": [
    {
      "id": 572,
      "praca": "UBERABA KM 104+900 SUL",
      "rodovia": "BR 050",
      "km": 104.9,
      "concessionaria": "MGO RODOVIAS",
      "tarifa": 15.80,
      "lat": -19.754353,
      "lng": -47.933698
    },
    { "id": 574, "praca": "DELTA KM 198+060 SUL", "rodovia": "BR 050", "tarifa": 11.20, /* ... */ }
  ],
  "segmentos": [
    { "from": "-18.9186,-48.2772", "to": "-19.7472,-47.9381", "custo": 27.00, "pracas": 2, "km": 110 },
    { "from": "-19.7472,-47.9381", "to": "-23.5505,-46.6333", "custo": 208.67, "pracas": 10, "km": 760 }
  ]
}

Erros

Status Mensagem
400 origem e destino devem ter { lat, lng } numéricos
502 Falha roteirizar segmento N — Sem Parar fora do ar ou rota sem pedágio

2. Emitir VPO

POST /api/v1/vpo/sem-parar/emit

Cria (ou atualiza) motorista, veículo e rota; emite o VPO no Sem Parar; debita o saldo. Operação atômica — ou tudo dá certo, ou tudo falha.

Request

{
  "motorista": {
    "cpfCnpj": "32984580151",
    "nome": "Adenilson Ferreira da Silva",
    "rntrc": "004398915",
    "telefone": "+5511999999999"
  },
  "veiculo": {
    "placa": "ABC1D23",
    "eixos": 6,
    "tipo": "cavalo"
  },
  "rota": {
    "origem":  { "lat": -18.9186, "lng": -48.2772, "label": "Uberlândia/MG" },
    "destino": { "lat": -23.5505, "lng": -46.6333, "label": "São Paulo/SP" },
    "paradas": []
  },
  "periodo": {
    "inicio": "2026-06-01",
    "fim": "2026-06-05"
  },
  "referencia_externa": "PEDIDO-12345"
}

Campos:

  • motorista.cpfCnpj (obrigatório): só números, 11 ou 14 dígitos, DV válido
  • motorista.nome (obrigatório): razão social ou nome completo
  • motorista.rntrc (opcional): se não passar, é resolvido pelo Sem Parar via lookup do CPF
  • veiculo.placa (obrigatório): 7 caracteres alfanuméricos (formato Mercosul ou antigo)
  • veiculo.eixos (obrigatório): 2 a 10
  • rota.{origem,destino}.lat,lng (obrigatório): coordenadas
  • rota.{origem,destino}.label (opcional): rótulo legível pra aparecer no recibo
  • rota.paradas (opcional): paradas intermediárias
  • periodo.inicio/fim (obrigatório): YYYY-MM-DD, máximo 15 dias de janela
  • referencia_externa (opcional): seu ID interno (contrato, pedido). Aparece em /status pra correlação.

Response 201

{
  "ok": true,
  "viagemId": "75e64d34-4667-41d3-9220-09f32ebdf3cd",
  "env": "prod",
  "sem_parar_codigo": 103243030,
  "nsu": 1780016186869,
  "custoTotal": 235.67,
  "confirmada": true,
  "status": "emitida"
}

sem_parar_codigo é o número que precisa entrar no campo VPO do MDF-e do cliente.

Erros

Status Mensagem Causa
400 motorista.cpfCnpj inválido (DV ou tamanho) DV não bate ou não é 11/14 dígitos
400 veiculo.placa deve ter 7 caracteres Placa malformada
400 Rota sem praças de pedágio A rota não passa por pedágios
402 Saldo insuficiente Créditos pré-pagos zerados
502 Falha ao vincular placa ao embarcador RNTRC inativo ou inconsistente
502 Falha na compra (com timeout: true) Timeout do Sem Parar — consulte status antes de retentar

3. Consultar status + recibo

GET /api/v1/vpo/sem-parar/status/{viagemId}

Consulta o estado atual no Sem Parar + recibo oficial. Idempotente, sem efeitos colaterais (exceto sincronizar status local se mudou).

Response 200

{
  "ok": true,
  "viagemId": "75e64d34-4667-41d3-9220-09f32ebdf3cd",
  "env": "prod",
  "referencia_externa": "PEDIDO-12345",
  "status_local": "emitida",
  "sem_parar_codigo": 103243030,
  "nsu": 1780016186869,
  "status": {
    "codigo": 2,
    "label": "Confirmada",
    "success": true
  },
  "recibo": {
    "success": true,
    "cnpjEmissor": "07.604.556/0001-36",
    "nomeEmissor": "EMPRESA BRASILEIRA DE BEBIDAS E ALIMENTOS S/A",
    "cnpjTransp": "039.350.207-40",
    "nomeTransp": "JOAQUIM JOSE FERNANDES",
    "nomeRota": "ASTOLFO DUTRA/MG → RIO DE JANEIRO/RJ",
    "catVeiculo": "02 EIXOS ROD DUPLA",
    "dataCompra": "2026-05-28T21:56:29-03:00",
    "dataExp": "2026-05-31",
    "dataViagem": "2026-05-28",
    "total": "65.93",
    "tipo": "ROTA PLANEJADA",
    "pracas": [
      {
        "nomePraca": "BR116, KM789+900, SUL, LEOPOLDINA",
        "nomeRodovia": "BR116",
        "nomeConcessionaria": "ECORIOMINAS",
        "placa": "DVT1B37",
        "tag": "0740177920",
        "tarifa": "27.55"
      }
    ]
  }
}

Códigos de status Sem Parar

Código Label Significado
1 Comprada VPO criado, ainda não confirmado pela cabine
2 Confirmada VPO ativo e válido
8 Cancelada Foi cancelada (pela embarcadora ou pelo Sem Parar)
9 Encerrada Motorista passou em todas as praças ou venceu prazo

Observação importante sobre o cnpjTransp no recibo

O CPF/CNPJ que aparece em cnpjTransp NÃO é necessariamente o mesmo que você enviou em motorista.cpfCnpj na emissão. O Sem Parar resolve o RNTRC vinculado à placa na base da ANTT e devolve o CPF do titular real do RNTRC. Esse é o CPF que vai aparecer no MDF-e e em fiscalizações.


3b. Listar VPOs

GET /api/v1/vpo/sem-parar/list

Lista os VPOs do seu tenant no ambiente do token (um token hmlg só vê viagens de homologação; prod só vê produção). Ideal para a tela de listagem.

Query params (todos opcionais)

Param Default Observação
limit 50 itens por página (máx 100)
offset 0 deslocamento para paginação
status filtra por status (emitida, cancelada, erro, rascunho…)
referencia_externa filtra pelo seu ID externo (igualdade)

Response 200

{
  "ok": true,
  "total": 137,
  "limit": 50,
  "offset": 0,
  "vpos": [
    {
      "viagemId": "d8ad700f-8f6f-4ef4-9bc7-f82fae00e6ce",
      "status": "emitida",
      "env": "prod",
      "referencia_externa": "PEDIDO-4471",
      "placa": "ABC1D23",
      "eixos": 5,
      "transportador": "TRANSPORTES EXEMPLO LTDA",
      "origem": "Uberlândia/MG",
      "destino": "Araguari/MG",
      "custo_total": 11,
      "antt_id_vpo": "89616552778700127661",
      "sem_parar_codigo": "104620273",
      "data_inicio": "2026-06-22",
      "data_fim": "2026-07-06",
      "created_at": "2026-06-19T14:08:27Z"
    }
  ]
}

Para o detalhe completo (recibo, praças, status atual no Sem Parar), use GET /v1/vpo/sem-parar/status/{viagemId} com o viagemId retornado aqui. Para buscar pelo seu identificador, passe ?referencia_externa=....


Verificar aptidão da placa

POST /api/v1/vpo/sem-parar/verificar-placa

Antes de emitir, verifica se a placa tem TAG Sem Parar ativa (apta a receber Vale-Pedágio). Evita tentar emitir para um veículo que vai falhar.

Request

{ "placa": "ABC1D23" }

Response 200

{
  "ok": true,
  "placa": "ABC1D23",
  "apto": true,
  "temTag": true,
  "tag": "0740177920",
  "proprietario": "JOAQUIM JOSE FERNANDES",
  "eixos": 5,
  "statusCodigo": 0,
  "motivo": "Apto: a placa possui TAG Sem Parar ativa e pode receber Vale-Pedágio."
}
  • apto: true → pode emitir. apto: false → não emita (oriente o titular a regularizar a TAG). statusCodigo: 0 = apto, 4 = sem TAG/não encontrada.

4. Cancelar VPO

POST /api/v1/vpo/sem-parar/cancel/{viagemId}

Cancela uma viagem emitida ou em uso. Em ambiente PROD, estorna automaticamente o valor para o saldo de créditos da Loog.

Response 200

{
  "ok": true,
  "viagemId": "75e64d34-4667-41d3-9220-09f32ebdf3cd",
  "status": "cancelada"
}

Erros

Status Mensagem Causa
400 Status "encerrada" — só cancela emitida/em_uso A viagem já foi consumida em praça
403 Token de ambiente "prod" não pode cancelar viagem do ambiente "hmlg" Mismatch de env

5. Saldo de créditos

GET /api/v1/vpo/sem-parar/saldo

Retorna o saldo de créditos pré-pagos Loog do seu tenant. Útil pra exibir o saldo disponível e bloquear a emissão antes mesmo de chamar /emit (que também valida saldo e devolve 402 Saldo insuficiente). O saldo é por tenant, não por ambiente — em HMLG não há débito real, mas o número reflete o saldo que valeria em PROD. Adicionar saldo é feito na store (https://store.loog.ai/creditos).

Response 200

{
  "ok": true,
  "env": "hmlg",
  "saldo_centavos": 1500000,
  "saldo": 15000,
  "saldo_formatado": "R$ 15.000,00"
}

Boas práticas

1. Use HMLG primeiro

Antes de ir a PROD, teste todo o fluxo em HMLG (token loog_hmlg_*). Não há cobrança, não há débito de saldo. AVISO: o Sem Parar tem instabilidade conhecida no HMLG — se cair na call, o nosso endpoint indica que você pode usar PROD com placas de teste pra validar.

2. Use referencia_externa

Sempre passe seu ID interno (pedido, contrato, frete) em referencia_externa no /emit. Ele aparece no /status — fica fácil correlacionar com seu sistema sem precisar guardar o mapeamento.

3. Idempotência

Se /emit der timeout (502 com timeout: true), NÃO retente cegamente — primeiro consulte por referencia_externa (use o endpoint /status quando disponível) ou via dashboard. O Sem Parar pode ter emitido com sucesso mesmo retornando timeout pra nós.

4. Polling de status

Pra acompanhar consumo (motorista passando pelas praças), faça polling a cada 5-15 minutos no /status. Não é Webhook (ainda) — em roadmap.

5. Rate limit

Não publicamos limite estrito ainda — esperamos uso razoável de até 50 req/min por token. Avise se precisar de mais.


Exemplos práticos

cURL — preview

curl -X POST https://store.loog.ai/api/v1/vpo/sem-parar/route-preview \
  -H "Authorization: Bearer loog_prod_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "origem":  { "lat": -18.9186, "lng": -48.2772 },
    "destino": { "lat": -23.5505, "lng": -46.6333 },
    "eixos": 6
  }'

Node.js (fetch nativo)

const TOKEN = process.env.LOOG_API_TOKEN; // loog_prod_...

async function emitirVpo(payload) {
  const r = await fetch("https://store.loog.ai/api/v1/vpo/sem-parar/emit", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });
  const j = await r.json();
  if (!r.ok) throw new Error(`${r.status}: ${j.error}`);
  return j;
}

// Uso:
const resp = await emitirVpo({
  motorista: { cpfCnpj: "32984580151", nome: "Adenilson Ferreira" },
  veiculo:   { placa: "ABC1D23", eixos: 6 },
  rota: {
    origem:  { lat: -18.9186, lng: -48.2772, label: "Uberlândia/MG" },
    destino: { lat: -23.5505, lng: -46.6333, label: "São Paulo/SP" },
  },
  periodo: { inicio: "2026-06-01", fim: "2026-06-05" },
  referencia_externa: "PEDIDO-12345",
});
console.log("VPO emitido:", resp.sem_parar_codigo);

Python (requests)

import os, requests

TOKEN = os.environ["LOOG_API_TOKEN"]
BASE = "https://store.loog.ai/api/v1"

def emitir_vpo(payload):
    r = requests.post(
        f"{BASE}/vpo-sem-parar/emit",
        json=payload,
        headers={"Authorization": f"Bearer {TOKEN}"},
    )
    r.raise_for_status()
    return r.json()

Roadmap

  • Webhooks para eventos vpo.consumed / vpo.cancelled (eliminar polling)
  • Listagem de VPOs + busca por referencia_externa (GET /vpo/sem-parar/list)
  • Endpoint de relatório (extrato, totalizadores) por período
  • Endpoint CIOT Ailog (/ciot-ailog/emit e congêneres)

Sugestões e bugs: comercial@loog.ai