> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dacard.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Stripe webhook integration for subscription lifecycle events

# Webhooks

Dacard.ai uses Stripe webhooks to handle subscription lifecycle events. When a billing event occurs in Stripe, it sends a signed webhook to the platform, which processes the event and updates account state accordingly.

## Webhook endpoint

```
POST https://app.dacard.ai/api/stripe/webhook
```

This endpoint is **public** (no Clerk authentication required) but verifies the Stripe signature on every request.

## Supported events

| Event                           | Description                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------- |
| `checkout.session.completed`    | A customer completed a checkout session. Creates or upgrades the subscription.    |
| `customer.subscription.updated` | A subscription was modified (plan change, renewal, payment method update).        |
| `customer.subscription.deleted` | A subscription was cancelled or expired. Downgrades the account to the free plan. |
| `invoice.payment_succeeded`     | A payment was successfully processed. Resets monthly credit usage.                |
| `invoice.payment_failed`        | A payment attempt failed. May trigger grace period or account restriction.        |

## Signature verification

Every incoming webhook is verified using the Stripe webhook signing secret. Requests with invalid signatures are rejected with a `400 Bad Request`.

The signing secret is stored as an environment variable (`STRIPE_WEBHOOK_SECRET`) and is never exposed to the client.

```
Stripe-Signature: t=1234567890,v1=abc123...
```

## Subscription lifecycle

```
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Sign up    │────▸│   Checkout   │────▸│   Active     │
│   (Free)     │     │  (Stripe)    │     │  (Pro/Biz)   │
└──────────────┘     └──────────────┘     └──────┬───────┘
                                                  │
                                          ┌───────┴───────┐
                                          ▼               ▼
                                   ┌────────────┐  ┌────────────┐
                                   │  Renewed   │  │ Cancelled  │
                                   │ (credits   │  │ (→ Free)   │
                                   │  reset)    │  │            │
                                   └────────────┘  └────────────┘
```

1. User signs up on the **Free** plan (no Stripe involvement)
2. User initiates upgrade via `POST /api/stripe/checkout` to create a Stripe Checkout Session
3. On successful payment, `checkout.session.completed` fires and the account is upgraded
4. Monthly renewals trigger `invoice.payment_succeeded`, resetting credit counters
5. Cancellation triggers `customer.subscription.deleted`, downgrading to Free

## Retry behavior

Stripe retries failed webhook deliveries with exponential backoff for up to 3 days. The webhook handler is idempotent, so duplicate deliveries are safe.

## Testing webhooks locally

Use the [Stripe CLI](https://stripe.com/docs/stripe-cli) to forward events to your local dev server:

```bash theme={null}
stripe listen --forward-to localhost:3001/api/stripe/webhook
```

The CLI provides a temporary webhook signing secret for local development.

## Related endpoints

* [Create Checkout Session](/api-reference/billing/create-checkout-session), Initiate a Stripe checkout
* [Create Billing Portal Session](/api-reference/billing/create-billing-portal-session), Manage existing subscriptions
