Skip to main content
POST
/
v1
/
stripe
/
webhook
Webhook receiver
curl --request POST \
  --url https://api.example.com/v1/stripe/webhook
This endpoint is called by Stripe, not by your client. It receives events for the user-account configured as STRIPE_WEBHOOK_SECRET.

Required Stripe configuration

In your Stripe dashboard → Developers → Webhooks, add:
  • Endpoint URL: https://your-domain/api/v1/stripe/webhook
  • Events to send:
    • checkout.session.completed
    • customer.subscription.deleted
    • customer.subscription.paused
    • invoice.payment_failed
  • Copy the Signing secret into STRIPE_WEBHOOK_SECRET.

What gets granted on checkout.session.completed

The session carries metadata.lavendly_kind and either lavendly_plan (subscription) or lavendly_credits (pack). The handler dispatches:
KindAction
subscriptionsetUserPlan(user_id, plan)
credit_packtopUpCredits(user_id, credits)

Dedupe

Stripe delivers events at least once. We persist a marker per event id at /data/storage/_billing/_events/<event_id>.json; a re-delivery returns { "received": true, "deduped": true } without re-running the handler.

Errors

StatusMeaning
400Missing or invalid signature → Stripe stops retrying.
200Processed (or deduped).
500Handler crashed, Stripe will retry with exponential backoff.
503STRIPE_SECRET_KEY not configured, Stripe will retry.