> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sample Webhooks

> Worked examples of how a spend transaction moves through webhooks, including partial, over, multi, and force captures and refunds.

A spend transaction reports its progress through a sequence of `transaction` webhooks: `requested`, `created`, `updated`, and `completed`. The settlement amount, the order of events, and the sign of the amount differ depending on what the merchant does. These examples show how each common settlement pattern appears in the webhooks you receive, so you can reconcile transactions and manage holds in your ledger correctly.

Every example shows the whole delivery: the `id`, `resource`, `action`, and `version` envelope fields, then the `body`. The `version` reflects the schema version of the `body`, so the value you receive depends on the version your tenant is on. Rain signs each delivery, so verify the signature before you process it. See [Webhook delivery](/docs/webhook-delivery) for the envelope fields, lifecycle, and retry behavior, and [Set up webhooks](/docs/set-up-webhooks) for signature verification.

## Full lifecycle

The base case: an authorization for \$100.00 is created, adjusted down by \$20.00, and settled at \$80.00. Watch how `amount` and `status` change at each step.

<Steps>
  <Step title="transaction.requested">
    Rain forwards the authorization attempt for \$100.00 (`amount: 10000`). The transaction is `pending`.

    ```json transaction.requested theme={null}
    {
      "id": "db0a65af-0f49-403e-880a-20a82879a507",
      "resource": "transaction",
      "action": "requested",
      "version": "1.1.0",
      "body": {
        "id": "30dcf8c6-a1e5-48f1-9c40-ecffe8253d25",
        "type": "spend",
        "spend": {
          "amount": 10000,
          "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
          "status": "pending",
          "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
          "cardType": "virtual",
          "currency": "usd",
          "userEmail": "rain@example.com",
          "merchantId": "d0a30859-096d-57f4-bffd-fd745f44e048",
          "localAmount": 10000,
          "merchantCity": "New York",
          "merchantName": "Test Merchant",
          "userLastName": "Doe",
          "localCurrency": "usd",
          "userFirstName": "Rain",
          "merchantCountry": "US",
          "authorizedAmount": 10000,
          "merchantCategory": "Restaurants",
          "authorizationMethod": "00",
          "merchantCategoryCode": "5812",
          "previouslyAuthorizedAmount": 0
        }
      }
    }
    ```
  </Step>

  <Step title="transaction.created">
    Rain approves the authorization and creates the transaction, still `pending` for \$100.00.

    ```json transaction.created theme={null}
    {
      "id": "99493687-78c1-4018-8831-d8b1f66f58e2",
      "resource": "transaction",
      "action": "created",
      "version": "1.2.0",
      "body": {
        "id": "30dcf8c6-a1e5-48f1-9c40-ecffe8253d25",
        "type": "spend",
        "spend": {
          "amount": 10000,
          "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
          "status": "pending",
          "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
          "cardType": "virtual",
          "currency": "usd",
          "userEmail": "rain@example.com",
          "merchantId": "d0a30859-096d-57f4-bffd-fd745f44e048",
          "localAmount": 10000,
          "authorizedAt": "2025-06-25T15:24:11.337Z",
          "merchantCity": "New York",
          "merchantName": "Test Merchant",
          "userLastName": "Doe",
          "localCurrency": "usd",
          "userFirstName": "Rain",
          "merchantCountry": "US",
          "authorizedAmount": 10000,
          "merchantCategory": "Restaurants",
          "authorizationMethod": "00",
          "merchantCategoryCode": "5812"
        }
      }
    }
    ```
  </Step>

  <Step title="transaction.updated">
    The merchant reverses \$20.00. The status changes to `reversed`, `authorizationUpdateAmount` is `-2000`, and the amount drops to \$80.00.

    ```json transaction.updated theme={null}
    {
      "id": "e7b2853e-4bb7-4428-8dc2-27e604766dfa",
      "resource": "transaction",
      "action": "updated",
      "version": "1.1.0",
      "body": {
        "id": "30dcf8c6-a1e5-48f1-9c40-ecffe8253d25",
        "type": "spend",
        "spend": {
          "amount": 8000,
          "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
          "status": "reversed",
          "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
          "cardType": "virtual",
          "currency": "usd",
          "userEmail": "rain@example.com",
          "merchantId": "d0a30859-096d-57f4-bffd-fd745f44e048",
          "localAmount": 8000,
          "authorizedAt": "2025-06-25T15:24:11.337Z",
          "merchantCity": "New York",
          "merchantName": "Test Merchant",
          "userLastName": "Doe",
          "localCurrency": "usd",
          "userFirstName": "Rain",
          "merchantCountry": "US",
          "authorizedAmount": 8000,
          "merchantCategory": "Restaurants",
          "authorizationMethod": "00",
          "enrichedMerchantName": "Test Merchant",
          "merchantCategoryCode": "5812",
          "enrichedMerchantCategory": "Restaurants",
          "authorizationUpdateAmount": -2000
        }
      }
    }
    ```
  </Step>

  <Step title="transaction.completed">
    The transaction settles at \$80.00. The status is `completed` and `postedAt` is set.

    ```json transaction.completed theme={null}
    {
      "id": "0a20a26e-24a6-445a-a52f-ec95b62f0b0b",
      "resource": "transaction",
      "action": "completed",
      "version": "1.3.0",
      "body": {
        "id": "30dcf8c6-a1e5-48f1-9c40-ecffe8253d25",
        "type": "spend",
        "spend": {
          "amount": 8000,
          "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
          "status": "completed",
          "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
          "cardType": "virtual",
          "currency": "usd",
          "postedAt": "2025-06-26T15:24:42.486Z",
          "userEmail": "rain@example.com",
          "localAmount": 8000,
          "authorizedAt": "2025-06-25T15:24:11.337Z",
          "merchantCity": "New York",
          "merchantName": "Test Merchant",
          "userLastName": "Doe",
          "localCurrency": "usd",
          "userFirstName": "Rain",
          "merchantCountry": "US",
          "authorizedAmount": 8000,
          "merchantCategory": "Restaurants",
          "authorizationMethod": "00",
          "enrichedMerchantName": "Test Merchant",
          "merchantCategoryCode": "5812",
          "enrichedMerchantCategory": "Restaurants"
        }
      }
    }
    ```
  </Step>
</Steps>

Except for force capture, the patterns below follow this same `requested` → `created` → `completed` sequence. Each one highlights only the webhook where the behavior differs.

## Partial capture

The merchant settles **less** than the authorized amount. The `transaction.completed` `amount` (\$90.00) is lower than `authorizedAmount` (\$100.00). Reconcile against the settled `amount`, not the authorization.

```json transaction.completed theme={null}
{
  "id": "a79306b2-bbbc-4511-9e58-ca9fbc9a2d9a",
  "resource": "transaction",
  "action": "completed",
  "version": "1.3.0",
  "body": {
    "id": "be67eeb7-294a-42d9-b337-77bfad198aad",
    "type": "spend",
    "spend": {
      "amount": 9000,
      "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
      "status": "completed",
      "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
      "cardType": "virtual",
      "currency": "usd",
      "postedAt": "2025-07-03T18:40:47.116Z",
      "userEmail": "rain@example.com",
      "localAmount": 9000,
      "authorizedAt": "2025-07-03T18:40:28.024Z",
      "merchantCity": "New York",
      "merchantName": "Partial Capture Example",
      "userLastName": "Doe",
      "localCurrency": "usd",
      "userFirstName": "Rain",
      "merchantCountry": "US",
      "authorizedAmount": 10000,
      "merchantCategory": "Gas station",
      "authorizationMethod": "00",
      "enrichedMerchantName": "Partial Capture Example",
      "merchantCategoryCode": "5511",
      "enrichedMerchantCategory": "Gas station"
    }
  }
}
```

## Over capture

The merchant settles **more** than the authorized amount. The `transaction.completed` `amount` (\$110.00) is higher than `authorizedAmount` (\$100.00).

```json transaction.completed theme={null}
{
  "id": "593b0673-82ba-457b-afce-1cbd725f9e3c",
  "resource": "transaction",
  "action": "completed",
  "version": "1.3.0",
  "body": {
    "id": "68f05b24-da4e-4898-bc26-659563b5ed6a",
    "type": "spend",
    "spend": {
      "amount": 11000,
      "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
      "status": "completed",
      "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
      "cardType": "virtual",
      "currency": "usd",
      "postedAt": "2025-07-03T18:54:02.713Z",
      "userEmail": "rain@example.com",
      "localAmount": 11000,
      "authorizedAt": "2025-07-03T18:53:49.958Z",
      "merchantCity": "New York",
      "merchantName": "Over Capture Example",
      "userLastName": "Doe",
      "localCurrency": "usd",
      "userFirstName": "Rain",
      "merchantCountry": "US",
      "authorizedAmount": 10000,
      "merchantCategory": "Restaurants",
      "authorizationMethod": "00",
      "enrichedMerchantName": "Over Capture Example",
      "merchantCategoryCode": "5812",
      "enrichedMerchantCategory": "Restaurants"
    }
  }
}
```

## Multi-capture

The merchant settles one authorization in **more than one piece** (here \$70.00, then \$30.00). The transaction stays `pending` until the full amount settles, then Rain sends a single `transaction.completed` for the total (\$100.00).

<Warning>
  Wait for `transaction.completed` before releasing funds. The transaction remains `pending` between the partial settlements, even though money has started to move.
</Warning>

```json transaction.completed theme={null}
{
  "id": "b96cc05a-e246-4968-82e5-60981791b7f1",
  "resource": "transaction",
  "action": "completed",
  "version": "1.3.0",
  "body": {
    "id": "e4d630c4-bce2-4545-9936-5e21624689a9",
    "type": "spend",
    "spend": {
      "amount": 10000,
      "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
      "status": "completed",
      "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
      "cardType": "virtual",
      "currency": "usd",
      "postedAt": "2025-07-03T19:36:12.383Z",
      "userEmail": "rain@example.com",
      "localAmount": 10000,
      "authorizedAt": "2025-07-03T19:11:39.909Z",
      "merchantCity": "New York",
      "merchantName": "Multi Capture Example",
      "userLastName": "Doe",
      "localCurrency": "usd",
      "userFirstName": "Rain",
      "merchantCountry": "US",
      "authorizedAmount": 10000,
      "merchantCategory": "Children's and Infant's Wear Store",
      "authorizationMethod": "00",
      "enrichedMerchantName": "Multi Capture Example",
      "merchantCategoryCode": "5641",
      "enrichedMerchantCategory": "Children's and Infant's Wear Store"
    }
  }
}
```

## Force capture

A force capture settles **without a prior authorization**. The merchant bypasses the authorization phase and settles directly, so you receive a single `transaction.completed` with no preceding `requested` or `created` events. This happens in offline scenarios, such as in-flight purchases where the merchant has no connectivity. Rain populates `authorizedAt` and `authorizedAmount` from the settlement message itself, not from a prior authorization step.

```json transaction.completed theme={null}
{
  "id": "3394c9f0-af89-437d-afdb-c7cf3cabe245",
  "resource": "transaction",
  "action": "completed",
  "version": "1.3.0",
  "body": {
    "id": "feaec7b0-a9d5-4907-b05f-887661a54cd7",
    "type": "spend",
    "spend": {
      "amount": 10000,
      "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
      "status": "completed",
      "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
      "cardType": "virtual",
      "currency": "usd",
      "postedAt": "2025-07-06T15:35:00.553Z",
      "userEmail": "rain@example.com",
      "localAmount": 10000,
      "authorizedAt": "2025-07-06T15:35:00.274Z",
      "merchantCity": "Jersey City",
      "merchantName": "Test Force Capture",
      "userLastName": "Doe",
      "localCurrency": "usd",
      "userFirstName": "Rain",
      "merchantCountry": "US",
      "authorizedAmount": 10000,
      "merchantCategory": "Test Merchant",
      "authorizationMethod": "00",
      "merchantCategoryCode": "3000"
    }
  }
}
```

## Refund

A refund appears as a spend transaction with **negative amounts**, with `amount` and `authorizedAmount` negative throughout.

In [Partner-Managed](/docs/authorization-partner-managed#refund-authorizations) programs, the refund follows the same `requested` → `created` → `completed` sequence. In [Rain-Managed](/docs/authorization-rain-managed#refund-authorizations) programs, Rain decides the authorization, so you receive `created` and then `completed` without a `requested` event.

<Warning>
  **Partner-managed authorization clients:** do not credit pending refunds. Wait for `transaction.completed` before releasing the authorization hold in your ledger.
</Warning>

```json transaction.completed theme={null}
{
  "id": "77474a56-51eb-4918-b09e-73cf20077b1b",
  "resource": "transaction",
  "action": "completed",
  "version": "1.3.0",
  "body": {
    "id": "4e19a38e-3161-4db1-ac91-e12630950e2c",
    "type": "spend",
    "spend": {
      "amount": -10000,
      "cardId": "827c3893-d7c8-46d4-a518-744b016555bc",
      "status": "completed",
      "userId": "8e03decf-26b9-41fb-bb73-4fe1f847042a",
      "cardType": "virtual",
      "currency": "usd",
      "postedAt": "2025-07-03T19:57:04.332Z",
      "userEmail": "rain@example.com",
      "localAmount": -10000,
      "authorizedAt": "2025-07-03T19:52:59.806Z",
      "merchantCity": "New York",
      "merchantName": "Test Refund",
      "userLastName": "Doe",
      "localCurrency": "usd",
      "userFirstName": "Rain",
      "merchantCountry": "US",
      "authorizedAmount": -10000,
      "merchantCategory": "Children's and Infant's Wear Store",
      "authorizationMethod": "00",
      "enrichedMerchantName": "Test Refund",
      "merchantCategoryCode": "5641",
      "enrichedMerchantCategory": "Children's and Infant's Wear Store"
    }
  }
}
```

Rain cannot link every refund to the original `transactionId`, because Rain can process refunds as standalone transaction authorizations. When no `transactionId` link exists, treat `merchantName` as a heuristic only: it is neither unique nor stable, so combine it with other signals such as amount, card, and timing before you associate a refund with a specific spend. If you cannot match with confidence, leave the refund unlinked rather than risk a wrong association.

## What's next

<Columns cols={2}>
  <Card title="Transaction events" icon="money-bill-transfer" href="/docs/transaction">
    See the full field reference behind every payload shown above.
  </Card>

  <Card title="Decline reasons" icon="circle-exclamation" href="/docs/decline-reasons">
    Look up what a `declinedReason` value means and what to do about it.
  </Card>
</Columns>
