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

# Handling transaction timeout

As a merchant, the checkout experience defines the timeout for API requests. It is critical to have a robust strategy when a timeout occurs to ensure an accurate view of the transaction. A timeout doesn't automatically mean the payment or refund has failed. It simply means the connection was lost before a definitive response could be returned.

<Warning>
  It is highly recommended to use [webhooks](/guides/features/webhooks/overview) to get
  the transaction status combined with
  [idempotency](/guides/api/idempotent-requests) to safely retry the requests.
  If webhook is not feasible, the transaction status can be polled by calling
  [`GET /transactions`](/reference/transactions/get-transaction) endpoint using
  the exponential back-off strategy.
</Warning>

## Use webhooks to get the final status

The recommended approach to get the final status of a transaction or refund after a timeout is to listen for [webhooks](/guides/features/webhooks/overview).

Webhooks are automated messages sent to the system to provide real-time updates on events. When a transaction is successfully completed, a webhook notification is sent to the webhook endpoint. This is the recommended approach because it is:

* **Reliable**: It provides the definitive, final state of the transaction directly from the system.
* **Efficient**: It avoids the need to repeatedly poll the systems, reducing network traffic and load on servers.
* **Asynchronous**: The system can respond to the event in its own time without holding a connection open.

Webhooks are supported for all [transaction and refund events](/guides/features/webhooks/events). When a webhook is received, the order status in the system can be updated confidently.

## Idempotency: safely retrying requests

It's crucial to use idempotency when creating transactions to prevent duplicate actions in the case of a retry in case of a timeout. An [idempotent](/guides/api/idempotent-requests) request is one that can be sent multiple times without changing the result beyond the initial execution. `POST` requests for transactions can be made idempotent by providing a unique `Idempotency-Key` in the request header.

Please note, if a request is retried with the same `Idempotency-Key` but a different request body, the request fails with a `400 bad_request - Idempotency-Key already in use for a different request.` to prevent accidental misuse.

<Note>
  Currently, only the `POST /transactions` endpoint supports idempotent
  requests.
</Note>

## Alternative strategy: Polling with exponential back-off

If implementing webhooks is not feasible, the alternative is to poll for the [transaction](/reference/transactions/list-transactions) or [refund](/reference/transactions/list-transaction-refunds) status. This involves making a call to the `GET /transactions` endpoint to perform a transaction search with a combination of `external_identifier`, `created_at`, and `amount`. For refund, `GET /transactions/{transaction_id}/refunds` endpoint can be called.

To avoid overwhelming the systems, an exponential back-off strategy should be used. It works by progressively increasing the delay between retries to give the system time to recover and process the transaction. Adding a small, random delay (known as jitter) to the back-off timing to prevent many requests all retrying at the same time is also recommended.

## Exception process

While the systems, combined with webhook and polling strategies, are designed to resolve network timeout issues, some transactions may occasionally remain in an `unknown` state for an extended time.

If a transaction's status is still `unknown` after some time, a manual reconciliation needs to be initiated. Building a report to identify any transactions that have been in an `unknown` state for over 24 hours is recommended.

For resolution, please raise a support ticket with the team, providing the details such as `external_identifier`, `amount`, and `created_at` for each transaction requiring investigation.

## Common timeout scenarios

### Scenario 1: Timeout resolved by webhook

This is the scenario where the system receives the final transaction status asynchronously from webhook before the internal timeout period expires.

```mermaid theme={"system"}
sequenceDiagram
    actor Customer
    participant Merchant
    participant Gr4vy
    participant PaymentService as Payment Service

    Customer->>Merchant: Submit payment
    Merchant->>Gr4vy: POST /transactions with new Idempotency-key
    Merchant->>Merchant: Listen to webhooks
    Gr4vy->>PaymentService: Create transaction

    alt Service degradation on PaymentService
        Note over PaymentService: Unexpected service degradation
        PaymentService--xGr4vy: response
        Gr4vy--xMerchant: response
    else Service degradation on Gr4vy
        Note over Gr4vy: Unexpected service degradation
        PaymentService--xGr4vy: response
        Gr4vy--xMerchant: response
    end

    Note over Merchant: Merchants may set their own timeout.

    PaymentService->>Gr4vy: Webhook notification
    Gr4vy->>Gr4vy: Update transaction status
    Gr4vy->>Merchant: Webhook notification
    Merchant->>Merchant: Match webhook to payment.
    Merchant->>Merchant: Save payment status
    Merchant-->>Customer: response
```

In this flow the following steps are seen:

1. Initiate transaction: The server calls `POST /transactions` with a unique `Idempotency-Key` and `external_identifier`.
2. API call times out. From the perspective of the system, the transaction state is `unknown`. It's crucial not to assume the payment has failed. The system should continue waiting for a webhook to arrive.
3. Webhook received before the timeout period ends: the server's webhook listener receives a `transaction.*` event. Match the webhook event to the transaction using these fields: `external_identifier`, `created_at`, and `amount`. Save the transaction status from the webhook.
4. Before responding to the first API call, check the transaction status and display the final status from the webhook in the confirmation page (for example "Payment Successful").

### Scenario 2: Webhook received after the timeout period expires

Here, the webhook is received after the timeout period expires. The merchant needs to track the transaction that timed out and if necessary perform a reversal.

Here, the webhook is received after the timeout period expires. The merchant needs to track the transaction that timed out and if necessary perform a reversal.

```mermaid theme={"system"}
sequenceDiagram
  actor Customer as Customer
  participant Merchant as Merchant
  participant Gr4vy as Gr4vy
  participant PaymentService as Payment Service

  Customer ->> Merchant: Submit payment 1
  Merchant ->> Gr4vy: POST /transaction 1 with idempotency-key 1
  Merchant ->> Merchant: Listen to webhooks

  Gr4vy ->> PaymentService: Create transaction 1

  alt Service degradation on Payment Service
    Note over PaymentService: Unexpected service degradation
    PaymentService --x Gr4vy: response 1
    Gr4vy --x Merchant: response 1
    Merchant -->> Customer: Display a timeout error message
  else Service degradation on Gr4vy
    Note over Gr4vy: Unexpected service degradation
    PaymentService -->> Gr4vy: response 1
    Gr4vy --x Merchant: response 1
    Merchant -->> Customer: Display a timeout error message
  end

  Customer ->> Merchant: Submit payment 2
  Merchant ->> Gr4vy: POST /transactions 2 with new idempotency-key 2
  Gr4vy ->> PaymentService: Create transaction 2
  PaymentService -->> Gr4vy: response 2
  Gr4vy -->> Merchant: response 2
  Merchant -->> Customer: Transaction 2 response

  PaymentService ->> Gr4vy: Webhook notification 1

  Gr4vy ->> Merchant: Webhook notification 1

  Merchant ->> Merchant: Match webhook 1 to payment 1 (using external_identifier, amount, created_at).
  Merchant ->> Merchant: Update payment 1 status.

  alt Transaction 1 status was successful after transaction 2 was attempted
    Note over Merchant: If transaction 1 was successful, void/refund based on intent
    Merchant ->> Gr4vy: Void / Refund transaction 1
    Gr4vy ->> PaymentService: Void / refund transaction 1
    PaymentService -->> Gr4vy: response
    Gr4vy -->> Merchant: response
  end

  PaymentService ->> Gr4vy: Webhook notification 2
  Gr4vy ->> Merchant: Webhook notification 2
  Merchant ->> Merchant: Match webhook 2 to payment 2.
  Merchant ->> Merchant: Update payment 2 status.
```

In this flow the following steps are seen:

1. Initiate transaction: The server calls `POST /transactions` with a unique `Idempotency-Key` and `external_identifier`.
2. API call times out. From the perspective of the system, the transaction state is `unknown`. It's crucial not to assume the payment has failed. The system should continue waiting for a webhook to arrive.
3. Customer retries: For this scenario, assume the customer attempts to pay for the same order again by submitting a second transaction successfully. Here, it is critical that a new `Idempotency-Key (key-2)` is generated. The same `external_identifier` as the original attempt can be reused or a new `external_identifier` can be used.
4. Webhook for the first transaction arrives. Match the webhook event with the transaction by using these fields: `external_identifier`, `created_at`, and `amount`.
   * Save the transaction status from the webhook.
   * If the transaction was approved, submit a void or refund to make the customer good again.
5. Webhook for Transaction #2 arrives. Do nothing.

### Scenario 3: Resolving timeouts with polling

This scenario outlines how to handle a transaction timeout when webhooks cannot be used. The system must take on the responsibility of actively polling for the transaction's final status to resolve the order and prevent duplicate charges.

```mermaid theme={"system"}
sequenceDiagram
    actor Customer as Customer
    participant Merchant as Merchant
    participant Gr4vy as Gr4vy
    participant PaymentService as Payment Service

    Customer ->> Merchant: Submit payment 1
    Merchant ->> Gr4vy: POST /transaction 1 with idempotency-key 1
    Merchant ->> Merchant: Listen to webhooks

    Gr4vy ->> PaymentService: Create transaction 1

    alt Service degradation on Payment Service
        Note over PaymentService: Unexpected service degradation
        PaymentService --x Gr4vy: response 1
        Gr4vy --x Merchant: response 1
        Merchant -->> Customer: Display a timeout error message
    else Service degradation on Gr4vy
        Note over Gr4vy: Unexpected service degradation
        PaymentService -->> Gr4vy: response 1
        Gr4vy --x Merchant: response 1
        Merchant -->> Customer: Display a timeout error message
    end

    Customer ->> Merchant: Submit payment 2
    Merchant ->> Gr4vy: POST /transactions 2 with new idempotency-key 2
    Gr4vy ->> PaymentService: Create transaction 2
    PaymentService -->> Gr4vy: response 2
    Gr4vy -->> Merchant: response 2
    Merchant -->> Customer: Transaction 2 response

    loop Poll with exponential back-off to get the transaction 1 status
        Merchant->>Gr4vy: Get /transactions
        Gr4vy-->>Merchant: response
        Merchant->>Merchant: Update transaction 1 status
        Note over Merchant: Exit loop if transaction is no longer in progress
    end

    alt Transaction 1 status was successful after transaction 2 was attempted
        Note over Merchant: If transaction 1 was successful, void/refund based on intent
        Merchant ->> Gr4vy: Void / Refund transaction 1
        Gr4vy ->> PaymentService: Void / refund transaction 1
        PaymentService -->> Gr4vy: response
        Gr4vy -->> Merchant: response
    end
```

In this flow the following steps are seen:

1. Initiate transaction: The server calls `POST /transactions` with a unique `Idempotency-Key` and `external_identifier`.
2. API call times out. From the perspective of the system, the transaction state is `unknown`. It's crucial not to assume the payment has failed. The system should continue waiting for a webhook to arrive.
3. Customer retries: For this scenario, assume the customer attempts to pay for the same order again by submitting a second transaction successfully. Here, it is critical that a new `Idempotency-Key (key-2)` is generated. The same `external_identifier` as the original attempt can be reused or a new `external_identifier` can be used.
4. Background Polling: The background job, which is aware of the `unknown` state of the first transaction, periodically calls the `GET /transactions` endpoint to search for the transaction using a combination of `external_identifier`, `created_at`, and `amount` to determine its final status.
   * Save the transaction status from the webhook.
   * Submit a void or refund to reverse the transaction if required.

### Scenario 4: Handling refund, capture and void timeout using webhook

When timeout happens on merchant initiated requests such as a capture, refund, and void, the webhook can be listened to and matched using `transaction_id` and `event.name` (for example `refund.succeeded` or `transaction.authorized`).

```mermaid theme={"system"}
sequenceDiagram
    participant Merchant
    participant Gr4vy
    participant PaymentService as Payment Service

    Merchant->>Gr4vy: POST /capture (or /void, or /refunds)
    Merchant->>Merchant: Listen to webhooks
    Gr4vy->>PaymentService: Capture/void/refund transaction

    alt Service degradation on PaymentService
        Note over PaymentService: Unexpected service degradation
        PaymentService--xGr4vy: response
        Gr4vy--xMerchant: response
    else Service degradation on Gr4vy
        Note over Gr4vy: Unexpected service degradation
        PaymentService-->>Gr4vy: response
        Gr4vy--xMerchant: response
    end

    PaymentService->>Gr4vy: Webhook notification
    Gr4vy->>Merchant: Webhook notification
    Merchant->>Merchant: Update the transaction or refund status

```

In this flow the following steps are seen:

1. Initiate transaction: The server calls the void, capture, or refund APIs.
2. API call times out. From the perspective of the system, the action is `unknown`.
3. Webhook received after some time. The server's webhook listener receives a `transaction.*` or `refund.*` event. Match the webhook event to the transaction using `transaction_id` and `event.name`.
4. Save the status of capture, void or refund from the webhook (for example "Capture successful").

### Scenario 5: Handling refund, capture and void timeout with polling

When webhook is not an option, the responsibility of actively polling for the final status of capture, void, and refund must be taken.

```mermaid theme={"system"}
sequenceDiagram
    participant Merchant
    participant Gr4vy
    participant PaymentService as Payment Service

    Merchant->>Gr4vy: POST /capture (or /void, or /refunds)
    Merchant->>Merchant: Listen to webhooks
    Gr4vy->>PaymentService: Capture/void/refund transaction

    alt Service degradation on PaymentService
        Note over PaymentService: Unexpected service degradation
        PaymentService--xGr4vy: response
        Gr4vy--xMerchant: response
    else Service degradation on Gr4vy
        Note over Gr4vy: Unexpected service degradation
        PaymentService-->>Gr4vy: response
        Gr4vy--xMerchant: response
    end

    loop Poll with exponential back-off to get the transaction/refund status
        Merchant->>Gr4vy: Get /transactions (or /transactions/:id/refunds)
        Gr4vy-->>Merchant: response
        Merchant->>Merchant: Update transaction or refund status
        Note over Merchant: Exit loop if transaction/refund is no longer in progress
    end
```

In this flow the following steps are seen:

1. Initiate transaction: The server calls the void, capture, or refund APIs.
2. API call times out. From the perspective of the system, the action is `unknown`.
3. Background Polling: The background job, which is aware of the `unknown` state of the transaction, periodically calls the `GET /transactions` endpoint to search for the transaction using a combination of `external_identifier`, `created_at`, and `amount` to determine its final status. For refunds, use `GET /transactions/{transactionId}/refunds` endpoint.
4. The loop continues until the transaction or refund status moves from an indeterminate state like "in-progress" to a final state, such as approved, declined, or failed.
