Skip to main content
This is the canonical workflow page for the most distinctive operation in the Raise API: POST /api/Raise/give. It’s the only endpoint that creates Gifts. There is no POST /api/Gift — donations enter Raise exclusively through this path. This page walks through the full flow: building the request, what happens server-side, handling the response, and what events fire downstream. For the resource-level reference, see Gifts and Donors.

When to use this workflow

Use POST /api/Raise/give when:
  • A donor completes a donation form on a Raise-hosted or partner-hosted page.
  • A partner integration captures a donation through its own UI and submits to Raise for payment processing.
  • An integration replays a stored donation (e.g., re-attempting a previously-failed submission) — though the response will produce a new Gift, not modify an existing one.
Do not use this workflow for:
  • Importing historical gifts that have already been processed elsewhere. There’s no API for that — coordinate with the customer’s admin team for bulk imports.
  • Recording a manual gift entered by staff. That happens through the Raise admin UI.
  • Updating an existing Gift. There is no Gift PUT — see Gifts.

What POST /api/Raise/give actually does

The endpoint runs five operations as a single transaction: The five operations:
  1. Charge the payment method through the configured gateway using the tokenized paymentMethodId.
  2. Match or create the Donor from the embedded donor block (typically by email).
  3. Create the Gift record with the gateway response.
  4. Fire the giftCreate webhook to subscribers.
  5. Queue for sync to downstream products like CRM+.
The first three happen synchronously and contribute to the response. The webhook and sync happen as a side effect.

Building the request

Required fields

Only three fields are required:
FieldTypeDescription
amountnumberThe donation amount.
paymentMethodIdstringThe tokenized payment method.
paymentMethodTypeenumThe payment method type.
Everything else is optional and either has a sensible default or is supplemental context for downstream reporting.

A minimal request

cURL
curl -X POST https://prod-api.raisedonors.com/api/Raise/give \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50.00,
    "paymentMethodId": "tok_abc123",
    "paymentMethodType": "CreditCard"
  }'
This will succeed if the token is valid and the amount is positive, but it produces an anonymous gift with no donor information and the default Project allocation from the form context. For real donations, you almost always include the embedded donor block.

A realistic request

cURL
curl -X POST https://prod-api.raisedonors.com/api/Raise/give \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50.00,
    "currency": "USD",
    "paymentMethodId": "tok_abc123",
    "paymentMethodType": "CreditCard",
    "donor": {
      "firstName": "Bruce",
      "lastName": "Wayne",
      "email": "bruce@wayne.example",
      "phone": "(555) 555-0100"
    },
    "projects": [
      { "projectId": 12, "amount": 50.00 }
    ],
    "isAnonymous": false,
    "comments": "In support of the Gotham Outreach Program"
  }'

The full request body

The DonatePaymentRequest body has many optional fields beyond the basics. Group them by purpose:
GroupFields
Requiredamount, paymentMethodId, paymentMethodType
Currencycurrency, exchangeRate
Donordonor (embedded DonorRequest block)
Designationprojects[], projectOverrideCode, premiumId
Recurring setupisRecurring, frequency, startDate, startDateTimeUtc
Attributionsegment, segmentOverrideCode, motivationCodeId, motivationCodeGroupId, googleGclid, UTM fields
Fee handlingdonorPaidCosts, coverAdminFee, adminFee, adminFeeProjectId
Tributetribute
GatewaygatewayId, creditCardType, paypalPaymentSource, nonce
Anonymous / test modeisAnonymous, isTestMode
External linkagecrmKey (seed the CRM+ Contact ID on creation)
Form-flow trackingpublicId, pageRequestId, paymentId
Browser contextvisitorId, sessionId, clientId, timeZone
Employer matchdoubleTheDonationCompanyId, doubleTheDonationEnteredText
UK Gift AidgiftAidRequested
Commentscomments
For the field-by-field reference, see Gifts: The full DonatePaymentRequest body.

Step-by-step walkthrough

Step 1: obtain a payment method token

Raise uses tokenization to keep partner integrations out of PCI scope. Your integration never sees a raw card number — instead, the donor’s payment information is exchanged for an opaque paymentMethodId token that you submit to POST /api/Raise/give. For development and testing, use the dedicated test-payment-method generator:
cURL
curl -X POST https://prod-api.raisedonors.com/api/Raise/generate-test-payment-method \
  -H "Authorization: Bearer YOUR_API_TOKEN"
The response contains a paymentMethodId you can submit to /api/Raise/give in test mode (isTestMode: true) without touching real card data. For production, the customer’s payment gateway provides tokenization through its hosted fields, drop-in UI, or client-side SDK. The donor’s card data is captured by the gateway’s tools, tokenized client-side, and the resulting paymentMethodId flows through your integration to POST /api/Raise/give.
Never accept raw card numbers in your integration’s API or store them server-side. Doing so puts your integration in PCI DSS scope. Use the gateway’s tokenization tooling for the donor-facing capture, and submit only the resulting token to Raise.

Step 2: prepare the donor block

The donor field on the request is an embedded block that Raise uses for matching or creating the Donor record. Include the fields you have:
{
  "donor": {
    "firstName": "Bruce",
    "lastName": "Wayne",
    "email": "bruce@wayne.example",
    "phone": "(555) 555-0100",
    "billingAddress": {
      "address1": "1007 Mountain Drive",
      "city": "Gotham",
      "state": "NJ",
      "postalCode": "10001",
      "country": "US"
    }
  }
}
Email is the most important field for donor matching. Raise’s matching algorithm typically uses email as the primary key — if the email matches an existing Donor, the gift is attached to that Donor rather than creating a new one. If the email doesn’t match, a new Donor is created with the embedded block’s data. If you already have a Raise donor ID (e.g., from a previous gift), you can include it explicitly to skip the matching step — see Create or Find a Donor.

Step 3: prepare the project allocation

The projects[] array specifies how the donation’s amount should be designated across one or more Projects. Each entry has a Project identifier and an amount:
{
  "projects": [
    { "projectId": 12, "amount": 50.00 }
  ]
}
For a split gift:
{
  "projects": [
    { "projectId": 12, "amount": 30.00 },
    { "projectId": 47, "amount": 20.00 }
  ]
}
The sum of all entries must equal the gift’s amount. If you omit projects[] entirely, the gift designates to the default Project configured on the Form (or the Campaign, depending on configuration).

Step 4: submit the request

JavaScript
async function processDonation({ amount, paymentMethodId, paymentMethodType, donor, projects, isTestMode }) {
  const response = await fetch(
    'https://prod-api.raisedonors.com/api/Raise/give',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RAISE_API_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        amount,
        paymentMethodId,
        paymentMethodType,
        currency: 'USD',
        donor,
        projects,
        isTestMode: isTestMode ?? false,
      }),
    }
  );

  if (!response.ok) {
    const problem = await response.json();
    throw new DonationError(problem.detail || problem.title, response.status, problem);
  }

  return response.json();
}

Step 5: handle the response

A successful donation returns 200 OK with the created Gift record. The most important fields:
FieldWhy it matters
idThe Gift’s Raise primary key. Store this for reconciliation and audit.
donorIdThe Donor that the gift attached to (existing or newly created).
status / statusTextThe Gift’s processing state — see Statuses and Lifecycle States.
transactionIdThe payment gateway’s transaction reference.
authorizationNumberThe gateway authorization.
canRefundWhether the Gift can be refunded through POST /api/Gift/{id}/refund.
recurringGiftIdIf isRecurring: true was set in the request, the ID of the newly-created RecurringGift schedule.
JavaScript
const gift = await processDonation({ /* ... */ });

console.log('Donation processed:', {
  giftId: gift.id,
  donorId: gift.donorId,
  statusText: gift.statusText,
  amount: gift.formattedAmount,
  transactionId: gift.transactionId,
});

Handling errors

The most common error categories and how to respond:

400: validation failure

The request body is syntactically valid but failed validation — missing required field, invalid combination, etc. The response includes the errors map identifying which fields failed:
{
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "amount": ["The amount field must be greater than zero."],
    "paymentMethodType": ["The paymentMethodType field is required."]
  }
}
Don’t retry the same request — fix the body and re-submit. Surface the field-level errors to your integration’s UI where applicable.

400: payment processing failure

A 400 can also indicate that the payment gateway rejected the charge — card declined, insufficient funds, expired card. The detail or errors map will identify the specific reason. The Gift record was not created in this case. Don’t retry the same payment method — the result will be the same. The right path is to surface the failure to the donor and let them try a different method (or contact their card issuer).

401: unauthorized

The token is invalid, expired, or revoked. See Authentication: errors. Don’t retry; fix the credential.

429: rate limited

Too many requests in too short a time. Honor Retry-After and back off — see Rate Limits. Retries should not multiply the customer’s charge — see “Idempotency considerations” below.

500 / 502 / 503: server errors

Transient. Retry with exponential backoff. But see “Idempotency considerations” below — a request that the server received but couldn’t acknowledge may have created a Gift even though your code raised an error.

Idempotency considerations

POST /api/Raise/give charges a payment method. A naive retry on a network error or transient failure can result in double charges if the original request actually succeeded but the response didn’t reach you. The Raise spec does not currently document an idempotency-key header (like Stripe’s Idempotency-Key). Until one is published, partner integrations should defend against duplicate submissions client-side:
DefenseDescription
Pre-submission deduplicationGenerate a unique key per intended donation (e.g., a UUID stored before submission). On retry, check whether your local record already shows the donation as submitted.
Webhook reconciliationSubscribe to giftCreate webhooks. On a retry where the original may have succeeded, check whether a Gift with your tracking identifier already exists before re-submitting.
Limited retriesCap retries strictly — 2 attempts at most for POST /api/Raise/give. Persistent failures should surface for human review rather than continue retrying.
Test mode for validationWhen uncertain whether to retry, switch to isTestMode: true for a validation submission to confirm the request shape works before retrying production.
Never retry POST /api/Raise/give automatically on a network error without one of the defenses above. A naive retry produces double charges. The cost of failing to retry is one missed donation; the cost of duplicating a charge is a customer service incident.⚠️ Human input required: Confirm with the platform team whether POST /api/Raise/give supports an idempotency-key header. If so, document it here; if not, this defensive guidance is the recommended approach.

Webhook events

A successful POST /api/Raise/give triggers a giftCreate event to subscribers. Partner integrations that need to react to donations beyond the immediate response should subscribe rather than poll:
JavaScript
// Webhook handler example
async function handleGiftCreate(event) {
  const gift = event.payload;
  await emailService.sendThankYou(gift.donor.email, {
    amount: gift.formattedAmount,
    project: gift.projects[0]?.projectName,
  });
}
If the donation includes isRecurring: true, a corresponding event for the RecurringGift schedule also fires. See Event Types for the full event list.

Test-mode flow for development

For end-to-end testing without real charges:
JavaScript
// 1. Generate a test payment method
const tokenResponse = await fetch(
  'https://prod-api.raisedonors.com/api/Raise/generate-test-payment-method',
  {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.RAISE_API_TOKEN}` },
  }
);
const testToken = await tokenResponse.json();

// 2. Submit a test donation
const giftResponse = await fetch(
  'https://prod-api.raisedonors.com/api/Raise/give',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.RAISE_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amount: 10.00,
      paymentMethodId: testToken.paymentMethodId,
      paymentMethodType: 'CreditCard',
      isTestMode: true,
      donor: {
        firstName: 'Test',
        lastName: 'Donor',
        email: 'test@example.com',
      },
    }),
  }
);
const gift = await giftResponse.json();
console.log('Test donation processed:', gift);
The resulting Gift carries isTestMode: true and won’t appear in production-filtered reports. See Base URLs and Environments: Use the test payment method generator.

Where to go next

Create or Find a Donor

The workflow for ensuring a donor exists before submitting a donation — useful when matching needs to happen explicitly before the donation step.

Configure a Recurring Gift

Use the same POST /api/Raise/give path to set up a recurring schedule.

Handle Failed Payments

The full failure-handling workflow including recurring payment failures.

Webhooks Overview

React to donations in real time through giftCreate events.
Last modified on May 20, 2026