Skip to main content

Set up and use Double webhooks

Learn how to configure webhooks, review the available events, and understand the JSON payloads Double sends to your endpoint.

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

data.id represents

donation.created

A payment record is created

A payment or charge

donation.updated

A payment status changes

A payment or charge

recurring.created

A recurring donation plan is successfully created

A recurring plan

recurring.updated

A recurring donation plan changes

A recurring plan

orgDonor.created

A donor is first created for your organization

An organization donor

fundraiser.created

A fundraiser is created or a fundraiser signup is submitted

A fundraiser, when available

fundraiser.updated

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

  1. In the Double dashboard, go to Integrations.

  2. Select Webhook.

  3. Enter the URL that should receive webhook requests.

  4. Select the events you want to receive.

  5. 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

object

string

Always event.

type

string

The webhook event name.

data

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

id

UUID

Payment or charge ID.

number

integer

Organization-facing payment number.

chargeDate

string

Human-formatted payment date with timezone.

rawChargeDate

datetime

ISO 8601 payment date.

status

string

Current payment status.

doubleUrl

string

Payment page in the Double dashboard.

stripeUrl

string/null

Legacy alias of gatewayUrl.

gatewayUrl

string/null

Payment in the processor dashboard, when available.

gatewayId

string/null

Processor payment or invoice ID.

amount

decimal

Amount currently received. It can change after a refund.

expectedAmount

decimal/null

Amount Double attempted to charge.

amountFormatted

string

Formatted value of amount.

payoutAmount

decimal/null

Net amount in the organization’s default currency.

feeAmount

decimal/null

Processing fee amount.

currency

string

Uppercase three-letter payment currency.

transaction

object

Transaction amount, fee, payout, and processor currency.

donation

object

Parent donation or recurring plan.

donor

object

Donor snapshot.

fundraiser

object/null

Associated fundraiser, when present.

prefilledFields

object/null

Original prefilled checkout values, when captured.

Possible payment statuses are:

  • initial

  • pending

  • error

  • success

  • disputed

  • partiallyRefunded

  • refunded

  • offline

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

id

UUID

Parent donation or recurring plan ID.

stripeUrl

string/null

Legacy alias of gatewayUrl.

gatewayUrl

string/null

Processor subscription or payment URL.

doubleUrl

string

Donation page in the Double dashboard.

number

integer

Organization-facing donation number.

status

string

Donation lifecycle status.

category

string

Legacy alias of designation.

designation

string

Designation name; defaults to General.

designationExternalId

string/null

Designation ID in an external system.

donationAmount

decimal

Base donation amount before covered fees.

donationAmountFormatted

string

Formatted base donation amount.

transactionAmount

decimal

Legacy alias of totalAmount.

transactionAmountFormatted

string

Legacy alias of totalAmountFormatted.

totalAmount

decimal

Donation amount including donor-covered fees.

totalAmountFormatted

string

Formatted total amount.

currency

string

Uppercase donation currency.

payCoverFees

boolean

Whether the donor chose to cover fees.

coverFeesAmount

decimal/null

Donor-covered fee amount.

companyName

string/null

Donor company name.

comment

string/null

Donor comment.

pageUrl

string/omitted

Page associated with the donation, when available.

utm

object

source, medium, campaign, term, and content. Each can be null.

campaignExternalId

string

Legacy campaign external ID; defaults to n/a.

campaign

object

Campaign name, external ID, goal, donor count, and amount-raised snapshot.

url

string/omitted

Legacy donation source.

dateCreated

datetime

Donation creation time.

dateCreatedFormatted

string

Human-formatted creation time.

recurring

object/null

Recurring details, or null for a one-time donation.

solicitor

object/null

Solicitor ID, slug, and name.

paymentMethod

object/null

Processor-dependent payment method summary.

dedication

object/null

Dedication details.

customQuestion

object/null

Legacy first custom question.

customQuestions

object/null

All custom questions, keyed by "1", "2", and so on.

hubspotId

string/null

HubSpot ID when the donation has been synchronized.

amountDescription

string

Freeform amount description; defaults to an empty string.

When donation.recurring is present, it contains:

Field

Type

Description

frequency

string

daily, weekly, monthly, or yearly.

chargeNumber

integer

This payment’s position in the recurring series.

endDate

datetime/null

Configured plan end date.

expectedCharges

integer/null

Planned number of charges; null can mean there is no fixed limit.

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:

  • id

  • anonymous

  • fullName

  • firstName

  • lastName

  • phoneNumber

  • email

  • address

  • doubleUrl

  • hubspotId, 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 use exp_month and exp_year instead of expMonth and expYear.

  • DAF payments through Chariot include type: "daf" and orgName.

  • 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

id

UUID

Recurring donation plan ID.

doubleUrl

string

Plan page in the Double dashboard.

campaign

string

Campaign name; defaults to n/a.

campaignExternalId

string

Campaign external ID; defaults to n/a.

stripeUrl

string/null

Legacy alias of gatewayUrl.

gatewayUrl

string/null

Plan in the processor dashboard.

number

integer

Organization-facing donation number.

frequency

string

daily, weekly, monthly, or yearly.

endDate

datetime/null

Configured plan end date.

expectedCharges

integer/null

Planned charge count; null can mean there is no fixed limit.

category

string

Legacy designation field.

designation

string

Designation name; defaults to General.

donationAmount

decimal

Base amount for each charge.

donationAmountFormatted

string

Formatted base amount.

transactionAmount

decimal

Amount per charge, including covered fees.

transactionAmountFormatted

string

Formatted transaction amount.

currency

string

Uppercase currency; defaults to USD if unavailable.

payCoverFees

boolean

Whether the donor covers fees.

coverFeesAmount

decimal/null

Covered fee amount per charge.

status

string

Current plan lifecycle status.

companyName

string/null

Donor company name.

comment

string/null

Donor comment.

pageUrl

string/omitted

Page associated with the plan, when available.

url

string/omitted

Legacy donation source.

dateCreated

datetime

Plan creation time.

dateCreatedFormatted

string

Human-formatted creation time.

donor

object

Donor and billing-address snapshot.

dedication

object/null

Dedication details.

customQuestion

object/null

Legacy first custom question.

customQuestions

object/null

All custom questions, keyed by "1", "2", and so on.

Possible lifecycle statuses are:

  • An empty string

  • paymentMethodError

  • paymentMethodSuccess

  • subscriptionError

  • subscriptionSuccess

  • 1stChargeError

  • 1stChargeSuccess

  • inProgressError

  • endedCanceled

  • endedError

  • endedSuccess

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

id

UUID

Organization donor ID.

number

integer

Organization-facing donor number.

firstName

string

First name; defaults to an empty string.

lastName

string

Last name; defaults to an empty string.

fullName

string

First and last name joined with a space.

phoneNumber

string

Phone number; defaults to n/a.

address

object/null

Current billing address stored on the donor.

email

string

Email address; defaults to n/a.

doubleUrl

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

id

UUID/omitted

Persistent fundraiser ID. It can be omitted for a signup.

userId

UUID/omitted

Legacy user identifier; on newer records it can match the fundraiser ID.

orgId

UUID/omitted

Organization ID.

approved

boolean

Whether the fundraiser is approved.

goalAmount

decimal/null

Fundraiser goal.

goalAmountFormatted

string

Goal formatted in the organization’s currency.

fundraiserLink

string

Public fundraiser URL, or an empty string.

fundraiserMessage

string

Fundraiser message, or an empty string.

nickname

string/omitted

Public nickname.

fullName

string

Nickname when present; otherwise, the person’s full name.

firstName

string

First name.

lastName

string

Last name.

email

string

Email address.

dateCreated

datetime

Creation or signup time.

dateCreatedFormatted

string

Human-formatted creation time.

dateApproved

datetime/omitted

Approval time.

dateApprovedFormatted

string

Human-formatted approval time, or n/a.

amountRaised

decimal

Current successful-charge total; defaults to 0.

amountRaisedFormatted

string

Formatted amount raised.

donations

number

Current donation count; defaults to 0.

donors

number

Current distinct donor count; defaults to 0.

lastActivity

datetime/omitted

Latest fundraising activity.

lastActivityFormatted

string

Human-formatted activity time, or n/a.

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.

Did this answer your question?