Coming Soon - Webhook functionality is currently in development. This page documents the
planned implementation.
Overview
The plan.cancelled event is triggered when a recurring donation plan is cancelled, either by the donor, an admin, or due to payment failures. This event marks the end of the recurring giving relationship.
When This Event Fires
Donor cancels their recurring plan
Admin cancels plan in the dashboard
Plan is cancelled due to multiple failed payments
Plan is cancelled via the API
Payment method expires and cannot be updated
When a plan is cancelled, no future payments will be processed. Past transactions remain in the
system and this event does not indicate a refund.
Webhook Payload
{
"id" : "evt_cde345fgh678" ,
"type" : "plan.cancelled" ,
"created_at" : "2024-06-15T16:30:00Z" ,
"data" : {
"id" : "plan_789012" ,
"amount" : 2500 ,
"currency" : "USD" ,
"frequency" : "monthly" ,
"interval" : 1 ,
"status" : "cancelled" ,
"payment_method" : "card" ,
"campaign" : {
"id" : "camp_abc123" ,
"title" : "Monthly Giving Program" ,
"url" : "https://givebutter.com/monthly-giving"
},
"donor" : {
"id" : "cont_987654321" ,
"first_name" : "Jane" ,
"last_name" : "Doe" ,
"email" : "[email protected] " ,
"phone" : "+1234567890"
},
"cancellation" : {
"reason" : "donor_request" ,
"note" : "Moving to annual giving instead" ,
"cancelled_by" : "donor" ,
"cancelled_at" : "2024-06-15T16:30:00Z"
},
"total_payments" : 5 ,
"total_donated" : 12500 ,
"started_at" : "2024-01-15T11:00:00Z" ,
"created_at" : "2024-01-15T11:00:00Z" ,
"updated_at" : "2024-06-15T16:30:00Z"
},
"account_id" : "acct_xyz789"
}
Event Data Fields
Unique identifier for the recurring plan (prefixed with plan_)
Recurring donation amount in cents (e.g., 2500 = $25.00)
Three-letter ISO currency code (e.g., USD, CAD, EUR)
Payment frequency: weekly, monthly, quarterly, or yearly
Number of frequency periods between payments
Plan status. Always cancelled for this event.
Payment method that was used: card, paypal, ach, or bank_transfer
Campaign the recurring plan supported Campaign ID (prefixed with camp_)
Public URL for the campaign
Information about the recurring donor Contact ID (prefixed with cont_)
Details about the cancellation Show Cancellation Properties
Reason code: donor_request, payment_failed, card_expired, admin, or other
Optional note about why the plan was cancelled
Who cancelled: donor, admin, or system
ISO 8601 timestamp when plan was cancelled
Total number of successful payments made during the plan
Total amount donated through this plan in cents
ISO 8601 timestamp when the plan began
ISO 8601 timestamp when the plan was originally created
ISO 8601 timestamp when the plan was last updated (cancellation time)
Common Use Cases
Send Cancellation Confirmation
Confirm cancellation and thank donors for their past support: async function handlePlanCancelled ( event ) {
const { donor , campaign , total_payments , total_donated , cancellation } = event . data ;
await sendEmail ({
to: donor . email ,
subject: 'Your recurring donation has been cancelled' ,
template: 'plan-cancelled' ,
data: {
first_name: donor . first_name ,
campaign_name: campaign . title ,
total_payments: total_payments ,
total_donated: total_donated / 100 ,
cancelled_at: cancellation . cancelled_at
}
});
}
Trigger Win-Back Campaign
Attempt to re-engage cancelled donors: async function handlePlanCancelled ( event ) {
const { donor , campaign , cancellation , total_donated } = event . data ;
// Wait 30 days, then send win-back email
await scheduler . scheduleEmail ({
to: donor . email ,
template: 'winback-recurring' ,
send_at: new Date ( Date . now () + 30 * 24 * 60 * 60 * 1000 ),
data: {
first_name: donor . first_name ,
campaign_name: campaign . title ,
past_impact: calculateImpact ( total_donated ),
reactivate_url: campaign . url
}
});
}
Move donors from active to lapsed recurring segments: async function handlePlanCancelled ( event ) {
const { donor , cancellation , total_payments } = event . data ;
await crm . updateContact ( donor . id , {
donor_type: 'lapsed_recurring' ,
recurring_status: 'cancelled' ,
cancellation_reason: cancellation . reason ,
cancelled_at: cancellation . cancelled_at ,
lifetime_payments: total_payments ,
tags: [ 'former-monthly-donor' ]
});
// Remove from recurring lists
await emailPlatform . removeFromList ( donor . email , 'Monthly Donors Circle' );
await emailPlatform . addToList ( donor . email , 'Lapsed Monthly Donors' );
}
Alert Team About Cancellations
Notify fundraising team to follow up with high-value donors: async function handlePlanCancelled ( event ) {
const { donor , amount , total_donated , cancellation } = event . data ;
// Alert for significant donors
if ( total_donated >= 50000 || amount >= 10000 ) {
await sendNotification ({
channel: '#major-donors' ,
message: `⚠️ High-value recurring donor cancelled` ,
details: {
donor: ` ${ donor . first_name } ${ donor . last_name } ` ,
monthly_amount: `$ ${ amount / 100 } ` ,
total_given: `$ ${ total_donated / 100 } ` ,
reason: cancellation . reason ,
note: cancellation . note
},
action: 'Schedule personal follow-up call'
});
}
}
Analyze Cancellation Patterns
Track cancellation reasons for improvement insights: async function handlePlanCancelled ( event ) {
const { cancellation , frequency , amount , total_payments } = event . data ;
await analytics . trackEvent ( 'recurring_plan_cancelled' , {
reason: cancellation . reason ,
cancelled_by: cancellation . cancelled_by ,
frequency: frequency ,
monthly_value: amount / 100 ,
lifetime_payments: total_payments ,
days_active: calculateDaysActive ( event . data . started_at , cancellation . cancelled_at )
});
// Update cancellation metrics
await metrics . increment ( 'recurring_cancellations' , {
reason: cancellation . reason ,
month: new Date (). getMonth ()
});
}
Ask donors why they cancelled to improve retention: async function handlePlanCancelled ( event ) {
const { donor , cancellation } = event . data ;
// Only ask if donor cancelled themselves
if ( cancellation . cancelled_by === 'donor' ) {
await sendEmail ({
to: donor . email ,
subject: 'We value your feedback' ,
template: 'cancellation-survey' ,
data: {
first_name: donor . first_name ,
survey_url: generateSurveyLink ( event . data . id ),
incentive: '$10 gift card for completing survey'
}
});
}
}
Cancellation Reasons
Reason Code Description donor_requestDonor chose to cancel payment_failedMultiple payment failures card_expiredPayment method expired and not updated adminAdmin cancelled in dashboard otherOther reason (check cancellation.note)
Who Can Cancel
Cancelled By Description donorDonor cancelled via their account or email link adminStaff member cancelled in the dashboard systemAutomatically cancelled (e.g., payment failures)