Webhooks
Receba um POST no seu servidor quando algo relevante acontecer. Os eventos de veículo são enviados apenas para organizações que já consultaram aquele veículo — o caso típico é a virada mensal da Tabela FIPE: quando o preço muda, você recebe vehicle.updated com price_changed: true.
| Evento | Quando |
|---|---|
vehicle.enriched | Um veículo foi catalogado pela primeira vez (por exemplo, um item processing do lote) |
vehicle.updated | O conteúdo de um veículo mudou (nova revisão) — novo preço FIPE, curadoria ou dado de outra fonte |
Cadastro
bash
curl -X POST https://datacar.digitalmart.com.br/api/v1/webhooks \
-H "Authorization: Bearer $DATACAR_API_KEY" -H "Content-Type: application/json" \
-d '{ "url": "https://dms.minharevenda.com.br/webhooks/datacar", "events": ["vehicle.updated", "vehicle.enriched"] }'A resposta traz o secret (whsec_…) uma única vez. A URL precisa ser HTTPS e apontar para um endereço público.
Requisição enviada
http
POST /webhooks/datacar HTTP/1.1
Content-Type: application/json
User-Agent: Datacar-Webhooks/1.0
Datacar-Event: vehicle.updated
Datacar-Delivery: 7f8e4c1a-3b2d-4e5f-9a8b-1c2d3e4f5a6b
Datacar-Signature: t=1798012345,v1=5f2b7c…e91a
{
"id": "7f8e4c1a-3b2d-4e5f-9a8b-1c2d3e4f5a6b",
"type": "vehicle.updated",
"created_at": "2026-10-27T12:00:00.000Z",
"data": {
"vehicle_id": "005340-6:2015-3",
"revision": 4,
"price_changed": true,
"vehicle": { "fipeCode": "005340-6", "price": { "month": "2026-11", "amountCents": 9600000 }, "...": "..." }
}
}Verificando a assinatura
Calcule o HMAC-SHA256 de <t>.<corpo bruto> com o seu secret e compare com v1 em tempo constante. Rejeite timestamps com mais de 5 minutos de diferença para evitar replay.
Node.jsjs
import { createHmac, timingSafeEqual } from 'node:crypto'
export function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
const t = Number(parts.t)
if (!parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest()
const given = Buffer.from(parts.v1, 'hex')
return given.length === expected.length && timingSafeEqual(given, expected)
}
// Express: use o corpo BRUTO, não o JSON já parseado.
app.post('/webhooks/datacar', express.raw({ type: 'application/json' }), (req, res) => {
if (!verify(req.body.toString('utf8'), req.get('Datacar-Signature') ?? '', process.env.DATACAR_WEBHOOK_SECRET)) {
return res.sendStatus(400)
}
const event = JSON.parse(req.body)
enfileirar(event) // responda rápido; processe de forma assíncrona
res.sendStatus(204)
})PHPphp
function datacar_verify(string $rawBody, string $header, string $secret, int $tolerance = 300): bool {
parse_str(str_replace(',', '&', $header), $parts);
if (empty($parts['v1']) || abs(time() - (int) $parts['t']) > $tolerance) return false;
$expected = hash_hmac('sha256', $parts['t'] . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1']);
}
$raw = file_get_contents('php://input');
if (!datacar_verify($raw, $_SERVER['HTTP_DATACAR_SIGNATURE'] ?? '', getenv('DATACAR_WEBHOOK_SECRET'))) {
http_response_code(400); exit;
}
$event = json_decode($raw, true);Entrega e retentativas
- Qualquer resposta
2xxem até 10 segundos conta como sucesso. Redirecionamentos não são seguidos. - Falhas são retentadas até 8 vezes com backoff exponencial (10 s, 20 s, 40 s… até cerca de 20 minutos).
- A mesma entrega pode chegar mais de uma vez: use
Datacar-Deliverycomo chave de idempotência. - A ordem não é garantida: compare
data.versioncom a versão que você já tem. - Depois de 20 entregas seguidas com falha definitiva, o endpoint é desativado e o motivo aparece em
GET /v1/webhooks.
Webhooks complementam o feed de alterações, mas não o substituem. Rode o feed periodicamente como rede de segurança.