Event Structure
All webhook events follow this structure:Payment Events
Payment events track the lifecycle of payment transactions.payment.succeeded
Fired when a payment is successfully completed. Handler:managePayment() at supabase/functions/dodo-webhook/index.ts:120
Unique identifier for the payment
Payment status (e.g., “succeeded”)
Total payment amount
Three-letter currency code (e.g., “USD”)
Payment method identifier
Type of payment method (e.g., “card”)
Associated subscription identifier
Customer information object
Dodo customer identifier
Customer email address
Customer name
ISO 8601 timestamp of payment creation
ISO 8601 timestamp of last update
Brand identifier
Business identifier
Custom metadata object
Billing information object
Last four digits of card number
Card network (e.g., “Visa”, “Mastercard”)
Card type (e.g., “credit”, “debit”)
ISO country code of card issuer
Tax amount
Settlement amount in settlement currency
Currency code for settlement
Tax amount in settlement currency
Applied discount identifier
Payment link URL
Product cart details
Refund information
Dispute information
payment.failed
Fired when a payment fails. Handler:managePayment() at supabase/functions/dodo-webhook/index.ts:120
Includes all fields from payment.succeeded plus:
Error code describing the failure
Human-readable error message
payment.processing
Fired when a payment is being processed. Handler:managePayment() at supabase/functions/dodo-webhook/index.ts:120
Uses the same payload structure as payment.succeeded.
payment.cancelled
Fired when a payment is cancelled. Handler:managePayment() at supabase/functions/dodo-webhook/index.ts:120
Uses the same payload structure as payment.succeeded.
Subscription Events
Subscription events track the lifecycle of recurring subscriptions.subscription.active
Fired when a subscription becomes active. Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Side Effect: Updates user’s current_subscription_id via updateUserTier() at line 201
Unique identifier for the subscription
Subscription status (e.g., “active”)
Product identifier
Subscription quantity
Recurring amount before tax
Whether pricing includes tax
Three-letter currency code
Customer information
Dodo customer identifier
Customer email address
Customer name
ISO 8601 timestamp of subscription creation
ISO 8601 timestamp of next billing
ISO 8601 timestamp of previous billing
Billing frequency interval (e.g., “month”, “year”)
Number of intervals between payments
Subscription period interval
Number of subscription periods
Length of trial period in days
Billing information
Custom metadata
Applied discount identifier
Subscription addons
Whether subscription is on-demand
Whether subscription will cancel at next billing
ISO 8601 timestamp of cancellation
subscription.plan_changed
Fired when a subscription plan is changed (upgrade/downgrade). Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Side Effect: Updates user’s current_subscription_id via updateUserTier() at line 201
Uses the same payload structure as subscription.active.
subscription.renewed
Fired when a subscription is renewed. Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Uses the same payload structure as subscription.active.
subscription.on_hold
Fired when a subscription is placed on hold. Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Uses the same payload structure as subscription.active.
subscription.cancelled
Fired when a subscription is cancelled. Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Side Effect: Sets user’s current_subscription_id to null via downgradeToHobbyPlan() at line 214
Uses the same payload structure as subscription.active.
subscription.expired
Fired when a subscription expires. Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Side Effect: Sets user’s current_subscription_id to null via downgradeToHobbyPlan() at line 214
Uses the same payload structure as subscription.active.
subscription.failed
Fired when a subscription fails (e.g., payment failure). Handler:manageSubscription() at supabase/functions/dodo-webhook/index.ts:164
Side Effect: Sets user’s current_subscription_id to null via downgradeToHobbyPlan() at line 214
Uses the same payload structure as subscription.active.
Event Flow Examples
New Subscription Flow
payment.processing- Initial payment beginspayment.succeeded- Payment completessubscription.active- Subscription activated, user tier updated
Subscription Cancellation Flow
subscription.cancelled- Subscription cancelled, user downgraded- Future payment events stop
Failed Payment Flow
payment.processing- Payment attempt beginspayment.failed- Payment fails with error detailssubscription.failed- Subscription marked as failed, user downgraded
Testing Webhooks
During development, you can test webhook events using:- Dodo Dashboard: Send test events from your Dodo Payments dashboard
- Local Testing: Use ngrok or similar to expose your local Supabase function
- Replay Events: Re-send previous webhook events from the Dodo dashboard
All webhook events are idempotent - replaying the same event will not create duplicates due to upsert operations.
