Skip to main content
Coming Soon - Webhook functionality is currently in development. This page documents the planned implementation.

Overview

The plan.created event is triggered when a donor sets up a recurring donation plan. This event fires when the plan is created, typically after the first successful payment is processed.

When This Event Fires

  • Donor signs up for recurring monthly giving
  • Recurring plan is set up via the dashboard
  • Subscription is created through the API
  • Donor converts one-time donation to recurring
  • Recurring plan is established for any frequency
This event fires when the plan is created. Each subsequent payment will trigger a transaction.created event. The initial payment may fire before or after this event depending on payment timing.

Webhook Payload

{
  "id": "evt_wxy789zab012",
  "type": "plan.created",
  "created_at": "2024-01-15T11:00:00Z",
  "data": {
    "id": "plan_789012",
    "amount": 2500,
    "currency": "USD",
    "frequency": "monthly",
    "interval": 1,
    "status": "active",
    "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"
    },
    "next_payment_date": "2024-02-15T11:00:00Z",
    "started_at": "2024-01-15T11:00:00Z",
    "created_at": "2024-01-15T11:00:00Z",
    "updated_at": "2024-01-15T11:00:00Z"
  },
  "account_id": "acct_xyz789"
}

Event Data Fields

id
string
required
Unique identifier for the recurring plan (prefixed with plan_)
amount
integer
required
Recurring donation amount in cents (e.g., 2500 = $25.00)
currency
string
required
Three-letter ISO currency code (e.g., USD, CAD, EUR)
frequency
string
required
Payment frequency: weekly, monthly, quarterly, or yearly
interval
integer
required
Number of frequency periods between payments (e.g., 1 = every month, 2 = every 2 months)
status
string
required
Plan status: active, paused, cancelled, or expired
payment_method
string
required
Payment method for recurring charges: card, paypal, ach, or bank_transfer
campaign
object
required
Campaign the recurring plan supports
donor
object
required
Information about the recurring donor
next_payment_date
string
required
ISO 8601 timestamp when the next payment will be processed
started_at
string
required
ISO 8601 timestamp when the plan began
created_at
string
required
ISO 8601 timestamp when the plan was created
updated_at
string
required
ISO 8601 timestamp when the plan was last updated

Common Use Cases

Send special welcome messages to monthly donors:
async function handlePlanCreated(event) {
  const { donor, amount, frequency, campaign } = event.data;

  await sendEmail({
    to: donor.email,
    subject: `Thank you for joining our ${frequency} giving program!`,
    template: 'recurring-welcome',
    data: {
      first_name: donor.first_name,
      amount: amount / 100,
      frequency: frequency,
      campaign_name: campaign.title,
      next_payment: event.data.next_payment_date
    }
  });
}
Automatically segment recurring donors for special communications:
async function handlePlanCreated(event) {
  const { donor, frequency, amount } = event.data;

  // Add to CRM segment
  await crm.updateContact(donor.id, {
    donor_type: 'recurring',
    giving_frequency: frequency,
    monthly_gift_amount: frequency === 'monthly' ? amount / 100 : null,
    tags: ['monthly-donor', 'sustainer']
  });

  // Add to special email list
  await emailPlatform.addToList(donor.email, 'Monthly Donors Circle');
}
Project and track recurring donor lifetime value:
async function handlePlanCreated(event) {
  const { donor, amount, frequency } = event.data;

  // Calculate annual value
  const frequencyMultiplier = {
    weekly: 52,
    monthly: 12,
    quarterly: 4,
    yearly: 1
  };

  const annualValue = (amount / 100) * frequencyMultiplier[frequency];
  const projectedLifetimeValue = annualValue * 3; // Average 3-year retention

  await analytics.updateDonor(donor.id, {
    annual_recurring_value: annualValue,
    projected_ltv: projectedLifetimeValue,
    recurring_since: event.data.started_at
  });
}
Schedule reminders before upcoming payments:
async function handlePlanCreated(event) {
  const { id, donor, amount, next_payment_date } = event.data;

  // Schedule reminder 3 days before payment
  const reminderDate = new Date(next_payment_date);
  reminderDate.setDate(reminderDate.getDate() - 3);

  await scheduler.scheduleEmail({
    to: donor.email,
    template: 'payment-reminder',
    send_at: reminderDate,
    data: {
      first_name: donor.first_name,
      amount: amount / 100,
      payment_date: next_payment_date,
      plan_id: id
    }
  });
}
Alert fundraising team when recurring plans are created:
async function handlePlanCreated(event) {
  const { donor, amount, frequency, campaign } = event.data;

  await sendSlackNotification({
    channel: '#recurring-giving',
    message: `🎉 New ${frequency} donor!`,
    details: {
      donor: `${donor.first_name} ${donor.last_name}`,
      amount: `$${amount / 100}/${frequency}`,
      campaign: campaign.title,
      annual_value: calculateAnnualValue(amount, frequency)
    }
  });
}
Set up automated touchpoints for recurring donors:
async function handlePlanCreated(event) {
  const { id, donor, frequency } = event.data;

  await stewardship.createPlan({
    plan_id: id,
    donor_id: donor.id,
    touchpoints: [
      { type: 'welcome_email', delay: '0 days' },
      { type: 'impact_update', delay: '30 days' },
      { type: 'anniversary_thank_you', delay: '365 days' },
      { type: 'quarterly_report', frequency: 'quarterly' }
    ]
  });
}

Plan Frequencies

FrequencyDescription
weeklyCharges every week
monthlyCharges every month (most common)
quarterlyCharges every 3 months
yearlyCharges once per year

Plan Statuses

StatusDescription
activePlan is active and processing payments
pausedPlan temporarily paused by admin or donor
cancelledPlan has been cancelled
expiredPlan ended naturally (e.g., fixed duration)