# Guide to Smartcar Webhooks: Features and Best Practices

Smartcar webhooks notify your application when connected-vehicle data changes: a monitored signal updates, a data retrieval fails. If you're building on Smartcar's vehicle APIs, webhooks are how you react to vehicle state without polling each car on a timer.

This guide covers how Smartcar webhooks work, the events you'll handle, how to verify the `SC-Signature`, the VERIFY challenge handshake at setup, and the best practices for production.

## What are Smartcar webhooks?

Smartcar webhooks are HTTP POSTs delivered to a URL you configure, each carrying a vehicle event. Smartcar signs every delivery with an `SC-Signature` header keyed with your Application Management Token (AMT). At setup, Smartcar sends a one-time VERIFY event you answer with a keyed hash of a challenge.

## Smartcar webhook features

| Feature | Details |
| --- | --- |
| Signature header | `SC-Signature` |
| Signature scheme | hex HMAC-SHA256 over the raw body, keyed with the Application Management Token (AMT) |
| Setup handshake | VERIFY event: reply `{"challenge": HMAC-SHA256(challenge, AMT)}` within 15s |
| Events | `VERIFY`, `VEHICLE_STATE`, `VEHICLE_ERROR` |
| Acknowledgement | Any 2xx within 15 seconds |
| Ordering | Not guaranteed; dedupe on `eventId` (`deliveryId` changes per attempt) |
| SDK | Official SDKs verify: `verifyPayload` / `hashChallenge` (Node), `verify_payload` / `hash_challenge` (Python) |

## Common events

Smartcar's current signals model uses these event types:

| Event | Fires when |
| --- | --- |
| `VERIFY` | One-time setup challenge (respond with the keyed hash) |
| `VEHICLE_STATE` | A monitored vehicle signal changes |
| `VEHICLE_ERROR` | A data retrieval fails for a vehicle |

(The legacy v2 `scheduled` / `eventBased` webhook shapes are deprecated; build against the signals model above.)

## Setting up Smartcar webhooks

Create a webhook in the Smartcar dashboard, pointing at your HTTPS endpoint. Your Application Management Token (AMT, also called MAT) from the dashboard is the key you verify with. On setup, Smartcar sends the VERIFY event (below); until you answer it, the webhook isn't activated.

## Securing Smartcar webhooks

Every delivery carries an `SC-Signature` header: a hex-encoded HMAC-SHA256 of the raw request body, keyed with your AMT. The VERIFY event is special: its `data.challenge` is a random string, and you reply with `{"challenge": "<hex>"}` where the hex is `HMAC-SHA256(challenge, AMT)`. The official SDKs provide both helpers.

```javascript
const smartcar = require("smartcar");

const AMT = process.env.SMARTCAR_AMT; // Application Management Token

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const body = JSON.parse(req.body);

  // Setup: answer the VERIFY challenge within 15s
  if (body.eventName === "VERIFY") {
    return res.status(200).json({ challenge: smartcar.hashChallenge(AMT, body.payload.challenge) });
  }

  // All other events: verify SC-Signature over the raw body
  if (!smartcar.verifyPayload(AMT, req.headers["sc-signature"], req.body)) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge within 15s
  processQueue.add(body); // process asynchronously
});

```

The same in Python:

```python
import os
import smartcar

AMT = os.environ["SMARTCAR_AMT"]

@app.post("/webhook")
def webhook():
    body = request.get_json()
    if body["eventName"] == "VERIFY":
        return {"challenge": smartcar.hash_challenge(AMT, body["payload"]["challenge"])}, 200

    if not smartcar.verify_payload(AMT, request.headers["SC-Signature"], request.get_data(as_text=True)):
        return "", 401
    # process asynchronously
    return "", 200

```

If you verify manually, compute hex HMAC-SHA256 over the raw body keyed with the AMT and compare in constant time; for VERIFY, HMAC the `challenge` with the same AMT.

## Smartcar webhook limitations and pain points

### The VERIFY handshake gates activation

The Problem: Until you correctly answer the VERIFY event (with the keyed hash, within 15 seconds), the webhook isn't activated and you receive nothing.

Why It Happens: Smartcar confirms you hold the AMT and control the endpoint before sending vehicle data.

Workarounds:

* Branch on `eventName === "VERIFY"` and reply with `hashChallenge(AMT, challenge)` fast.

How Hookdeck Can Help: Hookdeck can answer the VERIFY handshake and verify signatures at the edge, so activation isn't a manual step and your app only sees verified events.

### One token verifies and answers the challenge

The Problem: The AMT is used both to verify `SC-Signature` and to compute the VERIFY response. Using the wrong token (or a per-vehicle credential) fails both.

Why It Happens: Smartcar keys the whole webhook scheme on the Application Management Token.

Workarounds:

* Store the AMT securely and use it for both verification and the challenge.

How Hookdeck Can Help: Hookdeck verifies with the AMT at the edge, so the token lives in one place rather than in every handler.

### Ordering and duplicates

The Problem: Ordering isn't guaranteed and events can repeat. The `deliveryId` changes per attempt, so it's not a dedup key; the `eventId` is.

Why It Happens: At-least-once delivery with per-attempt delivery IDs.

Workarounds:

* Dedupe on `eventId` (not `deliveryId`), and don't rely on receipt order.

How Hookdeck Can Help: Hookdeck deduplicates on the event ID at the edge, so retries don't double-process. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### The 15-second window

The Problem: You must return 2xx within 15 seconds. Slow synchronous work risks the window and triggers retries.

Why It Happens: Smartcar caps how long it waits for an ack.

Workarounds:

* Verify, enqueue, and return 200 immediately; process off a queue.

How Hookdeck Can Help: Hookdeck acknowledges Smartcar within the window and durably queues events, decoupling the ack from your processing time.

## Best practices

### Verify with the AMT and answer VERIFY

Use `verifyPayload` / `verify_payload` for `SC-Signature` over the raw body, and `hashChallenge` / `hash_challenge` for the VERIFY event, both keyed with the AMT.

### Acknowledge within 15 seconds, process asynchronously

Return 2xx immediately and defer work to a queue. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Dedupe on `eventId`

Use `eventId` (not the per-attempt `deliveryId`) as your idempotency key.

### Build against the signals model

Handle `VEHICLE_STATE` and `VEHICLE_ERROR`; don't build on the deprecated scheduled/eventBased shapes.

## Conclusion

Smartcar webhooks verify with an `SC-Signature` HMAC keyed with your Application Management Token, and won't activate until you answer the VERIFY challenge with a keyed hash of the challenge within 15 seconds. Add unguaranteed ordering, duplicates deduped on `eventId`, and a 15-second ack window, and there's real handling to get right.

[Hookdeck](https://hookdeck.com) answers the VERIFY handshake, verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique vehicle events.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Smartcar webhooks reliably in minutes.