API ReferenceDonations
Create Donation
Create a donation and initiate payment processing.
POST /api/donations
Creates a donation record, calculates fees, and returns a Stripe PaymentIntent client secret for completing the payment on the frontend.
The campaign must be in ACTIVE status. The org must have completed Stripe Connect onboarding before donations can be processed.
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
campaignId | string | Yes | The ID of the campaign to attribute this donation to | |
amount | integer | Yes | Donation amount in cents. Minimum 100 ($1.00) | |
currency | string | No | "usd" | Three-letter ISO currency code. Currently only "usd" supported |
frequency | string | Yes | One of: "one_time", "monthly", "quarterly", "annual" | |
paymentMethod | string | Yes | One of: "card", "ach", "apple_pay", "google_pay" | |
coverFees | boolean | Yes | Whether the donor is covering processing and platform fees | |
donor | object | Yes | Donor contact information (see below) | |
dedication | object | No | Optional tribute dedication (see below) | |
publicMessage | string | No | Public message shown on donor roll. Max 280 characters | |
emailOptIn | boolean | No | false | Whether donor opts in to future emails |
customFieldResponses | array | No | Responses to campaign custom fields | |
givingLevelId | string | No | ID of a pre-configured giving level | |
ticketCount | integer | No | For events: number of tickets (1-20) | |
utmSource | string | No | UTM source for attribution. Max 100 chars | |
utmMedium | string | No | UTM medium. Max 100 chars | |
utmCampaign | string | No | UTM campaign. Max 100 chars | |
referrerUrl | string | No | Referring page URL. Max 500 chars |
Donor Object
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
email | string | Yes | Donor email for receipt delivery and record matching | |
firstName | string | Yes | Donor first name | |
lastName | string | Yes | Donor last name | |
anonymous | boolean | No | false | Hide donor name from public donor roll |
Dedication Object
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | "in_honor" or "in_memory" |
name | string | Yes | Name of the person being honored. Max 200 chars |
message | string | No | Optional tribute message. Max 500 chars |
notifyName | string | No | Name of person to notify. Max 200 chars |
notifyEmail | string | No | Email to send tribute notification to |
Response
donation object
| Field | Type | Description |
|---|---|---|
id | string | Unique donation ID |
amountCents | integer | Donation amount in cents |
status | string | Always "PENDING" on creation |
frequency | string | "ONE_TIME", "MONTHLY", "QUARTERLY", or "ANNUAL" |
clientSecret (string)
The Stripe PaymentIntent client secret. Pass to Stripe Elements or confirmPayment() on the frontend.
fees object
| Field | Type | Description |
|---|---|---|
donationAmount | integer | Intended donation amount in cents |
processingFee | integer | Stripe processing fee in cents |
platformFee | integer | GiveLink platform fee in cents (1%, waived first $25K) |
totalCharged | integer | Total charged to donor in cents |
netToOrg | integer | Amount nonprofit receives in cents |
Example
curl -X POST https://givelink-api-production.up.railway.app/api/donations \
-H "Content-Type: application/json" \
-d '{
"campaignId": "clx9876543210",
"amount": 5000,
"currency": "usd",
"frequency": "monthly",
"paymentMethod": "card",
"coverFees": true,
"donor": {
"email": "jane@example.com",
"firstName": "Jane",
"lastName": "Smith",
"anonymous": false
},
"publicMessage": "Happy to support this cause!",
"emailOptIn": true,
"utmSource": "email",
"utmMedium": "newsletter",
"utmCampaign": "spring-2026"
}'{
"donation": {
"id": "clx1111111111",
"amountCents": 5000,
"status": "PENDING",
"frequency": "MONTHLY"
},
"clientSecret": "pi_1234_secret_5678",
"fees": {
"donationAmount": 5000,
"processingFee": 142,
"platformFee": 51,
"totalCharged": 5193,
"netToOrg": 5000
}
}Use the clientSecret with Stripe Elements on the frontend to collect payment details and confirm the PaymentIntent. The donation status remains PENDING until Stripe confirms payment via webhook.
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | Campaign not found | The campaignId does not exist |
| 400 | Campaign is not active | The campaign must be in ACTIVE status |
| 400 | Minimum donation is $X | Amount is below the campaign minimum |
| 400 | Organization has not completed payment setup | Stripe Connect onboarding is incomplete |
| 400 | Sorry, not enough tickets available | Event is sold out or insufficient capacity |
| 400 | Validation failed | One or more fields failed schema validation |
Last updated on 4/5/2026