> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runpayments.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runpayments.io/_mcp/server.

# Transaction Webhooks

Transaction webhooks tell you when a transaction is recorded and each time its status changes. Every transaction event has the same payload, so one handler can cover them all. For the envelope, signature, duplicates and retries, see the [Webhooks Overview](/docs/guides/webhooks/overview).

## Transaction Lifecycle

```
Authorized (1) → Batched (2) → Captured (3) → Processed (5)
```

A successful transaction moves along this path. You get `transaction.entered` when it's recorded, then `transaction.batched`, `transaction.captured` and `transaction.processed` as its status changes. `trans_status_id` is where the transaction is now, which is why most settled transactions show 5.

Off this path, Declined (4) and Rejected (22) end the transaction, and you get `transaction.decline` or `transaction.reject`. A transaction can also be Voided (7), or later Refunded (6) or Partially Refunded (63).

## Events

| **Event Type**                 | **Sent when**                                                                                                                          |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `transaction.entered`          | A transaction is recorded, whatever its outcome.                                                                                       |
| `transaction.entered.<source>` | Also sent, with `transaction.entered`, for a transaction from that source, such as `transaction.entered.hpp`. See [Sources](#sources). |
| `transaction.batched`          | The transaction is batched.                                                                                                            |
| `transaction.captured`         | The transaction is captured.                                                                                                           |
| `transaction.processed`        | The transaction is processed.                                                                                                          |
| `transaction.decline`          | The transaction is declined.                                                                                                           |
| `transaction.reject`           | The transaction is rejected.                                                                                                           |
| `transaction.voided`           | The transaction is voided.                                                                                                             |
| `transaction.refund`           | The transaction is fully refunded.                                                                                                     |
| `transaction.partialrefund`    | Part of the transaction is refunded. Sent again each time the refunded amount changes.                                                 |

One transaction can send several events. A transaction declined when it's entered sends `transaction.entered` and then `transaction.decline`. `event_table_pk` is the same on every event for the same transaction, so use it to match them up.

## Sources

Every transaction payload carries the source the transaction came from, as `source` and `transact_source_id`. Each source also has its own event, `transaction.entered.<source>`, sent alongside `transaction.entered`. Subscribe to a per-source event if you only want transactions from one source.

| **`source`** | **`transact_source_id`** | **Source**                                 | **Per-source event**             |
| ------------ | ------------------------ | ------------------------------------------ | -------------------------------- |
| `rmvt`       | 1                        | Run Merchant virtual terminal              | `transaction.entered.rmvt`       |
| `hpp`        | 2                        | Hosted Payment Page                        | `transaction.entered.hpp`        |
| `api`        | 3                        | Run Developer API                          | `transaction.entered.api`        |
| `thirdparty` | 4                        | Imported from a third-party processor feed | `transaction.entered.thirdparty` |
| `billing`    | 5                        | Billing plan payment                       | `transaction.entered.billing`    |
| `invoicing`  | 6                        | Invoice payment                            | `transaction.entered.invoicing`  |
| `clover`     | 21                       | Clover Connector                           | `transaction.entered.clover`     |

Both fields are `null` when the source isn't known. New sources may be added.

## Examples

### `transaction.entered`

```json
{
  "event_type": "transaction.entered",
  "source_id": "104000",
  "timestamp": "2026-10-15T14:49:28.181Z",
  "payload": {
    "event_table_pk": 219000000,
    "mid": "800000000000",
    "trans_id": "076400000000",
    "card_number": "9400000000001111",
    "name_on_account": "Jane Example",
    "expiration": "202812",
    "auth_code": "PPS123",
    "response_text": "Approval",
    "response_code": "000",
    "tokenized_card": "1a2b3c4d5e6f7a8b",
    "order_id": "ORDER-1001",
    "amount": 45.00,
    "refunded_amount": null,
    "transaction_timestamp": "2026-10-15T14:49:28.965+00:00",
    "trans_status_id": 1,
    "invoice_id": "INV-1001",
    "transact_source_id": 1,
    "source": "rmvt",
    "timestamp": "2026-10-15T14:49:28.089+00:00"
  },
  "metadata": {
    "webhook_id": "wh_600000",
    "attempt": 1,
    "idempotency_key": "4f1c0a9e2b7d4c3e8a6b5d0f1e2c3b4a"
  }
}
```

This card was saved, so `tokenized_card` holds its stored-card token. In most transactions it is `null`.

### `transaction.decline`

```json
{
  "event_type": "transaction.decline",
  "source_id": "104000",
  "timestamp": "2026-10-15T15:05:12.181Z",
  "payload": {
    "event_table_pk": 219000001,
    "mid": "800000000000",
    "trans_id": "076400000001",
    "card_number": "9400000000001111",
    "name_on_account": "Sam Example",
    "expiration": "202812",
    "auth_code": null,
    "response_text": "Insufficient Funds",
    "response_code": "051",
    "tokenized_card": null,
    "order_id": "ORDER-1002",
    "amount": 150.00,
    "refunded_amount": null,
    "transaction_timestamp": "2026-10-15T15:05:12.965+00:00",
    "trans_status_id": 4,
    "invoice_id": null,
    "transact_source_id": 2,
    "source": "hpp",
    "timestamp": "2026-10-15T15:05:12.089+00:00"
  },
  "metadata": {
    "webhook_id": "wh_600003",
    "attempt": 1,
    "idempotency_key": "0a1b2c3d4e5f60718293a4b5c6d7e8f9"
  }
}
```

### `transaction.reject`

```json
{
  "event_type": "transaction.reject",
  "source_id": "104000",
  "timestamp": "2026-10-15T15:12:40.181Z",
  "payload": {
    "event_table_pk": 219000002,
    "mid": "800000000000",
    "trans_id": "076400000002",
    "card_number": "9400000000001111",
    "name_on_account": "Alex Example",
    "expiration": "202812",
    "auth_code": null,
    "response_text": "Rejected",
    "response_code": "059",
    "tokenized_card": null,
    "order_id": "ORDER-1003",
    "amount": 999.99,
    "refunded_amount": null,
    "transaction_timestamp": "2026-10-15T15:12:40.965+00:00",
    "trans_status_id": 22,
    "invoice_id": null,
    "transact_source_id": 3,
    "source": "api",
    "timestamp": "2026-10-15T15:12:40.089+00:00"
  },
  "metadata": {
    "webhook_id": "wh_600004",
    "attempt": 1,
    "idempotency_key": "1b2c3d4e5f60718293a4b5c6d7e8f90a"
  }
}
```

### `transaction.refund`

```json
{
  "event_type": "transaction.refund",
  "source_id": "104000",
  "timestamp": "2026-10-15T16:20:05.181Z",
  "payload": {
    "event_table_pk": 219000004,
    "mid": "800000000000",
    "trans_id": "076400000004",
    "card_number": "9400000000001111",
    "name_on_account": "Jane Example",
    "expiration": "202812",
    "auth_code": "PPS789",
    "response_text": "Approval",
    "response_code": "000",
    "tokenized_card": null,
    "order_id": "ORDER-1005",
    "amount": 45.00,
    "refunded_amount": 45.00,
    "transaction_timestamp": "2026-10-15T16:20:05.965+00:00",
    "trans_status_id": 6,
    "invoice_id": "INV-1005",
    "transact_source_id": 1,
    "source": "rmvt",
    "timestamp": "2026-10-15T16:20:05.089+00:00"
  },
  "metadata": {
    "webhook_id": "wh_600005",
    "attempt": 1,
    "idempotency_key": "2c3d4e5f60718293a4b5c6d7e8f90a1b"
  }
}
```

### `transaction.partialrefund`

```json
{
  "event_type": "transaction.partialrefund",
  "source_id": "104000",
  "timestamp": "2026-10-15T16:31:47.181Z",
  "payload": {
    "event_table_pk": 219000003,
    "mid": "800000000000",
    "trans_id": "076400000003",
    "card_number": "9400000000001111",
    "name_on_account": "Jane Example",
    "expiration": "202812",
    "auth_code": "PPS456",
    "response_text": "Approval",
    "response_code": "000",
    "tokenized_card": null,
    "order_id": "ORDER-1004",
    "amount": 99.99,
    "refunded_amount": 25.00,
    "transaction_timestamp": "2026-10-15T16:31:47.965+00:00",
    "trans_status_id": 63,
    "invoice_id": "INV-1004",
    "transact_source_id": 5,
    "source": "billing",
    "timestamp": "2026-10-15T16:31:47.089+00:00"
  },
  "metadata": {
    "webhook_id": "wh_600006",
    "attempt": 1,
    "idempotency_key": "3d4e5f60718293a4b5c6d7e8f90a1b2c"
  }
}
```

`refunded_amount` is the total refunded so far, not the amount of the latest refund.

## Payload Fields

| **Field**               | **Data Type** | **Comments**                                                                                           |
| ----------------------- | ------------- | ------------------------------------------------------------------------------------------------------ |
| `event_table_pk`        | Integer       | Run's ID for the transaction record.                                                                   |
| `mid`                   | String        | Merchant ID assigned by the processor.                                                                 |
| `trans_id`              | String        | Transaction ID from the gateway.                                                                       |
| `card_number`           | String        | The card as returned by the gateway. Always present. Never a raw card number.                          |
| `tokenized_card`        | String        | Stored-card token. Present only when the card is saved; usually `null`. Not the same as `card_number`. |
| `name_on_account`       | String        | Cardholder name.                                                                                       |
| `expiration`            | String        | Card expiration date.                                                                                  |
| `auth_code`             | String        | Authorization code. `null` if the transaction wasn't authorized.                                       |
| `response_text`         | String        | Gateway response message.                                                                              |
| `response_code`         | String        | Gateway response code.                                                                                 |
| `order_id`              | String        | Order ID sent with the transaction.                                                                    |
| `amount`                | Number        | Transaction amount.                                                                                    |
| `refunded_amount`       | Number        | Total refunded so far. `null` if nothing has been refunded.                                            |
| `transaction_timestamp` | String        | When the transaction took place (UTC).                                                                 |
| `trans_status_id`       | Integer       | The transaction's status when the event was sent. See [Status IDs](#status-ids).                       |
| `invoice_id`            | String        | Invoice ID sent with the transaction. `null` if there is none.                                         |
| `transact_source_id`    | Integer       | Numeric ID of the transaction's source. See [Sources](#sources).                                       |
| `source`                | String        | The transaction's source, such as `hpp` or `api`. See [Sources](#sources).                             |
| `timestamp`             | String        | When the event was created (UTC).                                                                      |

## Status IDs

Act on `event_type`, not `trans_status_id`. The status ID is there for reference. There is no "approved" status: an approved payment is one that reaches Authorized and moves on. The `transaction.decline` and `transaction.reject` events tell you when it didn't.

These are the statuses that send an event:

| **`trans_status_id`** | **Status**         | **Event Sent**                                                                           |
| --------------------- | ------------------ | ---------------------------------------------------------------------------------------- |
| 1                     | Authorized         | None of its own. Every new transaction sends `transaction.entered`, whatever its status. |
| 2                     | Batched            | `transaction.batched`                                                                    |
| 3                     | Captured           | `transaction.captured`                                                                   |
| 5                     | Processed          | `transaction.processed`                                                                  |
| 4                     | Declined           | `transaction.decline`                                                                    |
| 22                    | Rejected           | `transaction.reject`                                                                     |
| 7                     | Voided             | `transaction.voided`                                                                     |
| 6                     | Refunded           | `transaction.refund`                                                                     |
| 63                    | Partially Refunded | `transaction.partialrefund`                                                              |

`trans_status_id` can hold a value not listed here, for example on `transaction.entered`. Don't fail on one you don't recognize.