> ## Documentation Index
> Fetch the complete documentation index at: https://docs.virtuous.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure a Donation Form

> What partner integrations can and can't do around Donation Form configuration in Raise — and the partner-side preparation work that supports a donation form's lifecycle.

Donation Form configuration in Raise — the form's fields, styling, designations, embed settings — happens in the Raise admin UI, not via the API. This page covers the practical workflow for partner integrations that need to support a customer's donation forms: what's possible through the API, what the customer's admin team handles, and the partner-side preparation that makes the integration work well around the forms once they're configured.

The audience is partner integration teams that have a customer with a Raise account and need to wire up the integration's behavior to react to that customer's forms.

<Warning>
  **There is no API for creating, updating, or listing Donation Forms.** The Raise OpenAPI spec doesn't expose a `/api/Form` or `/api/Page` resource. Form configuration happens entirely in the Raise admin UI.

  This workflow is about *partner-side preparation* — the steps your integration takes to react to forms that the customer's admin team has already configured. For the resource-level reference of what the API does expose around forms, see [Donation Forms](/raise/concepts/donation-forms).
</Warning>

## When to use this workflow

| Scenario                                                                            | Approach                                                                                |
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| A customer wants the partner integration to react to donations from a specific form | Use this workflow to set up the partner-side wiring.                                    |
| A customer needs a new donation form created                                        | Coordinate with the customer's Raise admin team — they configure forms in the admin UI. |
| A customer wants to embed a form on their website                                   | See [Embed a Form](/raise/workflows/embed-a-form).                                      |
| A customer wants the partner integration to manage forms programmatically           | Not currently supported. Coordinate with the platform team for v2 roadmap visibility.   |

***

## What the customer's admin team handles

For context, here's what's involved on the customer's side before your integration's work begins. This is admin-UI work, not API work — partners typically don't perform these steps.

| Step                                                | Where it happens |
| --------------------------------------------------- | ---------------- |
| Create the Donation Form record                     | Raise admin UI   |
| Configure form fields and validation                | Raise admin UI   |
| Set designations (which Projects donations support) | Raise admin UI   |
| Configure premium offers and tribute options        | Raise admin UI   |
| Apply branding and styling                          | Raise admin UI   |
| Attach the form to a Campaign and Segment           | Raise admin UI   |
| Publish the form and obtain the embed code          | Raise admin UI   |

When this is done, the customer has a published form, a public landing URL, and (typically) an embed code they can hand to your integration.

***

## Partner-side preparation: the four key steps

Once the form is published, your integration has four preparation steps before it can react meaningfully to donations from the form.

### Step 1: capture the form's identifier

Every donation submitted through a form attaches a `form` block to the resulting Gift record. The `form.id` is the form's stable identifier — use it to filter, group, and route gifts by source form.

```javascript JavaScript theme={null}
// On a giftCreate webhook event, capture the form context
async function handleGiftCreate(event) {
  const gift = event.payload;
  const formContext = {
    formId: gift.form?.id,
    formTitle: gift.form?.title,
    landingUrl: gift.form?.landingUrl,
    referringUrl: gift.form?.referringUrl,
    isLegacy: gift.form?.isLegacy,
  };

  await ingestGiftWithFormContext(gift, formContext);
}
```

For partner integrations that support multiple forms per customer, store the list of form IDs the customer has activated and use this list to filter incoming webhook events. This avoids cross-pollination between forms when one customer runs multiple distinct campaigns.

<Note>
  Form IDs are the only stable identifier you have for a form. The form's `title` can change without affecting the ID — but the title is what end users see, so it's the friendlier identifier for UI display. Use `id` for routing and `title` for display.
</Note>

### Step 2: subscribe to relevant webhook events

The forms produce events when donations complete. Subscribe to the events your integration needs:

```bash cURL theme={null}
curl -X POST https://prod-api.raisedonors.com/api/Webhook \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Partner X gift integration",
    "notificationUrl": "https://partner.example.com/raise-webhooks",
    "eventTypesList": [10, 11, 20, 21],
    "format": 1,
    "status": 1,
    "securityToken": "your-shared-secret"
  }'
```

The `eventTypesList` is an array of event type integers from the spec's documented enum (`10, 11, 12, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52`). The integers are grouped in fives suggesting per-resource events (gift events, recurring gift events, donor events, campaign events, page events) but the spec doesn't label them.

<Warning>
  **⚠️ Spec gap:** The Webhook `EventType` enum has 15 integer values but no labels in the spec. The grouping pattern (10–12, 20–22, etc.) suggests 5 resource families × 3 events each, but the exact mapping is not documented.

  See [Event Types](/raise/webhooks/event-types) for the discoverable mapping. Until the spec adds labels, use the discovery patterns there to identify which event type integers correspond to the events your integration needs.
</Warning>

The `format` and `status` are also documented integer enums:

* `format`: `1` or `2`. The two values likely correspond to JSON and XML delivery formats, but confirm against the live API.
* `status`: `1` for active, `2` for inactive (likely — confirm against live data).

See [Webhooks Overview](/raise/webhooks/overview) for the full webhook subscription workflow.

### Step 3: plan attribution capture

Each Gift record carries multiple attribution fields beyond just the form. Plan which ones your integration captures and stores:

| Attribution field                                                                                             | What it tells you                                  |
| ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `form.id`, `form.title`                                                                                       | Which form the donation came from                  |
| `segment`, `segmentName`                                                                                      | The Segment the form belongs to (channel grouping) |
| `campaignName`                                                                                                | The Campaign the form belongs to                   |
| `motivationCodeId`, `motivationCodeName`, `motivationCodeGroupName`                                           | Why the donor gave (attribution code)              |
| `googleUtmSource`, `googleUtmCampaign`, `googleUtmContent`, `googleUtmMedium`, `googleUtmTerm`, `googleGclid` | Marketing channel attribution                      |
| `landingUrl`, `referringUrl`, `submissionUrl`                                                                 | The web pages involved in the donation flow        |

For analytics integrations, capture all of these. For routing integrations (e.g., sending different thank-you emails per campaign), capture the ones you'll switch on.

The captured attribution lets your integration build per-form, per-segment, per-campaign reporting downstream — see [Donation Forms: form-aware gift analytics](/raise/concepts/donation-forms#pattern-1-form-aware-gift-analytics).

### Step 4: set up the personalized-page generation flow (optional)

For integrations that send personalized donation links — renewal campaigns, major-donor cultivation, payment-method-update prompts — wire up the `POST /api/Donor/{donorId}/generate-page` flow:

```javascript JavaScript theme={null}
async function sendRenewalEmailToDonor(donor) {
  const pageResponse = await fetch(
    `https://prod-api.raisedonors.com/api/Donor/${donor.id}/generate-page`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RAISE_API_TOKEN}`,
        'Content-Type': 'application/json',
      },
    }
  );
  const { pageUrl } = await pageResponse.json();

  await emailService.send({
    to: donor.email,
    subject: 'Renew your support',
    body: `Click here to renew: ${pageUrl}`,
  });
}
```

The endpoint generates a hosted Raise page personalized to the donor. The donor lands on a form pre-filled with their identity and only needs to choose an amount and pay.

<Note>
  The exact request body shape for `POST /api/Donor/{donorId}/generate-page` is not detailed in the spec (the body schema is empty). The endpoint likely accepts parameters like `campaignId` or `formId` to identify which form to personalize. Confirm against the live API before relying on specific request fields.
</Note>

This step is optional but high-leverage for stewardship workflows — partner integrations that send personalized links typically see higher conversion than those sending generic form URLs.

***

## A complete preparation workflow

Pulling the four steps together as a checklist for a new customer onboarding:

<Steps>
  <Step title="Obtain the Raise API token from the customer">
    The customer's Raise administrator generates the token in the admin UI and provides it to your integration through your onboarding flow. Store securely — see [Authentication](/raise/authentication).
  </Step>

  <Step title="Capture the customer's form IDs">
    Ask the customer to provide the form IDs (or titles) for the forms your integration should react to. Store these in your per-customer configuration so the integration knows which forms are in scope.
  </Step>

  <Step title="Subscribe to the relevant webhook events">
    Use `POST /api/Webhook` to subscribe to gift and (if applicable) recurring-gift events. Store the resulting webhook ID for later management. Confirm the `eventTypesList` integers correspond to the events your integration needs.
  </Step>

  <Step title="Set up your webhook receiver">
    Deploy the webhook endpoint, validate signatures using the `securityToken` you set during subscription, and route incoming events through your processing pipeline. See [Signature Verification](/raise/webhooks/signature-verification).
  </Step>

  <Step title="Run a test donation to validate end-to-end">
    Use the test-mode flow (see [Process a Donation: Test-mode flow](/raise/workflows/process-a-donation#test-mode-flow-for-development)) to submit a test donation through one of the customer's forms. Confirm the webhook arrives, your code captures the form context, and the gift flows through your integration correctly.
  </Step>

  <Step title="Document handoff to the customer">
    Confirm to the customer that integration setup is complete. List the forms that are wired up and any forms they haven't given you IDs for.
  </Step>
</Steps>

***

## Handling multi-form customers

Most customers have multiple forms — one for general giving, one for monthly giving, one for each major campaign or event. Partner integrations need to handle this cleanly.

### Pattern: per-form configuration in the partner side

Store a map of form ID → integration behavior:

```javascript JavaScript theme={null}
// Example per-customer form configuration
const formConfig = {
  customer_acme: {
    forms: {
      1234: {
        title: 'General Giving',
        thankYouTemplate: 'general-thank-you',
        slackChannel: '#donations-general',
      },
      5678: {
        title: 'Monthly Sustainers',
        thankYouTemplate: 'monthly-thank-you',
        slackChannel: '#donations-sustainers',
      },
      9012: {
        title: 'Capital Campaign 2025',
        thankYouTemplate: 'capital-thank-you',
        slackChannel: '#donations-capital',
        alertOnLargeGift: 1000,
      },
    },
  },
};

async function processGift(customerId, gift) {
  const config = formConfig[customerId]?.forms[gift.form?.id];
  if (!config) {
    console.warn(`Gift from unconfigured form ${gift.form?.id}`);
    return;
  }

  await sendThankYou(gift.donor, config.thankYouTemplate);
  await notifySlack(config.slackChannel, gift);

  if (config.alertOnLargeGift && gift.amount >= config.alertOnLargeGift) {
    await alertMajorDonorTeam(gift);
  }
}
```

This pattern scales cleanly as the customer adds new forms — the integration's logic doesn't change, only the configuration map.

### When a new form is added

When the customer adds a new form to their account, your integration won't know about it until either:

1. **A donation arrives** through the new form, triggering an "unknown form" log entry (see the `console.warn` above).
2. **The customer manually informs you** of the new form's ID through your onboarding flow.

Option 2 is more reliable but requires the customer to take an action. Option 1 catches everything but requires the integration to flag unknown forms for review rather than silently dropping them.

A reasonable default: log unknown forms loudly, process the gift through a default behavior, and surface the unknown form for human review (typically via a dashboard or email alert to the integration owner).

***

## What the API does not provide for form configuration

For completeness, the operations that the current API doesn't support. If your integration's design depends on one of these, you'll need to coordinate with the customer's admin team or wait for v2 API capabilities.

| Capability                                    | Not in current API                                        |
| --------------------------------------------- | --------------------------------------------------------- |
| List all forms for a customer                 | No `GET /api/Form/list` endpoint                          |
| Get a form's field configuration              | No form-get-by-ID endpoint                                |
| Update a form's content                       | No update endpoint                                        |
| Toggle a form between active and inactive     | Handled in admin UI                                       |
| Get a form's conversion rate or traffic stats | Use Campaign-level metrics or aggregate from Gift queries |
| Clone a form to a new Campaign                | Handled in admin UI                                       |
| Get the embed code for a form                 | The customer obtains this from the admin UI               |

See [Donation Forms](/raise/concepts/donation-forms#what-the-api-doesnt-expose) for the full list.

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Embed a Form" icon="code" href="/raise/workflows/embed-a-form">
    The workflow for placing a Raise donation form on a partner-hosted page.
  </Card>

  <Card title="Process a Donation" icon="hand-holding-dollar" href="/raise/workflows/process-a-donation">
    What happens when a donor submits a configured form — the `POST /api/Raise/give` flow.
  </Card>

  <Card title="Donation Forms" icon="file-lines" href="/raise/concepts/donation-forms">
    The resource-level reference for what the API does and doesn't expose about forms.
  </Card>

  <Card title="Webhooks Overview" icon="webhook" href="/raise/webhooks/overview">
    Subscribe to the events that forms produce when donations complete.
  </Card>
</CardGroup>
