Double webhooks send real-time notifications to your application when donations, recurring donation plans, donors, or fundraisers are created or updated.
You can use them to connect Double with a CRM, data warehouse, automation platform, or custom application.
For developers and technical partners: Use this reference to configure Double webhooks and understand their events and JSON payloads.
Available webhook events
Event | Sent when |
|
| A payment record is created | A payment or charge |
| A payment status changes | A payment or charge |
| A recurring donation plan is successfully created | A recurring plan |
| A recurring donation plan changes | A recurring plan |
| A donor is first created for your organization | An organization donor |
| A fundraiser is created or a fundraiser signup is submitted | A fundraiser, when available |
| A fundraiser changes or receives a successful donation | A fundraiser |
Event names are case-sensitive. The camel-case spelling of orgDonor.created is intentional.
Donation events and recurring events represent different records. A recurring gift normally creates a recurring plan and individual payments. Use recurring.* events to synchronize the plan, and donation.* events to synchronize each charge generated by that plan.
Set up a webhook
In the Double dashboard, go to Integrations.
Select Webhook.
Enter the URL that should receive webhook requests.
Select the events you want to receive.
Save the integration.
Double uses one configured webhook endpoint per organization and sends all selected events to that URL.
Request format
Double sends an HTTP POST request with an application/json body. Every webhook uses the same top-level structure:
{
"object": "event",
"type": "donation.created",
"data": {
"id": "01900000-0000-0000-0000-000000000101"
}
}Field | Type | Description |
| string | Always |
| string | The webhook event name. |
| object | A snapshot of the record related to the event. Its structure depends on the event type. |
Unless a field is specifically described as a formatted display value, dates are sent as ISO 8601 strings. Optional values may be null, an empty string, or omitted.
Working with monetary values: Fields such as amountFormatted are for display only. Use the corresponding amount field for calculations. Amounts may be sent as JSON numbers or decimal strings, so parse and store them with decimal-safe code.
Donation events
donation.created and donation.updated represent individual payments or charges.
donation.created
Sent after Double creates a payment record. A created event does not necessarily mean the payment succeeded. Always check data.status, which may initially be success, pending, or error.
donation.updated
Sent when the status of an existing payment changes - for example, when a pending payment succeeds or fails, a payment is disputed, or a partial or full refund is issued.
Use data.id as the payment’s stable external ID and treat each event as the latest snapshot of that payment.
Example donation payload
{
"object": "event",
"type": "donation.created",
"data": {
"id": "01900000-0000-0000-0000-000000000101",
"number": 1427,
"chargeDate": "2026 July 16th at 3:30:00 pm +03:00",
"rawChargeDate": "2026-07-16T12:30:00.000Z",
"status": "success",
"doubleUrl": "https://dashboard.double.giving/#/charges/01900000-0000-0000-0000-000000000101",
"gatewayId": "pi_example",
"amount": 52.1,
"expectedAmount": 52.1,
"amountFormatted": "$52.1",
"payoutAmount": 50.29,
"feeAmount": 1.81,
"currency": "USD",
"transaction": {
"amount": 52.1,
"fee": 1.81,
"payout": 50.29,
"currency": "USD"
},
"donation": {
"id": "01900000-0000-0000-0000-000000000102",
"number": 892,
"status": "1stChargeSuccess",
"designation": "General",
"designationExternalId": null,
"donationAmount": 50,
"totalAmount": 52.1,
"currency": "USD",
"payCoverFees": true,
"coverFeesAmount": 2.1,
"companyName": null,
"comment": "Thank you",
"utm": {
"source": "newsletter",
"medium": "email",
"campaign": "summer",
"term": null,
"content": null
},
"campaign": {
"name": "Summer Campaign",
"externalId": "summer-2026",
"donors": 153,
"goal": 25000,
"goalFormatted": "$25,000",
"raised": 18750,
"raisedFormatted": "$18,750"
},
"dateCreated": "2026-07-16T12:29:55.000Z",
"recurring": {
"frequency": "monthly",
"chargeNumber": 1,
"endDate": null,
"expectedCharges": 12
},
"solicitor": null,
"paymentMethod": {
"type": "card",
"brand": "visa",
"last4": "4242",
"expYear": 2030,
"expMonth": 12,
"country": "US",
"bank": "",
"routingNumber": ""
},
"dedication": null,
"customQuestion": null,
"customQuestions": null,
"amountDescription": ""
},
"donor": {
"id": "01900000-0000-0000-0000-000000000103",
"anonymous": false,
"fullName": "Ada Lovelace",
"firstName": "Ada",
"lastName": "Lovelace",
"phoneNumber": "+12025550123",
"email": "[email protected]",
"address": {
"line1": "123 Main St",
"line2": null,
"city": "Washington",
"state": "DC",
"zip": "20001",
"country": "US"
},
"doubleUrl": "https://dashboard.double.giving/#/donors/01900000-0000-0000-0000-000000000103"
},
"fundraiser": null,
"prefilledFields": null
}
}
Payment fields
These fields are included inside data.
Field | Type | Description |
| UUID | Payment or charge ID. |
| integer | Organization-facing payment number. |
| string | Human-formatted payment date with timezone. |
| datetime | ISO 8601 payment date. |
| string | Current payment status. |
| string | Payment page in the Double dashboard. |
| string/null | Legacy alias of |
| string/null | Payment in the processor dashboard, when available. |
| string/null | Processor payment or invoice ID. |
| decimal | Amount currently received. It can change after a refund. |
| decimal/null | Amount Double attempted to charge. |
| string | Formatted value of |
| decimal/null | Net amount in the organization’s default currency. |
| decimal/null | Processing fee amount. |
| string | Uppercase three-letter payment currency. |
| object | Transaction amount, fee, payout, and processor currency. |
| object | Parent donation or recurring plan. |
| object | Donor snapshot. |
| object/null | Associated fundraiser, when present. |
| object/null | Original prefilled checkout values, when captured. |
Possible payment statuses are:
initialpendingerrorsuccessdisputedpartiallyRefundedrefundedoffline
The nested transaction object contains amount, fee, payout, and currency. These values may be unavailable for some processors.
Parent donation fields
These fields are included inside data.donation.
Field | Type | Description |
| UUID | Parent donation or recurring plan ID. |
| string/null | Legacy alias of |
| string/null | Processor subscription or payment URL. |
| string | Donation page in the Double dashboard. |
| integer | Organization-facing donation number. |
| string | Donation lifecycle status. |
| string | Legacy alias of |
| string | Designation name; defaults to |
| string/null | Designation ID in an external system. |
| decimal | Base donation amount before covered fees. |
| string | Formatted base donation amount. |
| decimal | Legacy alias of |
| string | Legacy alias of |
| decimal | Donation amount including donor-covered fees. |
| string | Formatted total amount. |
| string | Uppercase donation currency. |
| boolean | Whether the donor chose to cover fees. |
| decimal/null | Donor-covered fee amount. |
| string/null | Donor company name. |
| string/null | Donor comment. |
| string/omitted | Page associated with the donation, when available. |
| object |
|
| string | Legacy campaign external ID; defaults to |
| object | Campaign name, external ID, goal, donor count, and amount-raised snapshot. |
| string/omitted | Legacy donation source. |
| datetime | Donation creation time. |
| string | Human-formatted creation time. |
| object/null | Recurring details, or |
| object/null | Solicitor ID, slug, and name. |
| object/null | Processor-dependent payment method summary. |
| object/null | Dedication details. |
| object/null | Legacy first custom question. |
| object/null | All custom questions, keyed by |
| string/null | HubSpot ID when the donation has been synchronized. |
| string | Freeform amount description; defaults to an empty string. |
When donation.recurring is present, it contains:
Field | Type | Description |
| string |
|
| integer | This payment’s position in the recurring series. |
| datetime/null | Configured plan end date. |
| integer/null | Planned number of charges; |
Campaign and fundraiser totals are current snapshots. Replace the totals stored in your external system instead of adding the webhook values to earlier totals.
Donor fields within a donation
The nested data.donor object includes:
idanonymousfullNamefirstNamelastNamephoneNumberemailaddressdoubleUrlhubspotId, when available
Some payloads may also include legacy flattened address fields such as line1, line2, city, state, zip, and country.
The address object normally contains line1, line2, city, state, zip, and country. Partial addresses are possible.
Payment method, custom questions, and dedications
The structure of donation.paymentMethod depends on the processor and payment method:
Card payments normally include fields such as
type,brand,last4, expiration month and year, country, bank, and routing number. Some processors useexp_monthandexp_yearinstead ofexpMonthandexpYear.DAF payments through Chariot include
type: "daf"andorgName.PayPal payments include
type: "paypal".
The payment method summary does not include full payment credentials.
Each custom question contains question and answer. An answer may be text, a selected value, or a public file URL, depending on the question type.
A dedication, when present, has the following structure:
{
"from": "Ada Lovelace",
"message": "In honor of...",
"dedicatee": {
"email": "[email protected]"
}
}
Recurring donation events
recurring.created and recurring.updated represent a recurring donation plan, not an individual payment.
recurring.created
Sent when Double successfully establishes a recurring donation plan. A one-time donation does not produce this event. The first payment may separately produce a donation.created event.
recurring.updated
Sent when the recurring plan changes. This can include payment-method or subscription setup, an in-progress payment error, cancellation, or successful or unsuccessful completion.
Always inspect data.status instead of assuming a specific lifecycle transition.
Example recurring payload
{
"object": "event",
"type": "recurring.created",
"data": {
"id": "01900000-0000-0000-0000-000000000201",
"doubleUrl": "https://dashboard.double.giving/#/donations/01900000-0000-0000-0000-000000000201",
"campaign": "Annual Campaign",
"campaignExternalId": "annual-2026",
"gatewayUrl": "https://dashboard.stripe.com/subscriptions/sub_example",
"number": 712,
"frequency": "monthly",
"endDate": null,
"expectedCharges": 12,
"designation": "General",
"donationAmount": 25,
"donationAmountFormatted": "$25",
"transactionAmount": 26.06,
"transactionAmountFormatted": "$26.06",
"currency": "USD",
"payCoverFees": true,
"coverFeesAmount": 1.06,
"status": "1stChargeSuccess",
"companyName": null,
"comment": null,
"dateCreated": "2026-07-16T12:30:00.000Z",
"donor": {
"id": "01900000-0000-0000-0000-000000000202",
"anonymous": false,
"fullName": "Grace Hopper",
"firstName": "Grace",
"lastName": "Hopper",
"phoneNumber": "+12025550124",
"email": "[email protected]",
"address": {
"line1": "456 Oak Ave",
"city": "Arlington",
"state": "VA",
"zip": "22201",
"country": "US"
}
},
"dedication": null,
"customQuestion": {
"question": "How did you hear about us?",
"answer": "A friend"
},
"customQuestions": {
"1": {
"question": "How did you hear about us?",
"answer": "A friend"
}
}
}
}
Recurring plan fields
Field | Type | Description |
| UUID | Recurring donation plan ID. |
| string | Plan page in the Double dashboard. |
| string | Campaign name; defaults to |
| string | Campaign external ID; defaults to |
| string/null | Legacy alias of |
| string/null | Plan in the processor dashboard. |
| integer | Organization-facing donation number. |
| string |
|
| datetime/null | Configured plan end date. |
| integer/null | Planned charge count; |
| string | Legacy designation field. |
| string | Designation name; defaults to |
| decimal | Base amount for each charge. |
| string | Formatted base amount. |
| decimal | Amount per charge, including covered fees. |
| string | Formatted transaction amount. |
| string | Uppercase currency; defaults to |
| boolean | Whether the donor covers fees. |
| decimal/null | Covered fee amount per charge. |
| string | Current plan lifecycle status. |
| string/null | Donor company name. |
| string/null | Donor comment. |
| string/omitted | Page associated with the plan, when available. |
| string/omitted | Legacy donation source. |
| datetime | Plan creation time. |
| string | Human-formatted creation time. |
| object | Donor and billing-address snapshot. |
| object/null | Dedication details. |
| object/null | Legacy first custom question. |
| object/null | All custom questions, keyed by |
Possible lifecycle statuses are:
An empty string
paymentMethodErrorpaymentMethodSuccesssubscriptionErrorsubscriptionSuccess1stChargeError1stChargeSuccessinProgressErrorendedCanceledendedErrorendedSuccess
Donor events
orgDonor.created is sent when Double first creates a donor record for your organization. A returning donor matched to an existing record does not produce another created event.
The same person may have different donor IDs in different Double organizations. Use data.id as the stable donor ID within your organization.
Example donor payload
{
"object": "event",
"type": "orgDonor.created",
"data": {
"id": "01900000-0000-0000-0000-000000000301",
"number": 328,
"firstName": "Katherine",
"lastName": "Johnson",
"fullName": "Katherine Johnson",
"phoneNumber": "+12025550125",
"address": {
"line1": "789 Pine St",
"line2": null,
"city": "Hampton",
"state": "VA",
"zip": "23669",
"country": "US"
},
"email": "[email protected]",
"doubleUrl": "https://dashboard.double.giving/#/donors/01900000-0000-0000-0000-000000000301"
}
}
Donor fields
Field | Type | Description |
| UUID | Organization donor ID. |
| integer | Organization-facing donor number. |
| string | First name; defaults to an empty string. |
| string | Last name; defaults to an empty string. |
| string | First and last name joined with a space. |
| string | Phone number; defaults to |
| object/null | Current billing address stored on the donor. |
| string | Email address; defaults to |
| string | Donor page in the Double dashboard. |
The address normally contains line1, line2, city, state, zip, and country. Contact and address fields may be missing, and fallback values such as n/a do not mean that the information was verified.
Fundraiser events
fundraiser.created and fundraiser.updated contain fundraiser identity, approval details, public messaging, goals, and aggregate performance.
fundraiser.created
Sent when a fundraiser is created or when a public fundraiser signup is submitted. A signup may be sent before the permanent fundraiser record has an ID, public slug, approval date, or statistics, so the payload can be partial.
fundraiser.updated
Sent when a fundraiser changes. Double also sends this event after a successful donation associated with the fundraiser so that external systems can refresh the fundraiser’s aggregate totals.
Example fundraiser payload
{
"object": "event",
"type": "fundraiser.updated",
"data": {
"id": "01900000-0000-0000-0000-000000000401",
"userId": "01900000-0000-0000-0000-000000000402",
"orgId": "01900000-0000-0000-0000-000000000403",
"approved": true,
"goalAmount": 5000,
"goalAmountFormatted": "$5,000",
"fundraiserLink": "https://nonprofit.example.org?fundraiser=dorothy-vaughan",
"fundraiserMessage": "Help me reach this goal.",
"nickname": "Dorothy's Fundraiser",
"fullName": "Dorothy's Fundraiser",
"firstName": "Dorothy",
"lastName": "Vaughan",
"email": "[email protected]",
"dateCreated": "2026-07-16T12:30:00.000Z",
"dateApproved": "2026-07-16T12:35:00.000Z",
"amountRaised": 1250,
"amountRaisedFormatted": "$1,250",
"donations": 18,
"donors": 16,
"lastActivity": "2026-07-16T13:00:00.000Z"
}
}
Fundraiser fields
Field | Type | Description |
| UUID/omitted | Persistent fundraiser ID. It can be omitted for a signup. |
| UUID/omitted | Legacy user identifier; on newer records it can match the fundraiser ID. |
| UUID/omitted | Organization ID. |
| boolean | Whether the fundraiser is approved. |
| decimal/null | Fundraiser goal. |
| string | Goal formatted in the organization’s currency. |
| string | Public fundraiser URL, or an empty string. |
| string | Fundraiser message, or an empty string. |
| string/omitted | Public nickname. |
| string | Nickname when present; otherwise, the person’s full name. |
| string | First name. |
| string | Last name. |
| string | Email address. |
| datetime | Creation or signup time. |
| string | Human-formatted creation time. |
| datetime/omitted | Approval time. |
| string | Human-formatted approval time, or |
| decimal | Current successful-charge total; defaults to |
| string | Formatted amount raised. |
| number | Current donation count; defaults to |
| number | Current distinct donor count; defaults to |
| datetime/omitted | Latest fundraising activity. |
| string | Human-formatted activity time, or |
amountRaised, donations, and donors are current totals. Replace the values stored in your system instead of incrementing them from each webhook.
If a signup payload does not include data.id, store it as a provisional record using your own derived key. Once a later payload includes the fundraiser ID, use that ID as Double’s stable external key. Do not automatically merge fundraisers based only on matching email addresses.
