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

# Payment Operation Failure

> Every operation you trigger on a payment deserves a clear answer. These webhooks deliver one instantly - so you can act before a missed capture or failed refund becomes a problem you discover too late.

Primer notifies you with a failure webhook whenever a payment operation attempt does not succeed and the payment status has not changed as a result. The following event types are supported:
- `PAYMENT.CAPTURE.FAILED` - a capture attempt did not succeed
- `PAYMENT.REFUND.FAILED` - a refund attempt did not succeed
- `PAYMENT.CANCELLATION.FAILED` - a cancellation attempt did not succeed
- `PAYMENT.AUTHORIZATION_ADJUSTMENT.FAILED` - an authorization adjustment attempt did not succeed

These events are operational signals, not status changes. Receiving a `PAYMENT.CAPTURE.FAILED` event, for example, means the capture attempt was unsuccessful - it does not mean the payment has failed or that the authorization has been lost.

**When does a failure webhook fire?** Common scenarios include:
- **Amount mismatch** - you attempt to capture £100, but the remaining authorised amount is only £80
- **PSP-side error** - the processor returns a technical error or the request times out before a response is received

> **Particularly useful with Primer Workflows.** If you trigger captures or refunds automatically through a Workflow, failure webhooks are the signal you need to keep your own systems in sync. Because the action happened upstream, your backend won't know it failed unless Primer tells you - these events close that gap.

A **webhook is emitted for every individual failed attempt**. If the same operation fails multiple times, you will receive a separate event each time.

The webhook payload is intentionally minimal. To **retrieve the full context** - including the error reason, timeline, and processor response - **call** `GET /payments/{id}?expand=transactions.events` after receiving the event.

**What to do when you receive a failure event**

The right action depends on the failure type: for amount mismatches, you may want to retry the capture with a corrected amount; for PSP errors or timeouts, a retry after a short delay is often appropriate. In all cases, make sure your internal systems reflect the outcome - update your order management, notify your operations team if needed, and avoid leaving the payment in an ambiguous state on your end.

**Testing failure webhooks in sandbox**

The easiest way to trigger each failure event in your sandbox environment is to force a condition that will cause the processor to reject the operation. Because the failures are real processor responses, no special flags or mock modes are needed — you just need to engineer the right conditions.
- **`PAYMENT.CAPTURE.FAILED`** — Authorise a payment, then attempt to capture an amount larger than what was authorised. The processor will reject the capture and the event will fire immediately.
- **`PAYMENT.REFUND.FAILED`** — Capture a payment, then refund it directly through your processor's dashboard before sending the refund request to Primer. Since the funds have already been returned at the processor level, Primer's refund attempt will fail and the event will fire.
- **`PAYMENT.CANCELLATION.FAILED`** — Authorise a payment, then capture it directly through your processor's dashboard (outside of Primer). When you then send a cancel request through Primer, the processor will reject it since the payment is no longer in a cancellable state, since the capture already happened.

In all cases, once you receive the webhook, you can retrieve the full error detail by calling `GET /payments/{id}?expand=transactions.events`. The `transactionEvent.id` in the webhook payload corresponds directly to the event entry in the response, where you will find the processor status reason and the full operation timeline.


<RequestExample>
  ```json Webhook theme={"dark"}
  Version - 2.4
  ```
</RequestExample>

<ResponseExample>
  ```json Example capture failed notification theme={"dark"}
  {
    "eventType": "PAYMENT.CAPTURE.FAILED",
    "date": "2026-02-19T15:36:16.367687",
    "notificationConfig": {
      "id": "cc51f9f0-7e1c-492b-9d37-f83a818f6070",
      "description": "Payment webhook"
    },
    "version": "2.4",
    "signedAt": "1689221338",
    "payment": {
      "id": "DdRZ6YY0",
      "amount": 3000,
      "currencyCode": "GBP",
      "orderId": "order-123"
    },
    "requestAmount": 1500,
    "transactionEvent": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    }
  }
  ```
</ResponseExample>

Learn more about [how Primer handles webhooks](/api-reference/get-started/configure-webhooks).


## OpenAPI

````yaml https://api.schemas.primer.io/api-reference/v2.4/webhooks.yaml post /payment-operation-failure
openapi: 3.0.2
info:
  x-logo:
    url: https://apidocs.primer.io/docs/assets/images/primer-logo.svg
  title: Primer Webhooks
  version: '2.4'
  description: Webhooks specifications for Primer API v2.4
servers:
  - url: https://api.sandbox.primer.io
  - url: https://api.primer.io
security: []
paths:
  /payment-operation-failure:
    post:
      tags:
        - Payment Webhooks
      summary: Payment Operation Failure
      description: >
        Every operation you trigger on a payment deserves a clear answer. These
        webhooks deliver one instantly - so you can act before a missed capture
        or failed refund becomes a problem you discover too late.


        Primer notifies you with a failure webhook whenever a payment operation
        attempt does not succeed and the payment status has not changed as a
        result. The following event types are supported:

        - `PAYMENT.CAPTURE.FAILED` - a capture attempt did not succeed

        - `PAYMENT.REFUND.FAILED` - a refund attempt did not succeed

        - `PAYMENT.CANCELLATION.FAILED` - a cancellation attempt did not succeed

        - `PAYMENT.AUTHORIZATION_ADJUSTMENT.FAILED` - an authorization
        adjustment attempt did not succeed


        These events are operational signals, not status changes. Receiving a
        `PAYMENT.CAPTURE.FAILED` event, for example, means the capture attempt
        was unsuccessful - it does not mean the payment has failed or that the
        authorization has been lost.


        **When does a failure webhook fire?** Common scenarios include:

        - **Amount mismatch** - you attempt to capture £100, but the remaining
        authorised amount is only £80

        - **PSP-side error** - the processor returns a technical error or the
        request times out before a response is received


        > **Particularly useful with Primer Workflows.** If you trigger captures
        or refunds automatically through a Workflow, failure webhooks are the
        signal you need to keep your own systems in sync. Because the action
        happened upstream, your backend won't know it failed unless Primer tells
        you - these events close that gap.


        A **webhook is emitted for every individual failed attempt**. If the
        same operation fails multiple times, you will receive a separate event
        each time.


        The webhook payload is intentionally minimal. To **retrieve the full
        context** - including the error reason, timeline, and processor response
        - **call** `GET /payments/{id}?expand=transactions.events` after
        receiving the event.


        **What to do when you receive a failure event**


        The right action depends on the failure type: for amount mismatches, you
        may want to retry the capture with a corrected amount; for PSP errors or
        timeouts, a retry after a short delay is often appropriate. In all
        cases, make sure your internal systems reflect the outcome - update your
        order management, notify your operations team if needed, and avoid
        leaving the payment in an ambiguous state on your end.


        **Testing failure webhooks in sandbox**


        The easiest way to trigger each failure event in your sandbox
        environment is to force a condition that will cause the processor to
        reject the operation. Because the failures are real processor responses,
        no special flags or mock modes are needed — you just need to engineer
        the right conditions.

        - **`PAYMENT.CAPTURE.FAILED`** — Authorise a payment, then attempt to
        capture an amount larger than what was authorised. The processor will
        reject the capture and the event will fire immediately.

        - **`PAYMENT.REFUND.FAILED`** — Capture a payment, then refund it
        directly through your processor's dashboard before sending the refund
        request to Primer. Since the funds have already been returned at the
        processor level, Primer's refund attempt will fail and the event will
        fire.

        - **`PAYMENT.CANCELLATION.FAILED`** — Authorise a payment, then capture
        it directly through your processor's dashboard (outside of Primer). When
        you then send a cancel request through Primer, the processor will reject
        it since the payment is no longer in a cancellable state, since the
        capture already happened.


        In all cases, once you receive the webhook, you can retrieve the full
        error detail by calling `GET /payments/{id}?expand=transactions.events`.
        The `transactionEvent.id` in the webhook payload corresponds directly to
        the event entry in the response, where you will find the processor
        status reason and the full operation timeline.
      operationId: payment_operation_failed_webhook_event
      parameters:
        - in: header
          name: X-Signature-Primary
          schema:
            type: string
          required: true
          description: >-
            An [HMAC signature](https://en.wikipedia.org/wiki/HMAC) generated
            using the webhook payload and a shared signing secret. This is then
            converted to a base64 encoded string.
        - in: header
          name: X-Signature-Secondary
          schema:
            type: string
          required: true
          description: >-
            A secondary signature that is added when you have rotated your
            secret within the past 24 hours.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentOperationFailedWebhookPayload'
      responses:
        '200':
          description: >-
            Return a 200 status to indicate that the data was received
            successfully
components:
  schemas:
    PaymentOperationFailedWebhookPayload:
      title: PaymentOperationFailedWebhookPayload
      type: object
      required:
        - eventType
        - date
        - signedAt
        - notificationConfig
        - payment
        - transactionEvent
      properties:
        eventType:
          title: Event Type
          type: string
          enum:
            - PAYMENT.CAPTURE.FAILED
            - PAYMENT.CANCELLATION.FAILED
            - PAYMENT.AUTHORIZATION_ADJUSTMENT.FAILED
            - PAYMENT.REFUND.FAILED
          description: The type of the webhook raised.
        date:
          title: Date
          type: string
          description: The date-time that the webhook was sent.
          format: date-time
        signedAt:
          title: Signed At
          type: string
          description: The Unix timestamp at which the webhook payload was signed.
          example: '1689221338'
        notificationConfig:
          title: Notification Config
          type: object
          required:
            - id
            - description
          description: The notification configuration details.
          properties:
            id:
              title: ID
              type: string
              description: The notification configuration ID.
              format: uuid
            description:
              title: Description
              type: string
              description: The notification configuration description.
        version:
          title: Version
          type: string
          description: The payload version.
          example: '2.4'
        payment:
          title: Payment Details
          type: object
          required:
            - id
            - amount
            - currencyCode
            - orderId
          properties:
            id:
              title: Payment identifier
              type: string
              description: >
                The unique payment ID.


                You can use this ID to retrieve the payment details, or perform
                downstream operations.
            amount:
              title: Payment amount
              type: integer
              format: int64
              description: The amount you charged the customer, in minor units.
            currencyCode:
              title: Currency
              type: string
              description: >
                The 3-letter currency code in [ISO 4217
                format](https://en.wikipedia.org/wiki/ISO_4217#Active_codes).

                e.g. use `USD` for US dollars.
            orderId:
              title: Order ID
              type: string
              description: Your reference for the payment.
        transactionEvent:
          title: Transaction Event
          type: object
          required:
            - id
          properties:
            id:
              title: Transaction Event ID
              type: string
              format: uuid
              description: >-
                The unique identifier for the transaction event associated with
                the failed operation.
        requestAmount:
          title: Request Amount
          type: integer
          format: int64
          description: >-
            The requested amount for the operation in minor units. Present for
            capture, refund, and authorization adjustment events. Not applicable
            for cancellation events.

````