{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-parceiros/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Webhooks","description":"APIs públicas da Movvia para parceiros, estabelecimentos comerciais e clientes de dados veiculares.","meta":[{"name":"theme-color","content":"#7E3DEE"},{"name":"apple-mobile-web-app-title","content":"Movvia Docs"},{"name":"application-name","content":"Movvia Docs"}],"llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"webhooks","__idx":0},"children":["Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A Movvia entrega eventos em tempo real via HTTP POST no endpoint que você registrar. Todos os payloads são assinados com HMAC-SHA256."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"estrutura-do-evento","__idx":1},"children":["Estrutura do evento"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"evento\": \"pe.transacao.recebida\",\n  \"versao\": \"2\",\n  \"id\": \"evt_8f3a1b2c\",\n  \"timestamp\": \"2026-04-24T10:00:00Z\",\n  \"parceiroId\": \"par_7f3a1b2c\",\n  \"dados\": {\n    \"transacaoId\": \"t_9f2c7e1a\",\n    \"placa\": \"ABC1D23\",\n    \"valor\": 12.50,\n    \"praca\": \"KM 245 — Imigrantes\",\n    \"criadoEm\": \"2026-04-24T09:58:00Z\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"catálogo-de-eventos","__idx":2},"children":["Catálogo de eventos"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Evento"},"children":["Evento"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Quando ocorre"},"children":["Quando ocorre"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.transacao.recebida"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Nova passagem capturada para uma placa monitorada"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.transacao.atualizada"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Valor ou status de uma passagem foi revisado"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.transacao.cancelada"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Passagem cancelada pelo EC"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.pedidos.confirmado"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pedido confirmado com sucesso"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.pedidos.cancelado"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pedido cancelado"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pe.pedidos.expirado"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pedido expirou sem confirmação (48h)"]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"verificar-a-assinatura-hmac-sha256","__idx":3},"children":["Verificar a assinatura HMAC-SHA256"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["O header ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["x-movvia-signature"]}," contém a assinatura do payload. Sempre verifique antes de processar."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","header":{"controls":{"copy":{}}},"source":"import crypto from 'crypto';\n\nfunction verificarAssinatura(\n  payload: string,\n  signature: string,\n  secret: string\n): boolean {\n  const expected = crypto\n    .createHmac('sha256', secret)\n    .update(payload, 'utf8')\n    .digest('hex');\n  return crypto.timingSafeEqual(\n    Buffer.from(signature),\n    Buffer.from(expected)\n  );\n}\n\n// No seu handler:\napp.post('/webhook', (req, res) => {\n  const assinatura = req.headers['x-movvia-signature'] as string;\n  const payload = JSON.stringify(req.body);\n\n  if (!verificarAssinatura(payload, assinatura, process.env.MV_WEBHOOK_SECRET!)) {\n    return res.status(401).send('Assinatura inválida');\n  }\n\n  // processar evento\n  res.status(200).send('OK');\n});\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import hashlib, hmac, os\n\ndef verificar_assinatura(payload: bytes, signature: str, secret: str) -> bool:\n    expected = hmac.new(\n        secret.encode(), payload, hashlib.sha256\n    ).hexdigest()\n    return hmac.compare_digest(signature, expected)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Tutorial completo em ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/parceiros/tutorials/validar-hmac"},"children":["Validar HMAC"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"retentativas","__idx":4},"children":["Retentativas"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A Movvia faz retry automático em caso de falha (status != 2xx ou timeout):"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Tentativa"},"children":["Tentativa"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Intervalo"},"children":["Intervalo"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1ª (original)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["—"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["2ª"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1 min"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["3ª"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5 min"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["4ª"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["30 min"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5ª"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["2h"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["6ª"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["6h"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["7ª"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["24h"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Após 7 tentativas sem sucesso, o evento é marcado como ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["falha_definitiva"]}," e um alerta é enviado por email."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"requisitos-do-endpoint","__idx":5},"children":["Requisitos do endpoint"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Responder em até ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["5 segundos"]}," com status 2xx."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Processar de forma idempotente — o mesmo evento pode ser entregue mais de uma vez."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Aceitar ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Content-Type: application/json"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["HTTPS obrigatório em produção."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"success","name":"Processar de forma assíncrona"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Responda 200 imediatamente e processe o evento em background. Não faça operações lentas (banco, chamadas externas) dentro do handler síncrono."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"registrar-o-endpoint","__idx":6},"children":["Registrar o endpoint"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["O endpoint de webhook é configurado no onboarding. Para alterar, entre em contato com ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"mailto:comercial@movvia.com.br"},"children":["comercial@movvia.com.br"]},"."]}]},"headings":[{"value":"Webhooks","id":"webhooks","depth":1},{"value":"Estrutura do evento","id":"estrutura-do-evento","depth":2},{"value":"Catálogo de eventos","id":"catálogo-de-eventos","depth":2},{"value":"Verificar a assinatura HMAC-SHA256","id":"verificar-a-assinatura-hmac-sha256","depth":2},{"value":"Retentativas","id":"retentativas","depth":2},{"value":"Requisitos do endpoint","id":"requisitos-do-endpoint","depth":2},{"value":"Registrar o endpoint","id":"registrar-o-endpoint","depth":2}],"frontmatter":{"title":"Webhooks — Parceiros Movvia","description":"Como receber, verificar e tratar webhooks assinados com HMAC-SHA256 na API de Parceiros Movvia.","seo":{"title":"Webhooks"}},"lastModified":"2026-04-25T15:17:56.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/parceiros/guides/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}