Trilha · Módulo 5
Webhooks e chaves de acesso
Webhooks e chaves de acesso
Este módulo é para quem integra o sistema da transportadora (TMS), de um embarcador ou de um destinatário ao Veltra, para ler o que foi registrado (baixas, ocorrências, comprovantes) e ser avisado quando algo acontece. Para enviar manifestos, veja o módulo 4, "Manifestos pela API".
As listas completas de rotas, campos e eventos ficam nas referências geradas do contrato: referência de leitura e referência de webhooks. Aqui você aprende o caminho, na ordem em que se usa.
Tipos de chave e o que cada uma enxerga#
Toda leitura usa uma chave de API no cabeçalho X-Veltra-Api-Key. A chave é emitida pela transportadora, pertence a um ambiente (Teste ou Produção) e só enxerga dados desse ambiente. Cada chave tem uma finalidade:

| Finalidade | Para quem | O que enxerga |
|---|---|---|
INTEGRACAO_TMS | o TMS da transportadora | todos os eventos e documentos da transportadora, manifestos, PDF, pacote e XML do evento 110180, e a posição dos veículos (posicao.lote) |
CONSULTA_EMBARCADOR | um embarcador | só os documentos das raízes de CNPJ cadastradas na chave; não vê manifestos, PDF, pacote, XML nem posição |
CONSULTA_DESTINATARIO | um destinatário pessoa jurídica | só os documentos das raízes de CNPJ cadastradas na chave; não vê manifestos, PDF, pacote, XML nem posição |
Pontos que costumam gerar dúvida:
- A raiz do CNPJ é o início de 8 caracteres, o que cobre matriz e filiais. CPF não entra no escopo da chave, por causa da LGPD; destinatário pessoa física acompanha pelo link sem login.
- Uma chave de consulta que pede algo fora das suas raízes recebe
404, como se o documento não existisse. Rotas só do TMS respondem403para as outras finalidades. - As chaves de consulta só leem; não gravam nada.
- A chave que a transportadora emite em Chaves de API (
/chaves-api) é a do TMS, com a finalidadeINTEGRACAO_TMS. Ela serve para enviar manifestos e também para ler a API de saída. - Existe ainda a finalidade
ENTRADA_TRANSPORTADORA, prevista para o embarcador que contrata o Veltra como cliente próprio e recebe os dados das transportadoras parceiras. Ela não lê a API de saída, e esse caminho ainda está em decisão (ADR 0014), então não conte com ele agora.
Emitir, listar e revogar chaves#
Emita a chave do TMS no portal
Um administrador entra em Chaves de API (/chaves-api), informa o nome (por exemplo, "TMS produção") e escolhe o ambiente. A chave de Produção só pode ser emitida quando a transportadora está ativa; em homologação, o portal avisa que não é possível.

Copie a chave na hora
O valor completo aparece uma única vez, na criação. Depois só o prefixo fica visível. Guarde o valor em um cofre de segredos, nunca no código nem em log. Se perder, revogue e emita outra.

Revogue quando precisar
Na mesma tela, o administrador revoga a chave. O sistema que a usa perde o acesso.
Por API, o mesmo caminho é feito por /v1/api-keys: POST emite, GET lista (só o prefixo, nunca o valor, inclusive as revogadas) e DELETE /v1/api-keys/{id} revoga. Esse cadastro usa o login do portal, não a chave de API. Para testar se a sua chave está certa, chame GET /v1/api-keys/atual: ela devolve a transportadora e o ambiente da chave que fez a chamada.
curl -H "X-Veltra-Api-Key: SUA_CHAVE_AQUI" \
https://SEU_ENDERECO_AQUI/v1/api-keys/atualChaves de consulta para embarcador e destinatário#
As chaves de consulta são emitidas pelo administrador, pela API do portal em /v1/integracoes/chaves. Para CONSULTA_EMBARCADOR e CONSULTA_DESTINATARIO é obrigatório informar:
- o nome da chave;
- as raízes de CNPJ (
escopoCnpjs, 8 caracteres cada, ao menos uma); - a data de expiração (
expiraEm); - o limite de chamadas por minuto (
limitePorMinuto, de 1 a 600).
O valor da chave também aparece uma única vez. A revogação, em /v1/integracoes/chaves/{id}/revogacao, tem efeito imediato.
A chave de consulta de Produção faz parte de um módulo habilitado na conta. Se o módulo não estiver contratado, a emissão responde 409 com o código MODULO_NAO_HABILITADO no campo codigo. Se o módulo for desligado depois, as chaves já emitidas não são revogadas: elas ficam suspensas (403 com o código CHAVE_SUSPENSA) e voltam a funcionar quando o módulo for contratado de novo. O custo desse módulo é explicado no módulo 7, "Plano e pagamento", sem valores neste texto.
Ler eventos e documentos#
Estas são as rotas de leitura (/v1/saida). Elas são descritas na referência de leitura:
| Rota | Para que serve |
|---|---|
GET /v1/saida/eventos | lista de eventos, com cursor |
GET /v1/saida/documentos/{id} | situação de um documento |
GET /v1/saida/manifestos e /v1/saida/manifestos/{id} | manifestos (só TMS) |
GET /v1/saida/comprovantes/{id}/pdf | PDF do comprovante (só TMS) |
GET /v1/saida/comprovantes/{id}/pacote | pacote ZIP de evidências (TMS) |
GET /v1/saida/eventos-cte/{id}/xml | XML do evento 110180 do CT-e (TMS) |
GET /v1/saida/openapi/{finalidade} | OpenAPI filtrado para a finalidade da sua chave |
Leitura incremental com alteradoDesde#
Primeira leitura
Chame GET /v1/saida/eventos sem alteradoDesde. O parâmetro limite vai de 1 a 200 e, por padrão, é 50.
curl -H "X-Veltra-Api-Key: SUA_CHAVE_AQUI" \
"https://SEU_ENDERECO_AQUI/v1/saida/eventos?limite=50"Guarde o cursor
A resposta traz itens (os eventos, no mesmo envelope dos webhooks) e proximoCursor. Processe os itens e guarde o proximoCursor.
Continue de onde parou
Na próxima rodada, mande o cursor guardado em alteradoDesde.
curl -H "X-Veltra-Api-Key: SUA_CHAVE_AQUI" \
"https://SEU_ENDERECO_AQUI/v1/saida/eventos?alteradoDesde=CURSOR_GUARDADO_AQUI"Regras que evitam surpresa:
- O cursor é opaco: não monte nem edite.
- O Veltra só devolve eventos alterados há pelo menos 5 segundos. Por isso um evento recém-registrado pode demorar alguns segundos para aparecer. Não é falha; consulte de novo.
- Repetição é normal. O mesmo evento pode vir em duas respostas seguidas, por exemplo se o seu processo caiu antes de guardar o cursor. Trate pelo
iddo evento: processar duas vezes deve dar o mesmo resultado. - As chaves de consulta não recebem o tipo
posicao.lote.
PDF, pacote e XML do evento 110180#
Baixe o PDF e o pacote pelo comprovanteId que vem em dados do evento comprovante.emitido. O PDF só leva a assinatura ICP-Brasil quando a transportadora ativou a validação opcional, como explica o módulo 6. Para provar que o arquivo não mudou, confira o código SHA-256 dele. O pacote ZIP reúne o PDF, o manifesto da evidência e os arquivos de apoio.
Chaves de consulta (embarcador ou destinatário) não baixam PDF, pacote nem XML pela API de saída: a resposta é 403.
O XML do evento 110180 só existe quando o perfil da transportadora o gera; sem ele, a resposta é 404. O Veltra não transmite esse evento à SEFAZ: o XML é entregue ao TMS para uso dele. O Veltra também não transmite o evento 110190 (insucesso).
OpenAPI da sua finalidade#
GET /v1/saida/openapi/{finalidade} devolve, em YAML, o OpenAPI com só as rotas que a sua chave pode chamar. Use-o para gerar o cliente. Uma chave de consulta só obtém o OpenAPI da sua própria finalidade; pedir o de outra resulta em 403.
Erros#
As respostas de erro seguem application/problem+json (RFC 9457). Os mais comuns:
| Código | Significado |
|---|---|
400 | cursor ou limite inválido |
401 | chave ausente ou inválida |
403 | finalidade sem permissão para a rota, ou chave suspensa |
404 | não encontrado ou fora do escopo da chave |
429 | limite de chamadas por minuto da chave excedido (quando a chave tem limite); respeite o cabeçalho Retry-After |
Receber webhooks#
O que é e por que usar#
Um webhook é um aviso automático. Pense em pedir à portaria: "me ligue quando o meu pacote chegar", em vez de ir lá de hora em hora perguntar. Quando algo acontece (uma baixa, uma ocorrência, um comprovante), o Veltra faz um POST HTTP para um endereço do sistema que você cadastrar, com os dados do fato.
Comparado a consultar de tempos em tempos, o webhook avisa em poucos segundos e não gasta chamadas à toa. Por segurança, use os dois: o webhook para a velocidade e a leitura incremental (alteradoDesde, descrita acima) para conferir se nada ficou para trás. A assinatura segue o formato Standard Webhooks, conforme o ADR 0022. O guia técnico completo, com todos os eventos, exemplos de corpo e código de verificação em cinco linguagens, está na referência de webhooks. Este módulo é o passo a passo na tela.
Cadastrar o endereço e guardar o segredo#
Só um administrador cadastra webhooks. A tela fica em Integrações > Webhooks (/integracoes/webhooks).
Peça ao desenvolvedor o endereço
Ele precisa de um endereço público que comece com https:// (por exemplo, https://SEU_ENDERECO_AQUI/webhooks/veltra) e que responda rápido. Endereços internos (de rede privada) são recusados.
Clique em Criar webhook e preencha
- Nome do assinante: um nome que diga quem recebe, como "TMS da matriz".
- Quem recebe: "Sistema da transportadora (TMS)" ou "Terceiro (embarcador, cliente)". O terceiro precisa das raízes de CNPJ e não recebe PDF nem canhoto, a menos que seja o tomador do documento.
- URL de destino: o endereço do passo 1.
- Eventos: marque só o que o sistema vai usar. As posições dos veículos (
posicao.lote) aparecem só para o TMS, em lotes por veículo, no campo "Enviar posição a cada" (5, 10 ou 15 minutos). - E-mail de contato técnico: use uma caixa que alguém lê. É por ele que o Veltra avisa se o webhook for desativado.
Guarde o segredo agora
Ao salvar, aparece a janela "Guarde o segredo agora". O segredo aparece uma única vez. Clique em "Copiar segredo", guarde em um cofre de segredos (nunca no código nem em mensagem de e-mail ou chat), marque "Já guardei o segredo" e clique em "Concluir". Se perder, use "Girar segredo".
Entregue o segredo ao desenvolvedor, de forma segura
O segredo parece com whsec_ seguido de um texto. O desenvolvedor usa o segredo inteiro para conferir que cada aviso veio mesmo do Veltra (próxima seção).
Clique em Enviar teste
Veja a seção "Testar". Se o teste voltar com sucesso, o webhook está pronto.
Os eventos disponíveis são 8: parada.chegada, parada.saida, baixa.registrada, ocorrencia.registrada, comprovante.emitido, evento_cte.estado_alterado, posicao.lote e veiculo.km_registrado (km do veículo, só para o assinante TMS). A referência traz o formato de cada um. Eventos de Teste nunca chegam a webhooks de Produção, e vice-versa.
O mesmo cadastro existe na API do portal, para quem prefere automatizar: /v1/integracoes/webhooks (lista e criação) e /v1/integracoes/webhooks/{id} (ver, editar, excluir).
O que chega ao sistema#
Cada aviso traz três cabeçalhos:
| Cabeçalho | Conteúdo |
|---|---|
webhook-id | identificador da entrega; é o mesmo em todas as retentativas dela |
webhook-timestamp | segundos Unix; muda a cada tentativa |
webhook-signature | v1, seguido do HMAC-SHA256 em Base64; durante a rotação do segredo, duas assinaturas separadas por espaço |
O corpo é JSON no envelope { id, tipo, versao, ocorridoEm, ambiente, dados }. Os campos de dados de cada tipo, com um exemplo completo, estão na referência de webhooks.
Verificar a assinatura#
Sempre verifique antes de processar. Sem isso, qualquer pessoa que descubra o endereço poderia enviar eventos falsos. Esta parte é do desenvolvedor; aqui está o resumo para você conferir se ele fez:
Ler o corpo exatamente como chegou
Os bytes crus da requisição, sem fazer parse e serializar de novo: qualquer espaço a mais muda a assinatura.
Conferir o relógio
Recusar se webhook-timestamp estiver a mais de 5 minutos do relógio do servidor, para trás ou para a frente.
Montar a string assinada
webhook-id, um ponto, webhook-timestamp, um ponto e o corpo (bytes UTF-8).
Calcular o HMAC-SHA256
Com o segredo: tirar o prefixo whsec_ e decodificar o Base64 para obter os bytes da chave.
Comparar em tempo constante
Com cada assinatura v1 do cabeçalho webhook-signature, usando a função de comparação em tempo constante da linguagem (nunca ==).
Recusar se nenhuma conferir
Responder 401 e não processar. Cuidado: o Veltra não tenta de novo um 401, então um segredo errado joga os eventos na fila de falhas.
O código pronto, em Node.js, Python, Java, PHP e C#, está na seção 5 da referência de webhooks. Os segredos de teste devem ser sempre o seu (SEU_SEGREDO_AQUI), nunca um valor copiado de outro lugar.
O que o endereço deve responder#
O endereço deve responder 2xx em poucos segundos e processar depois. O Veltra espera até 5 segundos para conectar e 10 para a resposta.
| Resposta | Resultado |
|---|---|
2xx | entregue |
429, 5xx, sem resposta, demora, certificado inválido | falha temporária: tenta de novo mais tarde |
410 | a entrega esgota na hora e o webhook fica Pausado |
3xx (redirecionamento) ou outro 4xx | falha definitiva: sem nova tentativa, vai direto para a fila de falhas |
Para "tente de novo mais tarde", o sistema deve responder 503, nunca 400.
Quando falha: tentativas e fila de falhas#
Nas falhas temporárias, o Veltra tenta de novo com estes intervalos: 1 minuto, 5 minutos, 30 minutos, 2 horas, 12 horas e depois 12 horas (cada um com variação de 80% a 120%). Se a resposta trouxer Retry-After maior que o intervalo, vale o Retry-After (no máximo 72 horas). Passadas 72 horas desde a primeira tentativa (cerca de 10 tentativas), a entrega vira "Tentativas esgotadas" e vai para a fila de falhas. Nada é apagado.
Duas travas automáticas:
- Resposta
410pausa o webhook. - 20 entregas esgotadas seguidas deixam o webhook Desativado automaticamente, e o Veltra envia um e-mail ao contato técnico. Um webhook desativado não volta com "Retomar": crie outro.
Corrija a causa
Veja o código HTTP do histórico, conserte o endereço, o segredo ou o servidor.
Abra a aba Fila de falhas
Na página do webhook, a aba Fila de falhas lista só as entregas esgotadas.
Clique em Reenviar
Use Reenviar em uma entrega, ou Reenviar todas (N) no topo. O reenvio usa o mesmo webhook-id e o mesmo corpo, e a janela de 72 horas recomeça. Pela API: POST /v1/integracoes/webhooks/{id}/entregas/{entregaId}/reenvio, que responde 202.
Repetição e ordem#
Como pode haver repetição (por exemplo, a resposta 2xx se perdeu na rede), o sistema deve tratar pelo webhook-id: se já processou, responder 2xx sem refazer. A ordem não é garantida: use o ocorridoEm do corpo para saber qual fato é mais recente. Nunca assuma que a baixa chega antes do comprovante.
Girar o segredo sem perder eventos#
Clique em Girar segredo
Na página do webhook, Girar segredo e confirme. Pela API: POST /v1/integracoes/webhooks/{id}/rotacao-segredo. O novo segredo aparece uma única vez: guarde-o.
Troque o segredo no sistema em até 24 horas
Durante 24 horas, cada entrega leva duas assinaturas no webhook-signature: a do segredo novo e a do anterior, separadas por espaço. O código de verificação da referência aceita qualquer uma das duas, então nada é perdido enquanto o desenvolvedor troca.
Depois das 24 horas
Só o segredo novo vale. Evite girar duas vezes seguidas: o segredo do primeiro giro deixa de valer na hora.
Testar#
Na página do webhook, clique em Enviar teste. O resultado aparece na tela, como "Teste entregue: o assinante respondeu 200 em 120 ms" ou "O teste falhou" com o motivo. O evento de teste tem o tipo webhook.teste e "ambiente": "TESTE", é assinado como uma entrega real e vai ao endereço do próprio webhook; não aparece no histórico de entregas. Se o endereço for interno, o teste falha com o motivo ENDERECO_RECUSADO. Pela API: POST /v1/integracoes/webhooks/{id}/teste.
Para o desenvolvedor trabalhar na própria máquina, a referência explica como usar um túnel (Cloudflare Tunnel, ngrok ou localtunnel) e traz um roteiro com curl e openssl para assinar uma chamada de teste sem depender do Veltra.
Acompanhar, pausar e excluir#
Em Integrações > Webhooks você vê, para cada webhook, o nome, a URL, os eventos, a situação e a última entrega:
- Ativo: recebendo.
- Pausado: você pausou, ou o endereço respondeu
410. Nenhuma entrega sai enquanto estiver pausado; os eventos desse período não são apagados e são distribuídos depois do Retomar, sem garantia de ordem. Confira pela leitura incremental se nada ficou para trás. - Desativado automaticamente: por 20 entregas esgotadas seguidas.
A aba Histórico de entregas mostra, por entrega, o horário, o evento, o ID da entrega (o identificador da entrega no Veltra, com botão de copiar, e não o cabeçalho webhook-id), a situação (Entregue, Aguardando nova tentativa, Falhou, Teste ou Tentativas esgotadas), o código HTTP, o número de tentativas e a duração. As ações são Enviar teste, Pausar e Retomar (.../pausa e .../retomada na API), Editar, Girar segredo, Reenviar e Excluir. Excluir desativa o webhook: ele deixa de receber eventos.
Se algo der errado#
| Sintoma | Causa provável | O que fazer |
|---|---|---|
Todas as entregas "Tentativas esgotadas" com código 401 | segredo errado no sistema, ou corpo alterado antes de verificar | confirmar o segredo, ler o corpo cru e usar Reenviar todas |
Entregas esgotadas com 404 ou 400 | rota errada; 4xx não é retentado | corrigir a rota e usar Reenviar |
| Webhook "Pausado" sem ninguém ter pausado | o endereço respondeu 410 | corrigir o endereço e usar Retomar |
| Webhook "Desativado automaticamente" | 20 entregas esgotadas seguidas | corrigir a causa e criar um novo webhook |
| Muitas entregas "Falhou" sem código | o servidor não responde em 10 segundos, DNS ou certificado inválido | responder rápido e processar depois |
| O mesmo evento duas vezes | normal: repetição | tratar pelo webhook-id |
| Recusa depois de girar o segredo | sistema ainda com o segredo antigo depois de 24 horas | atualizar para o segredo mais novo |
Mais causas e correções estão na seção 13 da referência de webhooks.
Webhook e consulta, juntos#
Pelo princípio de paridade do ADR 0013, o que o portal mostra também existe na leitura pela API e nos eventos de webhook, no mesmo envelope e com o mesmo id. Receba pelo webhook, trate pelo webhook-id e, a cada hora ou a cada dia, leia GET /v1/saida/eventos?alteradoDesde=CURSOR_GUARDADO_AQUI para recuperar o que possa ter se perdido. O webhook leva só identificadores; para o PDF, o pacote ou o XML do evento 110180, use a leitura.
Boas práticas de segurança#
- Guarde chaves e segredos em cofre; nunca em repositório, código ou log.
- Use uma chave por sistema e por ambiente, com nome que identifique quem a usa; assim a revogação é cirúrgica.
- Para consulta de terceiros, dê a menor abrangência possível: poucas raízes de CNPJ, expiração curta e limite de chamadas baixo.
- Verifique sempre a assinatura do webhook e o relógio antes de processar, e deduplique pelo
webhook-id. - Guarde o segredo do webhook em cofre, gire-o periodicamente e mantenha o e-mail de contato técnico em uma caixa que alguém lê.
- Os dados de motoristas, destinatários e localização que passam por essas integrações são dados pessoais; trate-os conforme a LGPD nos seus sistemas.