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
- Acesse https://store.loog.ai/configuracoes/api-tokens com um usuário gestor/admin do seu tenant
- Clique "Novo token", dê um nome descritivo (ex: "ERP SAP", "Querocarga", "Mobile App") e escolha o ambiente
- 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 |
| POST | /v1/maplink/distancia |
KM/duração da rota (Maplink Trip, sem pedágio) — scope maplink |
| POST | /v1/maplink/trip |
Rota completa + praças/pedágio por eixos — scope maplink |
| GET | /v1/maplink/geocode |
Autocomplete de cidades (Geocode Maplink) — scope maplink |
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éricosdestino(obrigatório): idemparadas(opcional): array de{ lat, lng }— paradas intermediárias na ordem da viagemeixos(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álidomotorista.nome(obrigatório): razão social ou nome completomotorista.rntrc(opcional): se não passar, é resolvido pelo Sem Parar via lookup do CPFveiculo.placa(obrigatório): 7 caracteres alfanuméricos (formato Mercosul ou antigo)veiculo.eixos(obrigatório): 2 a 10rota.{origem,destino}.lat,lng(obrigatório): coordenadasrota.{origem,destino}.label(opcional): rótulo legível pra aparecer no reciborota.paradas(opcional): paradas intermediáriasrota.pracas(opcional): IDs Sem Parar das praças da rota escolhida pelo seu sistema (ex.:pracas[].id_sem_parardePOST /v1/maplink/rota). Quando informado, o roteirizador Sem Parar não roda: a rota é cadastrada exatamente com essas praças e o VPO cobre só elas. Use quando o usuário escolheu o trajeto num mapa (alternativas Maplink).rota.kmerota.custo_previsto(opcionais) só alimentam o registro da viagem.periodo.inicio/fim(obrigatório): YYYY-MM-DD, máximo 15 dias de janelareferencia_externa(opcional): seu ID interno (contrato, pedido). Aparece em/statuspra 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 |
2b. Emitir VPO pelas praças da Maplink
POST /api/v1/vpo/sem-parar/emit-por-pracas
Emite o VPO a partir das praças que a Maplink já traçou, sem passar de novo pelo roteirizador do Sem Parar. Para quem usa o hub de rotas (seção 6), reroteirizar é trabalho repetido — e pode devolver um caminho diferente do planejado, o que faz o vale cobrir uma estrada que o motorista não vai fazer.
Cotação, verificação de saldo, compra e débito continuam os mesmos da seção 2: o valor debitado é o que o Sem Parar cobra, e a tarifa da Maplink serve só para conferência.
Request — as praças, de um dos dois jeitos:
{
"motorista": { "cpfCnpj": "...", "nome": "..." },
"veiculo": { "placa": "ABC1D23", "eixos": 6 },
"periodo": { "inicio": "2026-09-22", "fim": "2026-09-25" },
"pracas": [ { "id_sem_parar": 572, "nome": "Uberlândia", "tarifa": 12.50 } ],
"rota": { "origem": {"lat": -18.64, "lng": -48.18},
"destino": {"lat": -18.97, "lng": -49.46} },
"politica_cobertura": "exigir_todas",
"teto_divergencia_pct": 20,
"referencia_externa": "OC-8891"
}
pracas— as que a Maplink devolveu em/api/v1/maplink/rota. Basta oid_sem_pararde cada uma;tarifamelhora a conferência de custo.rota— alternativa apracas: informando os pontos, o serviço consulta a Maplink internamente (o token precisa ter também o escopomaplink).politica_cobertura—exigir_todas(default) oupermitir_parcial.teto_divergencia_pct— default 20.
⚠️ Praça sem identificador do Sem Parar. O identificador vem da operadora Via
Fácil na resposta da Maplink, e nem toda praça tem. Emitir ignorando uma praça compra
VPO a menos: o motorista chega na cabine sem vale. Por isso o default recusa
com 422, dizendo quais praças faltam; liberar exige politica_cobertura: "permitir_parcial", e aí a resposta traz o aviso do que ficou descoberto.
Response 201
{
"ok": true,
"viagemId": "…",
"sem_parar_codigo": 123456,
"custoTotal": 187.40,
"antt_id_vpo": "…",
"status": "emitida",
"pracas": { "fonte": "maplink", "usadas": 6, "total": 6, "sem_id": 0, "cobertura_pct": 100 },
"custo_conferencia": { "estimado": 181.10, "cotado": 187.40,
"diferenca": 6.30, "diferenca_pct": 3.5, "dentro_do_teto": true }
}
Response 422 — cobertura insuficiente
{
"error": "cobertura de praças insuficiente para emitir",
"motivo": "1 de 6 praças do trajeto não têm identificador do Sem Parar (…). Emitir assim compra VPO a menos…",
"pracas_total": 6, "pracas_com_id": 5, "cobertura_pct": 83.3
}
Divergência de custo acima do teto não desfaz a emissão — o vale já foi comprado e
debitado pelo valor do Sem Parar. Ela volta em avisos, porque quase sempre significa
que as praças não são as mesmas dos dois lados.
Contabilização: cada chamada entra em api_request_log como vpo_emit_pracas, com o
desfecho, a origem das praças e a divergência apurada.
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 oviagemIdretornado 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"
}
6. Maplink — rotas e distâncias (hub de APIs)
A store centraliza o acesso à Maplink Platform para os sistemas do ecossistema
(scope maplink no token — a mesma autenticação da v1). Toda requisição é
registrada por cliente (token) e aparece no módulo "Maplink → Uso por cliente"
do dashboard.
6.1 Distância da rota (a API de KM)
POST /api/v1/maplink/distancia
Só a quilometragem/duração da rota, sem pedágio (~300ms). O critério
default é THE_SHORTEST (mais curta) — a modalidade usada na emissão de
VPO — e vem explícito na resposta (mode).
Request (pontos aceitam coordenada OU cidade+UF, como no route-preview):
{
"origem": { "cidade": "Araguari", "uf": "MG" },
"destino": { "lat": -29.8425, "lng": -51.1408 },
"paradas": [ { "cidade": "Santa Cruz do Sul", "uf": "RS" } ],
"calculationMode": "THE_SHORTEST"
}
origem/destino(obrigatórios):{ lat, lng }ou{ cidade, uf }paradas(opcional): máx. 10, mesma forma, na ordem da viagemcalculationMode(opcional):THE_SHORTEST(default) |THE_FASTEST
Response 200 (cache permanente por origem/destino/paradas/critério — km de rota não varia; a 1ª consulta de cada rota vai à Maplink, as demais saem do banco):
{
"ok": true,
"km": 1715.4,
"metros": 1715400,
"duracaoMin": 1654,
"mode": "THE_SHORTEST",
"cached": false
}
Erros: 400 (ponto inválido, geocode falhou, >10 paradas), 401/403 (token/scope),
429 (rate limit 120/min por token, header Retry-After), 502 (Maplink Trip: HTTP n + detail).
6.2 Rota completa + pedágio
POST /api/v1/maplink/trip
Proxy fiel da Trip v2 síncrona: polyline por trecho, praças com tarifa por
eixo e serviceTypes (o serviceId da operadora "Via Facil" é o ID da praça
no Sem Parar). Body: points[{siteId?, latitude, longitude}] (2–200),
vehicleType? (enum Maplink de eixos), calculationMode? (default
THE_FASTEST, igual à Maplink), tagDiscount?, calculationDate? (ms),
avoidanceTypes?. Resposta = JSON cru da Maplink. Rate limit 60/min.
6.2b Rota completa (com paradas) + pedágio por eixo
POST /api/v1/maplink/rota
Calcula a rota real origem → paradas (na ordem) → destino numa chamada e devolve km + pedágio total para o veículo (por nº de eixos) + praças com ID Sem Parar. É a fonte do cache de rota do QC (piso ANTT que respeita as paradas).
Request:
{
"pontos": [
{ "cidade": "Araguari", "uf": "MG" },
{ "cidade": "Santa Cruz do Sul", "uf": "RS" },
{ "lat": -29.8425, "lng": -51.1408 }
],
"eixos": 5
}
pontos(obrigatório): ≥ 2, na ordem (origem, paradas…, destino); cada um{lat,lng}ou{cidade,uf}; até 200.eixos(opcional, default 5): 2–10, vira ovehicleTypeda Toll.calculationMode(opcional):THE_SHORTEST(default) |THE_FASTEST.
Response 200 (cache de 7 dias — km é estável, o pedágio é uma prévia de 1 semana):
{
"ok": true,
"km": 1715.8,
"pedagio_total": 636.60,
"eixos": 5,
"mode": "THE_SHORTEST",
"pracas": [
{ "nome": "Pedágio - Araguari 2", "rodovia": "BR-050 ...", "tarifa": 31.50, "id_sem_parar": "570" }
],
"cached": false
}
Erros: 400 (ponto inválido / geocode / limites), 401/403, 429 (120/min por token), 502.
6.3 Autocomplete de cidades
GET /api/v1/maplink/geocode?q=uberl
Devolve { results: [{ label, type, city, state, lat, lon }] } — só cidades
(CITY_CENTROID). q com menos de 3 caracteres devolve lista vazia (200).
Rate limit 240/min.
7. CIOT — emissão na ANTT (hub de APIs)
Serviço de emissão de CIOT (Treeal/ANTT) exposto como API do hub: scope
ciot no token, uma linha em api_request_log por requisição, limite por
cliente. É independente do CIOT de tela da store (/api/ciot/treeal/*) —
não compartilha tabela nem cobrança; as credenciais do canal são as mesmas.
O contrato de entrada completo, com o modelo de perfil por cliente e os casos
de aceite, está em docs/ciot-contrato-v1.md.
⚠️ ambiente é obrigatório em toda chamada (prod | hmg). Não existe
default em lugar nenhum deste serviço: esquecer o ambiente nunca pode mandar
uma declaração de teste para a ANTT de verdade.
7.1 Emitir
POST /api/v1/ciot/emissoes
Faz o ciclo inteiro: reserva o número na ANTT (gerar), confere se uma
tentativa anterior já ficou registrada e declara.
{
"ambiente": "prod",
"referencia_externa": "161147",
"operacao": {
"contratado": { "cpf_cnpj": "...", "rntrc": "..." },
"destinatario": { "cpf_cnpj": "..." },
"veiculos": [ { "placa": "ABC1D23", "eixos": 3 } ],
"viagem": {
"inicio": "2026-09-14", "fim": "2026-09-16", "km_total": 654,
"trechos": [
{ "origem": {"ibge": 2301109}, "destino": {"ibge": 2607901}, "km": 639 },
{ "origem": {"ibge": 2607901}, "destino": {"ibge": 2611606}, "km": 15 }
]
},
"carga": { "natureza_ncm4": 2202, "peso_kg": 12276 },
"frete": {
"valor": 4584.00,
"creditado": { "cpf_cnpj": "...", "conta": { "banco": "237", "agencia": "...", "conta": "..." } }
},
"contexto": { "filial": "ARACATI" }
}
}
O que não precisa ir no request quando o perfil do cliente resolve: contratante, parcelas (frações, desconto, prazo), tipo de operação e tipo de carga. O que o serviço deriva sozinho: composição veicular a partir dos eixos do conjunto, km inteiro por trecho fechando com o total, junção de trechos curtos e o próprio CIOT 12.
Três modos, no mesmo endpoint:
| modo | quando usar |
|---|---|
operacao |
o serviço monta o payload com o perfil do cliente |
payload_pronto |
você já monta o payload da ANTT e quer só o canal |
dry_run: true |
monta e devolve sem tocar na ANTT; com explain: true, diz de onde veio cada campo |
Response 200
{
"ok": true,
"operacao_id": "…",
"ciot_12": "520030583215",
"ciot_16": "5600005466380452",
"protocolo": "…",
"codigo_antt": "110",
"perfil": "britvic_v1",
"perfil_versao": 3
}
Idempotência. A mesma referencia_externa, do mesmo cliente, no mesmo
ambiente é sempre a MESMA operação: já declarada devolve 200 com
ja_declarada: true e o número que existe, sem falar com a ANTT; em
andamento devolve 409. Declarar duas vezes gera documento fiscal duplicado
que, na modalidade 1, não se retifica — só se resolve por cancelamento em 24 h.
Quando falha. 422 com faltando: [{campo, motivo, esperado_de}] (dados
insuficientes) ou com o código da ANTT (recusa — o número reservado é
reaproveitado na próxima tentativa da mesma referência). 502 quando o canal
não responde: repita com a mesma referência, que o serviço consulta a ANTT
antes de declarar de novo.
7.2 Estado da operação
GET /api/v1/ciot/emissoes/{referencia_externa}?ambiente=prod
Lê o registro do serviço (não chama a ANTT): status, CIOT 12/16, protocolo, código, mensagem e cancelamento.
7.3 Cancelar
POST /api/v1/ciot/emissoes/{referencia_externa}/cancelar
{ "ambiente": "prod", "motivo_cancelamento": "carga cancelada pelo embarcador" }
Janela de 24 h após a declaração; o motivo é obrigatório e fica no registro.
7.4 Consultar um CIOT na ANTT
POST /api/v1/ciot/consultar
{ "ambiente": "prod", "ciot_12": "520030583215", "ano": 2026 }
Pergunta direta ao canal — útil para saber se uma declaração que ficou sem resposta chegou a ser registrada.
7.5 Pré-check do transportador
POST /api/v1/ciot/transportadores/situacao
{ "ambiente": "prod", "cpf_cnpj": "...", "rntrc": "..." }
POST /api/v1/ciot/transportadores/frota
{ "ambiente": "prod", "cpf_cnpj": "...", "rntrc": "...", "placas": ["ABC1D23"] }
Transportador irregular e placa sem vínculo são as recusas mais comuns, e
descobrir isso na declaração desperdiça um número reservado. A resposta separa
"a ANTT disse que não serve" de "não deu para perguntar" (indisponivel: true)
— a primeira é problema de cadastro, a segunda é problema de canal.
7.6 Descobrir o que mandar
GET /api/v1/ciot/schema
Devolve o contrato menos o que o perfil deste token já resolve, as
dimensões que ele espera em operacao.contexto (ex.: filial) e o que o
serviço deriva sozinho. Dois clientes recebem respostas diferentes.
7.7 Limites
| endpoint | por minuto, por cliente |
|---|---|
ciot_emitir, ciot_cancelar |
30 |
ciot_consultar, ciot_status, ciot_precheck |
120 |
ciot_schema |
60 |
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
- Emissão de CIOT na ANTT (
/api/v1/ciot/*, seção 7)
Sugestões e bugs: comercial@loog.ai