# Lithic Webhooks

Lithic delivers events (card issuing, transactions, disputes, money movement) to
your endpoint as webhooks. They implement the [Standard Webhooks](https://www.standardwebhooks.com/)
spec (powered by Svix), so verification is HMAC-SHA256 over
`{webhook-id}.{webhook-timestamp}.{rawBody}` with a per-subscription signing
secret.

## When to Use This Skill

* How do I receive Lithic webhooks?
* How do I verify Lithic webhook signatures?
* How do I handle `card_transaction.updated` or `payment_transaction.created` events?
* Why is my Lithic webhook signature verification failing?
* How do I configure a Lithic event subscription and signing secret?

## Signature at a Glance

| Property | Value |
| --- | --- |
| Headers | `webhook-id`, `webhook-timestamp`, `webhook-signature` (Svix also sends `svix-*` aliases) |
| Algorithm | HMAC-SHA256, base64-encoded |
| Signed content | `{webhook-id}.{webhook-timestamp}.{rawBody}` |
| Secret | Per-subscription, prefixed `whsec_` (base64-decode the part after the prefix) |
| Signature header format | Space-delimited list of `v1,<base64sig>` |
| Timestamp tolerance | ~5 minutes (reject outside the window to block replays) |

## Verification (core)

The official Lithic SDKs verify the signature and parse the event in a single
call — `webhooks.unwrap(rawBody, headers, secret)`. It enforces the timestamp
tolerance and throws when verification fails. Always pass the raw request body.

Node (Express / Next.js):

```javascript
const Lithic = require('lithic');
const lithic = new Lithic({ apiKey: process.env.LITHIC_API_KEY });

// rawBody: string from express.raw() or await request.text()
// headers: req.headers (Express) or Object.fromEntries(request.headers) (Next.js)
try {
  const event = lithic.webhooks.unwrap(rawBody, headers, process.env.LITHIC_WEBHOOK_SECRET);
  // event.event_type -> "card.created", "payment_transaction.created", ...
} catch (err) {
  // invalid signature or stale timestamp -> respond 400
}

```

Python (FastAPI):

```python
from lithic import Lithic
client = Lithic(api_key=os.environ["LITHIC_API_KEY"])

try:
    event = client.webhooks.unwrap(raw_body, request.headers, secret=os.environ["LITHIC_WEBHOOK_SECRET"])
    # event.event_type -> "card.created", ...
except Exception:
    raise HTTPException(status_code=400, detail="Invalid signature")

```

Python verification needs the optional `standardwebhooks` package
(`pip install "lithic[webhooks]"`).

> For complete handlers with tests, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/lithic-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/lithic-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/lithic-webhooks/examples/fastapi/).

## Common Event Types

Lithic event objects carry an `event_type` field in `resource.action` form.

| Event | Triggered When |
| --- | --- |
| `card.created` | A new card is created |
| `card.updated` | A card's state or attributes change |
| `card_transaction.updated` | A card authorization or clearing is updated |
| `payment_transaction.created` | An ACH / money-movement payment is created |
| `payment_transaction.updated` | A payment transaction changes state |
| `dispute.updated` | A dispute advances in its lifecycle |
| `balance.updated` | A financial account balance changes |
| `three_ds_authentication.created` | A 3DS authentication is initiated |

Other events include `account_holder.*`, `digital_wallet.*`, `tokenization.*`,
`external_bank_account.*`, `book_transfer_transaction.*`, and `statements.created`.
See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lithic-webhooks/references/overview.md) for the full list.

## Environment Variables

```bash
LITHIC_WEBHOOK_SECRET=whsec_xxxxx   # Per-subscription signing secret (from the Lithic Dashboard)
LITHIC_API_KEY=your_api_key         # Only needed to call the Lithic API

```

## Local Development

Receive webhooks locally with the Hookdeck CLI — no account required, one paste-and-run line:

```bash
npx hookdeck-cli listen 3000 lithic --path /webhooks/lithic

```

The CLI prints a public URL. Register it as your event subscription URL in the
Lithic Dashboard, then trigger events (or replay them from the Hookdeck UI).

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lithic-webhooks/references/overview.md) - Lithic webhook concepts and the full event list
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lithic-webhooks/references/setup.md) - Create an event subscription, get the signing secret, rotate secrets
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/lithic-webhooks/references/verification.md) - Signature verification details, SDK usage, and manual fallback