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

# Action updated notifications

> Get notified when an authorize, capture, cancel, or refund changes after its first result.

Payrails sends an `executionActionUpdated` notification when an action on an execution changes **after** you received its first result. The default `payment-acceptance` workflow sends it for four actions: **authorize**, **capture**, **cancel**, and **refund**.

Typical causes:

* The PSP sends a late result. For example, the PSP approves a payment that timed out synchronously.
* The PSP sends more notifications or data for an action Payrails already reported.
* Someone captures or refunds the payment outside Payrails, for example in the PSP dashboard.

Steps that run inside an action can change after their first result too: 3DS authentication, fraud score, and fraud update. The default workflow doesn't notify you about them. To get a notification, connect the step's Updated outcome to a [Notify step](/docs/orchestration/workflow-studio/actions.md#notify). The notification is still `executionActionUpdated`, and `details.action` names the action the step runs in, for example `authorize`.

> [!NOTE]
>
> Payrails delivers `executionActionUpdated` only when it's enabled for your notification endpoint. Make sure your endpoint accepts the new `event` value, then contact your Payrails representative to enable it.

## How it works

Payrails sends `executionActionCompleted` once, for the first result of an action. Every later notification on the same execution is `executionActionUpdated`. Payrails doesn't send `executionActionCompleted` again.

```mermaid theme={null}
sequenceDiagram
    participant M as Your server
    participant P as Payrails
    participant PSP as PSP
    M->>P: Request action (for example, authorize)
    P->>PSP: Forward request
    PSP-->>P: Synchronous result
    P-->>M: executionActionCompleted
    PSP-->>P: Later notification (late result, extra data or external action)
    P-->>M: executionActionUpdated
```

## Notification envelope

Every Payrails notification uses the same [envelope](/docs/resources/notifications). The `details` object has the same shape for `executionActionCompleted` and `executionActionUpdated`, so the parser you already use for completed actions also reads updates.

```json Example {2} theme={null}
{
  "event": "executionActionUpdated",
  "time": "2026-10-16T10:25:02.506295Z",
  "details": {
    "action": "authorize",
    "actionId": "725398d3-982f-5bfb-9131-bb1ac2a97636",
    "success": true
    // ...
  },
  "workspaceId": "<your-workspace-id>",
  "eventTrigger": "api"
}
```

**event** `string` *required*

The event type. `executionActionCompleted` for the first result of an action, `executionActionUpdated` for every notification after it.

**time** `string` *required*

When Payrails produced the event, in RFC 3339 format (UTC). Use it to order events: notifications can arrive out of order, and more than once.

**details** `object` *required*

The action payload. On an update, it carries the payment that changed.

**fields to capture**

**action** `string`

The action: `authorize`, `capture`, `cancel`, or `refund`.

**actionId** `string`

Identifies the action. The same `actionId` as an earlier notification means the update refers to that action. A new `actionId` means a new action.

**success** `boolean`

The current result of the action. An update can change it, for example from `false` to `true`.

**execution.id** `string`

The execution the action belongs to.

**paymentComposition\[].paymentId** `string`

The payment that changed.

**paymentComposition\[].operationType** `string`

The provider operation behind the result, for example `Authorize`, `AuthorizeNotification`, `CaptureNotification`, or `RefundNotification`.

**paymentComposition\[].operationResult** `string`

The result of that operation, for example `Success` or `Timeout`.

**workspaceId** `string` *required*

The workspace the execution belongs to.

**eventTrigger** `string` *required*

What started the event:

* `api`: requested through the Payrails API
* `action`: started by a workflow step, such as a delayed capture
* `external`: performed outside Payrails and reported by the PSP

## Tell notifications apart

Read `event`, `eventTrigger`, and `actionId` together.

| Situation | `event` | `eventTrigger` | `actionId` |
| - | - | - | - |
| First result of an action requested through Payrails | `executionActionCompleted` | `api` or `action` | New |
| A reported action changes later (late PSP result or extra data) | `executionActionUpdated` | As on the original | Same as the reported action |
| An action performed outside Payrails, for example in the PSP dashboard | `executionActionUpdated` | `external` | New |

## Handle the notification

**1. Accept the new event value**

Route `executionActionUpdated` to the same handler you use for `executionActionCompleted`. Keep [verifying the signature](/docs/orchestration/payment-acceptance/receive-notifications.md) and processing notifications idempotently.

**2. Match on details.actionId**

If you already have the `actionId`, update the state of that action. If the `actionId` is new and `eventTrigger` is `external`, record it as a new action that wasn't requested through Payrails.

**3. Order by time, not arrival**

Apply the most recent state using `time`, and ignore duplicates.

**4. Read the result again**

Check `details.success` and `paymentComposition[].operationResult`. An update can turn a failed authorization into an approved one. When `success` becomes `true`, fulfill the order, or refund it if your customer already saw the payment fail.

## Examples

Each example shows the execution events and a snippet of the fields to capture from each notification. Snippets show only those fields. The full `details` object follows the standard [execution action notification structure](/docs/resources/notifications#structure-of-the-details-object).

**Authorize**

The PSP didn't respond to the authorization in time, so Payrails reported it as failed. Three seconds later the PSP sent a notification saying the payment was paid. Payrails updated the same authorize action and sent `executionActionUpdated` with `success: true`.

| # | Execution event | Action ID | Notification |
| - | - | - | - |
| 2 | Authorize updated | `725398d3-982f-5bfb-9131-bb1ac2a97636` | `executionActionUpdated` |
| 1 | Authorize completed (`workflow.action.failed`) | `725398d3-982f-5bfb-9131-bb1ac2a97636` | `executionActionCompleted` |
| | Authorize requested | `725398d3-982f-5bfb-9131-bb1ac2a97636` | – |

```json 1 · Completed (timeout) {2,6,7,15,24} theme={null}
{
  "event": "executionActionCompleted",
  "time": "2025-05-26T08:07:40Z",
  "details": {
    "action": "authorize",
    "actionId": "725398d3-982f-5bfb-9131-bb1ac2a97636",
    "success": false,
    "execution": {
      "id": "d6042316-b599-5cbf-b0c4-cffcdef354b3"
    },
    "paymentComposition": [
      {
        "paymentId": "7149c86c-ba38-4d0e-b3df-45effb63a8d8",
        "operationType": "Authorize",
        "operationResult": "Timeout",
        "errors": [
          {
            "code": "workflow.action.failed"
          }
        ]
      }
    ]
  },
  "eventTrigger": "api"
}
```

```json 2 · Updated (approved) {2,6,7,15,19} theme={null}
{
  "event": "executionActionUpdated",
  "time": "2025-05-26T08:07:43Z",
  "details": {
    "action": "authorize",
    "actionId": "725398d3-982f-5bfb-9131-bb1ac2a97636",
    "success": true,
    "execution": {
      "id": "d6042316-b599-5cbf-b0c4-cffcdef354b3"
    },
    "paymentComposition": [
      {
        "paymentId": "7149c86c-ba38-4d0e-b3df-45effb63a8d8",
        "operationType": "AuthorizeNotification",
        "operationResult": "Success"
      }
    ]
  },
  "eventTrigger": "api"
}
```

The update also carries the PSP's message, "The payment was paid.", in `paymentComposition[].operationResultProviderDetails`.

**Cancel**

The PSP confirmed the cancel synchronously, then sent two more notifications about the same payment. Both arrive as `executionActionUpdated`. The first updates the existing cancel action. The second, which the PSP sent as a refund notification, reports a new cancel action.

| # | Execution event | Action ID | Notification |
| - | - | - | - |
| 3 | Cancel updated | `9d27b532-be43-453c-a642-055f1176a524` | `executionActionUpdated` (external) |
| 2 | Cancel updated | `2f10afe9-90f6-5d63-be1d-21c4e9862701` | `executionActionUpdated` |
| 1 | Cancel completed | `2f10afe9-90f6-5d63-be1d-21c4e9862701` | `executionActionCompleted` |
| | Cancel requested | `2f10afe9-90f6-5d63-be1d-21c4e9862701` | – |

```json 1 · Completed {2,6,19} theme={null}
{
  "event": "executionActionCompleted",
  "time": "2025-05-22T13:33:57Z",
  "details": {
    "action": "cancel",
    "actionId": "2f10afe9-90f6-5d63-be1d-21c4e9862701",
    "success": true,
    "execution": {
      "id": "5b41378f-b95e-5ab0-8d4b-b41c5c020239"
    },
    "paymentComposition": [
      {
        "paymentId": "e966e689-1ff3-4f11-8331-5dfb32ff10ff",
        "operationType": "Cancel",
        "operationResult": "Success"
      }
    ]
  },
  "eventTrigger": "api"
}
```

```json 2 · Updated {2,6,19} theme={null}
{
  "event": "executionActionUpdated",
  "time": "2025-05-22T13:33:57Z",
  "details": {
    "action": "cancel",
    "actionId": "2f10afe9-90f6-5d63-be1d-21c4e9862701",
    "success": true,
    "execution": {
      "id": "5b41378f-b95e-5ab0-8d4b-b41c5c020239"
    },
    "paymentComposition": [
      {
        "paymentId": "e966e689-1ff3-4f11-8331-5dfb32ff10ff",
        "operationType": "Cancel",
        "operationResult": "Success"
      }
    ]
  },
  "eventTrigger": "api"
}
```

```json 3 · Updated (new action) {2,6,14,19} theme={null}
{
  "event": "executionActionUpdated",
  "time": "2025-05-22T13:34:02Z",
  "details": {
    "action": "cancel",
    "actionId": "9d27b532-be43-453c-a642-055f1176a524",
    "success": true,
    "execution": {
      "id": "5b41378f-b95e-5ab0-8d4b-b41c5c020239"
    },
    "paymentComposition": [
      {
        "paymentId": "e966e689-1ff3-4f11-8331-5dfb32ff10ff",
        "operationType": "RefundNotification",
        "operationResult": "Success"
      }
    ]
  },
  "eventTrigger": "external"
}
```

> [!NOTE]
>
> Notifications 1 and 2 carry identical `details`; only `event` differs. Treat 2 as a confirmation of the cancel you already recorded. Notification 3 has its own `actionId` and `eventTrigger: external`, so record it as a separate cancel.

**Capture and refund**

Payrails authorized the payment. Someone then captured and refunded it in the PSP dashboard, and Payrails learned about both actions from PSP notifications. The execution already had its completed notification for the authorization, so both arrive as `executionActionUpdated` with `eventTrigger: external` and a new `actionId`.

| # | Execution event | Action ID | Notification |
| - | - | - | - |
| 2 | Refund updated | `b81c503b-bdc0-499a-b6c5-b5a9163b4ba3` | `executionActionUpdated` (external) |
| 1 | Capture updated | `ff9ec8eb-64d8-40c4-9e03-367f22bbc464` | `executionActionUpdated` (external) |
| | Authorize completed | `a28bbec0-8608-59d8-…` | `executionActionCompleted` |

```json 1 · Capture (external) {2,5,6,19} theme={null}
{
  "event": "executionActionUpdated",
  "time": "2025-05-21T16:10:56Z",
  "details": {
    "action": "capture",
    "actionId": "ff9ec8eb-64d8-40c4-9e03-367f22bbc464",
    "success": true,
    "execution": {
      "id": "bb7ae804-c63f-5256-82c8-3de76d6b074f"
    },
    "paymentComposition": [
      {
        "paymentId": "7bbfcb21-30fb-4f38-82ee-a66b31312cc7",
        "operationType": "CaptureNotification",
        "operationResult": "Success"
      }
    ]
  },
  "eventTrigger": "external"
}
```

```json 2 · Refund (external) {2,5,6,19} theme={null}
{
  "event": "executionActionUpdated",
  "time": "2025-05-21T16:11:30Z",
  "details": {
    "action": "refund",
    "actionId": "b81c503b-bdc0-499a-b6c5-b5a9163b4ba3",
    "success": true,
    "execution": {
      "id": "bb7ae804-c63f-5256-82c8-3de76d6b074f"
    },
    "paymentComposition": [
      {
        "paymentId": "7bbfcb21-30fb-4f38-82ee-a66b31312cc7",
        "operationType": "RefundNotification",
        "operationResult": "Success"
      }
    ]
  },
  "eventTrigger": "external"
}
```

> [!NOTE]
>
> Record each as a new action: it has its own `actionId` and `eventTrigger: external`, even though the event is `executionActionUpdated`. If the PSP later changes either result, another `executionActionUpdated` follows with the same `actionId`.


## Related topics

- [Notifications](/docs/resources/notifications.md)
- [Actions](/docs/orchestration/workflow-studio/actions.md)
- [Dispute alert notifications](/docs/resources/notifications/dispute-alert-notifications.md)


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