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

# Webhooks Overview

> The Raise webhook system — how to subscribe to events, the delivery model, and the diagnostic tools (webhook logs) available for investigating delivery issues.

Raise webhooks deliver real-time notifications when records change — donations completed, recurring schedules created or cancelled, donor updates, and others. For partner integrations that need to react to activity in Raise without polling, the webhook subscription is the canonical mechanism.

This page covers the webhook subscription endpoints, the create/list/update/delete operations, and the diagnostic log endpoints that make webhook delivery investigations possible. The detailed event catalog is on [Event Types](/raise/webhooks/event-types); the request-validation pattern is on [Signature Verification](/raise/webhooks/signature-verification).

## The webhook surface at a glance

The Raise webhook system has eight endpoints organized into two groups:

### Subscription management

| Endpoint                   | Method | Use                                         |
| -------------------------- | ------ | ------------------------------------------- |
| `POST /api/Webhook`        | POST   | Create a new webhook subscription           |
| `GET /api/Webhook/list`    | GET    | Paginated list of all webhook subscriptions |
| `GET /api/Webhook/{id}`    | GET    | Get a specific subscription                 |
| `PUT /api/Webhook/{id}`    | PUT    | Update a specific subscription              |
| `DELETE /api/Webhook/{id}` | DELETE | Delete a specific subscription              |

### Delivery logs

| Endpoint                            | Method | Use                                                 |
| ----------------------------------- | ------ | --------------------------------------------------- |
| `GET /api/Webhook/log/list`         | GET    | Paginated list of delivery logs across all webhooks |
| `GET /api/Webhook/{id}/log/list`    | GET    | Paginated logs for a specific webhook               |
| `GET /api/Webhook/{id}/log/{logId}` | GET    | A single log entry with full payload detail         |

The log endpoints are one of Raise's most useful diagnostic features. When a partner integration sees an event arrive (or fail to arrive), the logs show exactly what was delivered, what the partner's endpoint responded with, and when. See [Inspecting delivery logs](#inspecting-delivery-logs) below.

***

## Creating a webhook subscription

```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 integration — gift events",
    "notificationUrl": "https://partner.example.com/raise-webhooks",
    "eventTypesList": [10, 11],
    "format": 1,
    "status": 1,
    "securityToken": "your-shared-secret-here"
  }'
```

### The WebhookRequest fields

| Field             | Type                | Description                                                                                                              |
| ----------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `name`            | string              | Display name for the subscription. Appears in admin UI and delivery logs.                                                |
| `notificationUrl` | string              | The HTTPS URL on the partner side that receives event deliveries.                                                        |
| `eventTypesList`  | array of integers   | The event type integers to subscribe to. See [Event Types](/raise/webhooks/event-types).                                 |
| `format`          | integer enum (1, 2) | The delivery payload format. Two values exist; confirm against live API for the specific format mapping.                 |
| `status`          | integer enum (1, 2) | The subscription's active/inactive state. Two values exist; `1` is typically active.                                     |
| `securityToken`   | string              | The shared secret used for signature verification. See [Signature Verification](/raise/webhooks/signature-verification). |

<Note>
  **⚠️ Spec gap:** The `format` and `status` enums have two integer values each (`[1, 2]`) but the spec doesn't label them. `format: 1` likely corresponds to JSON delivery and `format: 2` to an alternative format; `status: 1` likely means active and `status: 2` means inactive — but confirm against the live API before relying on these assumptions.
</Note>

### Required fields in practice

The Raise spec doesn't formally mark any WebhookRequest fields as required. In practice, however, all subscriptions need at least these four:

* `name` — for identifying the subscription in the admin UI
* `notificationUrl` — without it, events have nowhere to go
* `eventTypesList` — without it, no events match the subscription
* `securityToken` — required for signature verification (don't deploy without it)

The `format` and `status` fields can be omitted to use defaults, but explicit values are clearer.

### Storing the webhook ID

The create response includes the new subscription's `id`. Store this — you'll need it to update or delete the subscription later, and to fetch delivery logs for it.

```javascript JavaScript theme={null}
async function createGiftWebhook(customerId) {
  const securityToken = generateSecureToken();

  const response = await fetch(
    'https://prod-api.raisedonors.com/api/Webhook',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RAISE_API_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name: `Partner X — ${customerId} — gift events`,
        notificationUrl: `https://partner.example.com/raise-webhooks/${customerId}`,
        eventTypesList: [10, 11], // gift events — see Event Types
        format: 1,
        status: 1,
        securityToken,
      }),
    }
  );

  const webhook = await response.json();

  // Store the webhook ID and the security token in your per-customer settings
  await secrets.set(`customer:${customerId}:webhook_id`, webhook.id);
  await secrets.set(`customer:${customerId}:webhook_secret`, securityToken);

  return webhook;
}
```

***

## The delivery model

When an event fires in Raise (a gift is created, a donor is updated, etc.), the platform identifies all webhook subscriptions whose `eventTypesList` includes that event type and `status` is active. For each match, it makes an HTTP POST to the subscription's `notificationUrl` with the event payload.

### The HTTP request shape

The partner's webhook endpoint receives a POST with:

* A JSON (or alternative format, per the `format` field) body containing the event payload.
* A header carrying a signature derived from the `securityToken` — used to verify the request came from Raise. See [Signature Verification](/raise/webhooks/signature-verification) for the verification pattern.

The exact body shape varies by event type. Most event payloads include:

* The event type identifier (the integer from the EventType enum)
* The resource that triggered the event (a Gift, Donor, RecurringGift, etc.) embedded in the payload
* Timing metadata (when the event occurred)

For the full event-by-event payload reference, see [Event Types](/raise/webhooks/event-types).

### What the partner endpoint should do

The partner's webhook endpoint should:

<Steps>
  <Step title="Verify the signature">
    Use the `securityToken` to verify the request actually came from Raise. Reject any request whose signature doesn't validate — without this check, anyone who knows the URL can forge events. See [Signature Verification](/raise/webhooks/signature-verification).
  </Step>

  <Step title="Acknowledge quickly">
    Return `200 OK` as soon as the request is received and the signature is verified. Don't process the event synchronously inside the request handler — queue it for processing and acknowledge.
  </Step>

  <Step title="Process asynchronously">
    A background worker drains the queue and processes events. This decouples Raise's delivery latency from your processing latency.
  </Step>

  <Step title="Handle duplicates">
    Webhook delivery is at-least-once, not exactly-once. Build the processor to handle duplicate deliveries gracefully — see [Idempotency and Safe Reprocessing](/raise/webhooks/idempotency-and-safe-reprocessing).
  </Step>
</Steps>

The "acknowledge quickly, process asynchronously" pattern is the single most important webhook practice. Endpoints that process synchronously inside the request handler hit timeouts under load and produce cascade failures during traffic spikes.

### Delivery characteristics

A few things to know about Raise's webhook delivery behavior:

| Characteristic     | Description                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------------- |
| Delivery model     | At-least-once. Events may be delivered more than once.                                                                |
| Retries on failure | Raise retries failed deliveries — see [Retry Behavior](/raise/webhooks/retry-behavior).                               |
| Order guarantees   | Events for the same resource are typically delivered in order, but ordering across resources is not guaranteed.       |
| Timing             | Most events deliver within seconds of the underlying change. Occasional delays of minutes are normal under high load. |

<Note>
  The Raise OpenAPI spec doesn't explicitly document the retry schedule, the exact timeout the partner endpoint must respect, or the maximum number of retry attempts. See [Retry Behavior](/raise/webhooks/retry-behavior) for what's known and the defensive patterns to use.
</Note>

***

## Listing subscriptions

```bash cURL theme={null}
curl https://prod-api.raisedonors.com/api/Webhook/list \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

Returns the paginated list of webhook subscriptions for the organization. Each item is a `WebhookListModel`:

| Field             | Type    | Description                                      |
| ----------------- | ------- | ------------------------------------------------ |
| `id`              | integer | The subscription's primary key                   |
| `name`            | string  | Display name                                     |
| `eventTypes`      | string  | Comma-separated string of subscribed event types |
| `notificationUrl` | string  | The partner's endpoint URL                       |
| `status`          | integer | Active/inactive state                            |
| `format`          | integer | Payload format                                   |
| `statusDisplay`   | string  | Human-readable status (`"Active"` or similar)    |
| `formatDisplay`   | string  | Human-readable format (`"JSON"` or similar)      |
| `securityToken`   | string  | The shared secret (stored verbatim)              |
| `createdDate`     | string  | When the subscription was created                |
| `modifiedDate`    | string  | When the subscription was last changed           |

The `statusDisplay` and `formatDisplay` fields are useful for understanding the enum integers — `status: 1` paired with `statusDisplay: "Active"` confirms the mapping. Inspect a few subscription records to learn the labels.

***

## Updating a subscription

```bash cURL theme={null}
curl -X PUT https://prod-api.raisedonors.com/api/Webhook/123 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Partner X — gift events (updated)",
    "notificationUrl": "https://partner.example.com/v2/raise-webhooks",
    "eventTypesList": [10, 11, 12],
    "format": 1,
    "status": 1,
    "securityToken": "rotated-secret"
  }'
```

`PUT /api/Webhook/{id}` accepts the same body shape as create. Send the full record — the spec doesn't expose a PATCH variant, so partial updates use the GET-then-PUT pattern.

### Common update scenarios

| Change                              | Fields to update                                                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Subscribe to additional event types | `eventTypesList` with the expanded set                                                                                          |
| Move the receiver to a new URL      | `notificationUrl`                                                                                                               |
| Rotate the security token           | `securityToken` — see [Signature Verification: rotating the secret](/raise/webhooks/signature-verification#rotating-the-secret) |
| Pause the subscription              | `status: 2` (inactive) — events stop being delivered but the subscription persists                                              |
| Resume a paused subscription        | `status: 1` (active)                                                                                                            |

### Pausing vs. deleting

Use `status: 2` to pause a subscription temporarily — useful during maintenance windows or partner-side issues. Use `DELETE /api/Webhook/{id}` to remove the subscription permanently when it's no longer needed (e.g., during customer offboarding).

***

## Deleting a subscription

```bash cURL theme={null}
curl -X DELETE https://prod-api.raisedonors.com/api/Webhook/123 \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

Deletes the subscription permanently. Once deleted, no further events will be delivered to the previous `notificationUrl`. The subscription's delivery logs may persist for a retention window — check via the log endpoints if needed.

Delete subscriptions during customer offboarding to stop sending events to a partner that's no longer integrated. Leaving stale subscriptions in place wastes the customer's notification budget and produces noise on the partner side if the URL is later reused.

***

## Inspecting delivery logs

The webhook log endpoints are the diagnostic tool for investigating "the partner integration didn't react to an event" issues. They show what Raise attempted to deliver and how the partner endpoint responded.

### Listing logs for a specific webhook

```bash cURL theme={null}
curl -G "https://prod-api.raisedonors.com/api/Webhook/123/log/list" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  --data-urlencode "Take=50" \
  --data-urlencode "SortBy=createddatetime" \
  --data-urlencode "Descending=true"
```

The endpoint supports the standard `/list` pagination parameters with these sort fields documented: `createddatetime, id`.

### The WebhookLogListModel

Each log entry includes:

| Field              | Description                                                                                                |
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
| `id`               | The log entry's ID                                                                                         |
| `webhookId`        | The subscription this delivery belongs to                                                                  |
| `contextId`        | The ID of the entity that triggered the event (e.g., the Gift ID)                                          |
| `eventType`        | Integer event type                                                                                         |
| `eventTypeDisplay` | **Human-readable label for the event** — this is the canonical place to learn the integer-to-label mapping |
| `serverResponse`   | The response body returned by the partner's endpoint                                                       |
| `success`          | Whether the delivery succeeded (string field — likely `"Yes"`/`"No"` or `"True"`/`"False"`)                |
| `httpStatusCode`   | The HTTP status the partner returned                                                                       |
| `createdDate`      | When the delivery was attempted                                                                            |
| `modifiedDate`     | When the log entry was last updated                                                                        |

The `eventTypeDisplay` field is the single best source of truth for the event type enum's integer-to-label mapping. The spec doesn't document the labels themselves, but inspecting delivery logs reveals them.

### Investigating a missed event

A common diagnostic flow:

<Steps>
  <Step title="Confirm the event fired">
    Query the webhook logs for the specific gift, donor, or other resource. If no log entry exists, the event wasn't fired at all — investigate the source data.
  </Step>

  <Step title="Check the response status code">
    If `httpStatusCode` is `2xx`, Raise considers the delivery successful. The partner endpoint may have a bug in its processing pipeline rather than a delivery issue.
  </Step>

  <Step title="Inspect the response body">
    `serverResponse` shows what the partner endpoint returned. A `200 OK` with an error message in the body indicates a partner-side processing issue.
  </Step>

  <Step title="Check for retries">
    Multiple log entries for the same `contextId` indicate Raise retried delivery. The retry pattern can reveal whether the partner endpoint was intermittently failing.
  </Step>

  <Step title="Fetch the full payload">
    Use `GET /api/Webhook/{id}/log/{logId}` to get the singular log with `payLoad` — the full delivered body. Useful for confirming the exact data the partner endpoint received.
  </Step>
</Steps>

### Getting a single log's full detail

```bash cURL theme={null}
curl https://prod-api.raisedonors.com/api/Webhook/123/log/456 \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

The single-log endpoint returns a `WebhookLogModel` with `webhookName` and `payLoad` — the full body delivered to the partner endpoint. Useful for confirming the exact shape of an event when building or debugging the partner-side handler.

***

## Listing logs across all webhooks

```bash cURL theme={null}
curl https://prod-api.raisedonors.com/api/Webhook/log/list \
  -H "Authorization: Bearer YOUR_API_TOKEN"
```

The org-wide log list — across every webhook subscription. Useful for the customer's admin team to monitor delivery health across all integrations, or for investigating "are webhooks working in general" questions independent of a specific subscription.

For per-integration diagnostics, the per-webhook log list (`/api/Webhook/{id}/log/list`) is more targeted.

***

## A complete subscription lifecycle

Putting the operations together as the full lifecycle for a customer onboarding:

<Steps>
  <Step title="Generate a security token">
    Cryptographically random, at least 32 bytes. Store securely on the partner side; you'll need it for signature verification on every delivery.
  </Step>

  <Step title="Create the subscription">
    `POST /api/Webhook` with the partner endpoint URL, the event type integers your integration cares about, and the security token.
  </Step>

  <Step title="Store the webhook ID">
    Save the returned `id` in your per-customer settings. You'll need it for updates, deletes, and log queries.
  </Step>

  <Step title="Deploy the receiver">
    Stand up the partner endpoint at the `notificationUrl`. Implement signature verification before any event processing — see [Signature Verification](/raise/webhooks/signature-verification).
  </Step>

  <Step title="Validate end-to-end">
    Run a test donation through the customer's account (in test mode) and confirm the event arrives at the partner endpoint with a valid signature. Inspect the webhook logs to confirm Raise sent it and the partner returned `200 OK`.
  </Step>

  <Step title="Monitor in production">
    Watch the partner endpoint's logs and the Raise webhook logs for signs of issues — `5xx` responses on the partner side, signature failures, repeated retries.
  </Step>

  <Step title="Update or pause as needed">
    During partner maintenance, set `status: 2` to pause delivery. During customer offboarding, `DELETE /api/Webhook/{id}` to remove permanently.
  </Step>
</Steps>

***

## Where to go next

<CardGroup cols={2}>
  <Card title="Event Types" icon="bolt" href="/raise/webhooks/event-types">
    The catalog of available webhook events and the discovery pattern for the integer-to-label mapping.
  </Card>

  <Card title="Signature Verification" icon="shield-check" href="/raise/webhooks/signature-verification">
    Verify that incoming requests actually came from Raise.
  </Card>

  <Card title="Retry Behavior" icon="arrows-rotate" href="/raise/webhooks/retry-behavior">
    What Raise does when the partner endpoint doesn't respond successfully.
  </Card>

  <Card title="Idempotency and Safe Reprocessing" icon="check-double" href="/raise/webhooks/idempotency-and-safe-reprocessing">
    Handle duplicate deliveries without producing duplicate side effects.
  </Card>
</CardGroup>
