Guide to Lithic Webhooks: Features and Best Practices
Lithic webhooks notify your systems about card and transaction activity: a card is created, a transaction updates, a dispute changes. If you're building card issuing or payments on Lithic, webhooks are how you react to account and transaction events without polling.
This guide covers how Lithic webhooks work, the events you'll handle, how to verify signatures (Lithic is Svix-powered and implements Standard Webhooks), secret rotation, and the best practices for production.
What are Lithic webhooks?
Lithic webhooks are HTTP POSTs delivered to endpoints you configure, powered by Svix and implementing the Standard Webhooks spec. Each request carries webhook-id, webhook-timestamp, and webhook-signature headers (Svix also emits the svix-id / svix-timestamp / svix-signature aliases; Lithic's docs use the webhook-* names).
Because it's Standard Webhooks, verification is the familiar HMAC-over-id.timestamp.body scheme, and the official Lithic SDKs wrap it for you.
Lithic webhook features
| Feature | Details |
|---|---|
| Configuration | Per-event-type subscriptions in the Lithic dashboard |
| Signature headers | webhook-id, webhook-timestamp, webhook-signature (Svix svix-* aliases too) |
| Signature scheme | base64 HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{rawBody} |
| Signing secret | Per-subscription, whsec_-prefixed (base64-decode the part after the prefix) |
| Replay protection | Reject timestamps outside ~5 minutes |
| Auto-disable | After ~5 continuous days of failed deliveries |
| SDKs | lithic (webhooks.unwrap), plus svix / standardwebhooks |
Common events
Lithic event names use resource.action:
| Event | Fires when |
|---|---|
card.created | A card is created |
card_transaction.updated | A card transaction updates |
payment_transaction.created | A payment transaction is created |
dispute.updated | A dispute changes |
Subscribe to the event types you process (the transaction families are card_transaction.* and payment_transaction.*), and consult Lithic's events reference for the full list.
Setting up Lithic webhooks
Configure per-event-type subscriptions and rotate secrets in the Lithic dashboard. Each subscription has its own whsec_-prefixed signing secret. Failed deliveries retry with backoff and the subscription auto-disables after about 5 continuous days of failures.
Securing Lithic webhooks
Each delivery is signed with HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{rawBody}, using the base64-decoded portion of your whsec_ secret as the key, and webhook-signature may carry multiple space-delimited v1,<sig> entries during rotation. The official Lithic SDK verifies and parses in one call:
const Lithic = require("lithic");
const client = new Lithic();
const SECRET = process.env.LITHIC_WEBHOOK_SECRET; // whsec_...
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
try {
// Verifies the signature + timestamp and returns the parsed event
const event = client.webhooks.unwrap(req.body, req.headers, SECRET);
res.sendStatus(200); // acknowledge fast
processQueue.add(event); // process asynchronously
} catch {
res.sendStatus(400); // verification failed
}
});
In Python, Lithic leans on the optional standardwebhooks package (the svix library also works directly):
from standardwebhooks import Webhook
wh = Webhook(os.environ["LITHIC_WEBHOOK_SECRET"]) # whsec_...
@app.post("/webhook")
def webhook():
try:
event = wh.verify(request.get_data(), dict(request.headers))
except Exception:
return "", 400
# process event asynchronously
return "", 200
Verify against the raw request body, and reject timestamps outside the ~5-minute tolerance (the libraries handle both).
Lithic webhook limitations and pain points
Standard Webhooks details still bite
The Problem: The whsec_ secret must be base64-decoded (after stripping the prefix) before it's the HMAC key, the signature is over id.timestamp.body (not the body alone), and rotation produces multiple v1,<sig> entries. Miss any and verification fails.
Why It Happens: These are Standard Webhooks/Svix mechanics.
Workarounds:
- Use the Lithic SDK (
webhooks.unwrap) or thesvix/standardwebhookslibraries, which handle all of it.
How Hookdeck Can Help: Hookdeck verifies Standard Webhooks signatures at the edge, including rotation, so your app receives pre-verified events. See our guide to Svix webhooks.
Auto-disable after sustained failures
The Problem: After ~5 continuous days of failed deliveries, the subscription auto-disables and you stop receiving events.
Why It Happens: Lithic (via Svix) prunes persistently failing endpoints.
Workarounds:
- Acknowledge fast so transient issues don't accumulate; monitor subscription status and re-enable.
How Hookdeck Can Help: Hookdeck always accepts deliveries and absorbs downstream failures itself, keeping the subscription active while it retries to your service.
The 5-minute replay window
The Problem: Deliveries older than ~5 minutes are rejected. A backlogged handler can see events age out before verification.
Why It Happens: The freshness window is the scheme's replay protection.
Workarounds:
- Acknowledge fast and process asynchronously so verification happens within the window; keep clocks in sync.
How Hookdeck Can Help: Hookdeck verifies once at the edge and durably queues events, so a downstream backlog doesn't risk deliveries aging out.
Duplicates
The Problem: Retries mean the same event can arrive more than once, and double-processing card or transaction events is costly.
Why It Happens: At-least-once delivery favors eventual delivery.
Workarounds:
- Dedupe on
webhook-idand make side effects idempotent.
How Hookdeck Can Help: Hookdeck deduplicates on the message ID at the edge, so retries don't double-process. See our guide to webhook idempotency.
Best practices
Use the SDK or a Standard Webhooks library
lithic's webhooks.unwrap, or svix / standardwebhooks, handle the base64 secret, the id.timestamp.body construction, rotation, and the timestamp window.
Verify against the raw body
Capture the raw body before parsing so the HMAC matches.
Acknowledge fast, process asynchronously
Return 2xx as soon as the signature verifies and defer work to a queue. See why to process webhooks asynchronously.
Dedupe on webhook-id
Use webhook-id as an idempotency key so retries don't double-process.
Make Lithic webhooks production-ready
Hookdeck verifies the Standard Webhooks signature, deduplicates, and durably queues every card and transaction event
Conclusion
Lithic webhooks are Svix-powered and implement Standard Webhooks: verify the webhook-signature over id.timestamp.body with the base64-decoded whsec_ secret, reject stale timestamps, and dedupe on webhook-id. The official SDK's webhooks.unwrap handles verification in one call, and subscriptions auto-disable after ~5 days of failures.
Hookdeck verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique card and transaction events.
Get started with Hookdeck for free and handle Lithic webhooks reliably in minutes.