Gareth Wilson Gareth Wilson

Guide to Lithic Webhooks: Features and Best Practices

Published


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

FeatureDetails
ConfigurationPer-event-type subscriptions in the Lithic dashboard
Signature headerswebhook-id, webhook-timestamp, webhook-signature (Svix svix-* aliases too)
Signature schemebase64 HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{rawBody}
Signing secretPer-subscription, whsec_-prefixed (base64-decode the part after the prefix)
Replay protectionReject timestamps outside ~5 minutes
Auto-disableAfter ~5 continuous days of failed deliveries
SDKslithic (webhooks.unwrap), plus svix / standardwebhooks

Common events

Lithic event names use resource.action:

EventFires when
card.createdA card is created
card_transaction.updatedA card transaction updates
payment_transaction.createdA payment transaction is created
dispute.updatedA 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 the svix / standardwebhooks libraries, 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-id and 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.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.