Guide to High Mobility Webhooks: Features and Best Practices
High Mobility connects fleet management, insurance and mobility apps to data from vehicles built by many different carmakers. Before an app can read a vehicle's data, the vehicle has to be cleared for the app through a process that differs between carmakers and can take some time. High Mobility webhooks tell you when that clearance is approved, rejected or revoked, so your app can start pulling data, or warn the fleet manager, without polling.
The sections below cover setting up a High Mobility webhook, the two events it sends, checking X-HM-Signature-256, and building for a retry policy that can keep resending for more than a day.
What are High Mobility webhooks?
Each High Mobility app has one webhook configuration with a URL, a secret you choose, and the events to send. The secret belongs to the webhook configuration, which is tied to a project. You can reuse a secret across projects, but High Mobility recommends a unique one for each.
There are only two events today. fleet_clearance_changed reports the outcome of a clearance, and ping confirms the configuration whenever it is created or changed. All webhooks work with the emulators, but which carmakers send clearance events in production varies.
High Mobility webhook features
| Feature | Details |
|---|---|
| Signature | hex HMAC-SHA256 |
| Signature header | x-hm-signature-256 |
| Body | JSON |
| Event routing | Body field event.type |
Delivery details from High Mobility's webhook documentation:
- Only HTTPS URLs are accepted.
- Any
2xxis success, and no response body is needed. - Connect and response timeouts are 10 seconds each.
- Failed deliveries are retried every 10 minutes, up to 200 attempts in production and 5 in the sandbox.
X-HM-Deliveryidentifies a delivery and is repeated on its retries.- Events can be duplicated or arrive out of order, even for the same VIN.
- There is no fixed set of source IP addresses.
Events and payloads
| Event | Triggered When | Common Use Cases |
|---|---|---|
fleet_clearance_changed | A fleet vehicle's clearance changes | Track onboarding status, surface rejections (event.action = rejected, event.detail = reason) |
ping | The webhook configuration changes | Confirm the endpoint is reachable and verifying correctly |
fleet_clearance_changed fires for approvals, rejections and revocations. Intermediate states such as revoking and canceling don't trigger it. ping is a normal signed delivery, not a challenge, and its vehicle.vin is *. High Mobility saves the configuration whether or not the ping succeeds, but your endpoint should still return 2xx.
This is the fleet_clearance_changed sample from the webhook samples library:
{
"vehicle": {
"vin": "1HMCF6112HA3FBBCC"
},
"event": {
"type": "fleet_clearance_changed",
"action": "rejected",
"detail": "invalid VIN",
"received_at": "2025-06-19T09:49:08.386159Z"
},
"application": {
"id": "A77294AC8DA324FB46DA98921"
}
}
event.action holds the new clearance state and event.detail the reason, when there is one. event.received_at is when High Mobility received the change, which helps when deliveries arrive out of order. application.id identifies the app, useful if several apps share one receiver.
Setting up High Mobility webhooks
- In the High Mobility console, go to Apps, open your app and select API Credentials, then Webhooks.
- Enter the HTTPS URL of your receiver, for example
https://example.com/webhooks/high-mobility. - Enter a secret: a random, high-entropy string you generate. Store the same value in your server's configuration as
HM_WEBHOOK_SECRET, exactly as entered. - Choose the events, then click Update. High Mobility sends a
ping.
To use Hookdeck, create a High Mobility source with the same secret as its verification secret, and enter the source URL in step 2.
HTTP Method
High Mobility's docs don't name the HTTP method. Hookdeck's High Mobility source pre-selects POST without managing the method, so any other method the sender uses must also be allowed on the source. The linked skill examples accept GET, POST, PUT, PATCH and DELETE.
Test a delivery
Forward events to a local server with the Hookdeck CLI:
npx hookdeck-cli listen 3000 high-mobility --path /webhooks/high-mobility
Saving the webhook configuration sends a ping, and sandbox clearance events exercise the rest of your handler.
Securing High Mobility webhooks
X-HM-Signature-256 is sha256= followed by a hex HMAC-SHA256 of the exact request body, keyed with your secret. Strip the prefix, compute the HMAC over the raw bytes, and compare in constant time. High Mobility still sends a SHA-1 X-HM-Signature header for older integrations, but describes it as deprecated, so ignore it.
Node.js verification
const crypto = require('node:crypto');
function verifySignature(rawBody, headers, secret) {
if (!Buffer.isBuffer(rawBody) || typeof secret !== 'string' || !secret) return false;
const signature = headers['x-hm-signature-256'];
if (typeof signature !== 'string') return false;
const key = Buffer.from(secret, 'utf8');
const message = rawBody;
const candidates = [signature.startsWith('sha256=') ? signature.slice(7) : signature];
const expected = crypto.createHmac('sha256', key).update(message).digest();
return candidates.some((candidate) => {
if (!/^[0-9a-fA-F]{64}$/.test(candidate)) return false;
const received = Buffer.from(candidate, 'hex');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
});
}
The receiver accepts any method, as the HTTP Method section explains, and handles ping before looking for a vehicle:
const express = require('express');
const app = express();
app.all('/webhooks/high-mobility', express.raw({ type: '*/*' }), async (req, res) => {
const secret = process.env.HM_WEBHOOK_SECRET;
if (!secret) return res.sendStatus(500);
const body = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);
if (!verifySignature(body, req.headers, secret)) return res.sendStatus(401);
if (!body.length) return res.sendStatus(200);
let payload;
try {
payload = JSON.parse(body.toString('utf8'));
} catch {
return res.sendStatus(400);
}
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return res.sendStatus(400);
try {
if (payload.event?.type === 'ping') return res.sendStatus(200);
if (payload.event?.type === 'fleet_clearance_changed') {
await recordClearanceChange(req.get('x-hm-delivery'), {
vin: payload.vehicle?.vin,
state: payload.event.action,
reason: payload.event.detail ?? null,
receivedAt: payload.event.received_at,
});
}
} catch {
return res.sendStatus(503);
}
res.sendStatus(200);
});
recordClearanceChange skips a delivery ID it has already stored. Before acting on a clearance, compare receivedAt with the latest one stored for the VIN, or fetch the current clearance from the API, since an older change can arrive after a newer one.
Nothing time-based is signed, so verification can't tell an old request from a new one. Hookdeck's High Mobility source checks the same sha256= HMAC with no age limit, returns 200 to High Mobility, and records a signature mismatch as failed verification.
Make High Mobility webhooks production-ready. Hookdeck Event Gateway verifies each clearance event, answers well within the 10-second timeout, and gives you a searchable history of every clearance change per VIN.
High Mobility webhook limitations and pain points
Retries run for over a day
The Problem: One failing delivery keeps arriving every 10 minutes, and old clearance changes can land long after newer ones.
Why It Happens: High Mobility retries every 10 minutes up to 200 attempts in production, which is more than 33 hours. Ordering is not guaranteed.
Workarounds:
- Deduplicate on
X-HM-Delivery. - Compare
event.received_atwith your stored state before applying a change, or check the latest clearance through the API.
How Hookdeck Can Help: Hookdeck accepts each delivery the first time, so High Mobility's retry queue stays empty, and you control how often and how long Hookdeck retries your service.
The sandbox gives up after 5 attempts
The Problem: Sandbox events go missing during development when your local server isn't running.
Why It Happens: The sandbox retries only up to 5 times in total, 10 minutes apart.
Workarounds:
- Keep a tunnel and receiver running while testing clearances.
- Trigger the clearance again if a test event was lost.
How Hookdeck Can Help: With the Hookdeck CLI, sandbox events are held by the Hookdeck source while your laptop is offline, and you can replay them when you reconnect.
The same change can arrive under different IDs
The Problem: Deduplicating on X-HM-Delivery alone doesn't catch every repeat.
Why It Happens: High Mobility notes that separately generated events may represent the same underlying change with different delivery identifiers.
Workarounds:
- Treat clearance updates as state, not as actions: applying the same state to a VIN twice should be harmless.
- Key side effects such as emails on VIN plus new state.
How Hookdeck Can Help: Hookdeck deduplication can use body fields such as vehicle.vin, event.action and event.received_at to drop repeats within a window you set.
Coverage depends on the carmaker
The Problem: Clearance events you see in the emulator may not arrive for some brands in production.
Why It Happens: High Mobility states that production availability differs by carmaker.
Workarounds:
- Check availability for the brands in your fleet before relying on webhooks alone.
- Keep a periodic clearance status check as a fallback.
How Hookdeck Can Help: Hookdeck's request history shows which VINs and brands are generating events, so gaps stand out.
Best practices
Ignore the SHA-1 header
Verify only X-HM-Signature-256. The SHA-1 header exists for older integrations.
Make clearance updates idempotent
Store the latest clearance state per VIN and treat each event as a request to move towards it. The idempotency guide for webhooks shows how to record what you've already handled.
Answer inside 10 seconds
Fetching vehicle data or notifying a fleet manager can be done after the response. See why webhooks should be processed asynchronously.
Use a unique secret per project
High Mobility allows sharing a secret between projects but recommends against it. A unique secret limits the damage if one leaks.
Conclusion
High Mobility webhooks report vehicle clearance outcomes and configuration pings, signed with an HMAC-SHA256 of the body in X-HM-Signature-256. Verify the raw body, accept the method High Mobility uses, and treat each clearance as state that can arrive late, twice or out of order during up to 200 retries.
Hookdeck Event Gateway takes those retries off High Mobility's hands and delivers clearance events to your app on your schedule.
Get started with Hookdeck for free and handle High Mobility webhooks reliably in minutes.