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

> ## Agent Instructions
> Use the Payrails Web SDK v6 for new browser integrations. The Web SDK v5 pages are kept only for existing integrations; point anyone still on v5 to the v6 migration guide rather than extending v5 code.
> Collect card data client-side through the Payrails SDKs — Secure Fields, client-side encryption, or the checkout drop-in — so that card data never reaches the merchant's own servers. Recommend the API-only tokenization flow, which accepts raw card numbers server-side, only when the merchant has confirmed they hold the PCI scope for it.
> Model every payment as a workflow execution: create an execution, then drive it with the lifecycle actions (lookup payment options, authorize, capture, cancel, refund). Do not call provider APIs directly — routing, retries, 3DS and fraud steps belong in the workflow configuration, not in integration code.
> Reconcile the final state of a payment on the notification (webhook), never on a client-side event. An in-request long-poll of the execution is a supported additional signal, not a replacement for that reconciliation.
> Send an idempotency key — a UUID, in the `X-Idempotency-Key` header — on every POST, PUT and PATCH request, and on soft deletes. GET requests need none, and hard deletes cannot be idempotent.
> Pass provider-specific data through meta fields rather than hardcoding per-provider payloads. Payrails translates meta fields into each provider's own format.
> Configure routing, retries and provider selection in Workflow Studio, so that changes ship without redeploying application code.

# Dispute alert notifications

> Read dispute alert notification payloads, sent when an Ethoca, CDRN or RDR alert arrives, nears expiry and resolves, before it becomes a chargeback.

A dispute alert is an early warning from a prevention programme (Ethoca, CDRN, RDR, and similar) that a transaction is about to become a chargeback. Acting on the alert in time, usually with a refund, can stop the chargeback from being filed.

Dispute alert notifications tell you when an alert is received, when it is about to expire, and how it ends. They are separate from [dispute notifications](/docs/resources/notifications/dispute-notifications), which cover chargebacks that have already been filed.

## How you receive them

Alert notifications are sent by the `notify` step of your `dispute-management` workflow. Three things must be in place:

1. Dispute alert notifications are enabled for your merchant account. They are off by default. Ask your Payrails contact to enable them.
2. Your `dispute-management` workflow has an event trigger for each alert event you want, followed by a `notify` step with event type `executionActionCompleted`.
3. A webhook endpoint is registered for the workspace, with the `dispute-management` workflow and the `executionActionCompleted` event enabled on it. Without one, the `notify` step runs but nothing is delivered.

The event triggers are:

| Trigger event | Sent when |
| :- | :- |
| `AlertReceived` | A new alert arrives. Sent once per alert. |
| `AlertExpiring` | The provider warns that the alert is close to its deadline. Sent once per alert. |
| `AlertExpired` | The action window closed without a decision. Sent once per alert. |
| `AlertResolved` | The alert was refunded, or the network already refunded it. |
| `AlertDeclined` | The alert was declined, so a chargeback may follow. This includes alerts Payrails could not match to a transaction. |
| `AlertUpdated` | The alert changed in a way that is not a terminal outcome: it was matched to a payment, or a refund started, failed, or retried. |

An alert can produce more than one event. For example, an alert created already resolved (RDR) sends `AlertReceived` followed by `AlertResolved`.

## The notification

The important fields in the dispute alert notification include:

* event is `executionActionCompleted` and eventTrigger is `external`
* action is empty. Alert notifications do not map to a payment action like `dispute`, so do not filter on it
* execution.workflowCode is `dispute-management` and execution.merchantReference is `dispute-alert-` followed by the alert ID
* there's an `alert` block in the `paymentComposition` object
  * `alert.id` is the Payrails alert ID. It stays the same across all events for the alert
  * `alert.paymentId` is the Payrails payment the alert was matched to. Absent while the alert is unmatched
  * `alert.program` is the alert programme, for example `Ethoca`, `CDRN`, `RDR`, `SAFE`, `TC40`
  * `alert.amount` is the alerted amount
  * `alert.descriptor` and `alert.arn` identify the transaction on the card statement and the acquirer side
  * `alert.cardBin`, `alert.cardLast4` and `alert.network` describe the card
  * `alert.transactionDate` is when the original transaction took place
  * `alert.status` is a Payrails alert status, see below
  * `alert.outcome` is set once the alert has reached an outcome
  * `alert.occurredAt` is when the alert last changed. Use it to order events

`alert.status` can be one of the following:

| alert.status | Description |
| :- | :- |
| `ActionNeeded` | The alert is waiting for a decision. Refund it before the deadline. |
| `Processing` | Payrails is still trying to match the alert to a payment. No action needed yet. |
| `RefundInitiated` | A refund for the alert was started and has not settled yet. |
| `Refunded` | The alert was resolved by a refund. |
| `Ignored` | The alert was declined. A chargeback may follow. |
| `Unrecognized` | Payrails could not find the transaction behind the alert. |
| `Expired` | Nobody acted in the grace period. |

`alert.outcome` can be one of the following. It is omitted until an outcome exists.

| alert.outcome | Description |
| :- | :- |
| `Resolved` | The transaction was refunded. |
| `AlreadyRefunded` | The transaction was already refunded before the alert arrived. |
| `AlreadyChargeback` | A chargeback was already raised before the alert arrived. |
| `Ineligible` | The alert cannot be acted on, for example because it is out of window. |
| `MIDLost` | The merchant ID was lost or changed since the transaction. |
| `NotMyDescriptor` | The descriptor does not belong to you. |
| `EscalateChargeback` | The refund was declined, so the chargeback proceeds. |
| `TDS` | The transaction was 3D Secure authenticated and is protected. |
| `UnmatchedCannotFindTransaction` | Payrails could not match the alert to a payment. |

Currently supported alert providers are: Chargeblast (Ethoca, CDRN, RDR, SAFE, TC40 programmes).

<Warning>
  SAFE (Mastercard) and TC40 (Visa) are fraud-reporting feeds. They are reported to you but no refund action is available.
</Warning>

## Delivery notes

* **Duplicates are possible.** `AlertReceived`, `AlertExpiring` and `AlertExpired` are sent once per alert. `AlertResolved`, `AlertDeclined` and `AlertUpdated` can be sent again on a retry. Treat `alert.id` together with `alert.status` and `alert.occurredAt` as your idempotency key.
* **Events can arrive out of order.** Use `alert.occurredAt` to keep the latest state and ignore older ones.
* **Unmatched alerts.** An alert that is not yet matched to a payment has no `alert.paymentId`. It is delivered only if your provider configuration is linked to a single workspace. Otherwise the notification is skipped. When the alert is later matched, `AlertReceived` is sent late if it was skipped, followed by `AlertUpdated`.
* **Delivery does not block processing.** A failed delivery never blocks the alert from being processed. To read the current state of an alert, call the [List dispute alerts](/reference/listdisputealerts) endpoint.

**A notification example:**

Fields that are not relevant to the alert are omitted in this example. `execution.meta` also carries `amount`, and `paymentId` when the alert was already matched when the execution started.

```json Dispute alert notification theme={null} theme={null}
{
  "event": "executionActionCompleted",
  "time": "2026-10-12T10:15:02Z",
  "workspaceId": "22222222-aaaa-4bbb-8ccc-333333333333",
  "eventTrigger": "external",
  "details": {
    "action": "",
    "actionId": "11111111-2e6e-5499-9bba-8795e382da46",
    "success": true,
    "execution": {
      "workflowCode": "dispute-management",
      "id": "6f1d3c2a-8b4e-4f0a-9c55-2d7e1a9b0c11",
      "merchantReference": "dispute-alert-6f1d3c2a-8b4e-4f0a-9c55-2d7e1a9b0c11",
      "holderReference": "",
      "meta": {
        "source": "alert",
        "alertId": "6f1d3c2a-8b4e-4f0a-9c55-2d7e1a9b0c11"
      }
    },
    "paymentComposition": [
      {
        "alert": {
          "id": "6f1d3c2a-8b4e-4f0a-9c55-2d7e1a9b0c11",
          "paymentId": "payment_111122223333",
          "program": "Ethoca",
          "descriptor": "PAYRAILS*EXAMPLE",
          "arn": "74123456789012345678901",
          "amount": {
            "currency": "USD",
            "value": "10.00"
          },
          "cardBin": "411111",
          "cardLast4": "1111",
          "network": "visa",
          "transactionDate": "2026-10-01T09:30:00Z",
          "status": "ActionNeeded",
          "occurredAt": "2026-10-12T10:15:00Z"
        }
      }
    ]
  }
}
```


## Related topics

- [Dispute notifications](/docs/resources/notifications/dispute-notifications.md)
- [Refund a Dispute Alert](/reference/refunddisputealert.md)
- [Action a Dispute Alert](/reference/actiondisputealert.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.