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

# Verification recommendations

> Polling intervals, timeouts, hybrid strategy, and handling pending transactions — best practices for reliable tracking.

This page gathers the operational best practices to verify the status of LigdiCash transactions reliably, without overloading the API or leaving orphan transactions behind.

## Recommended polling interval

The optimal interval depends on the type of transaction:

| Flow                     | Recommended interval | Reason                                                                           |
| ------------------------ | -------------------- | -------------------------------------------------------------------------------- |
| Payin (hosted or direct) | **3 to 5 seconds**   | Operator confirmation often arrives within seconds after the customer's action   |
| Payout                   | **30 to 60 seconds** | Payouts are processed in batches by the operator — polling too fast is pointless |

<Warning>
  Do not go below 3 seconds between two `confirm` calls. Overly aggressive polling can trigger rate limits on the LigdiCash side and degrade the reliability of your integrations.
</Warning>

## Timeouts — when to stop polling

Set a maximum number of attempts rather than an absolute delay: this gives you precise control over the number of API calls generated.

| Flow   | Recommended max attempts | Approx. total duration |
| ------ | ------------------------ | ---------------------- |
| Payin  | 10 to 15                 | 40 to 75 seconds       |
| Payout | 10                       | 5 to 10 minutes        |

After the timeout, the transaction technically remains `pending` on the LigdiCash side — it is not cancelled. The callback may still arrive. Apply the following logic:

1. **Mark the transaction as `expired` in your database** (application-level status, not LigdiCash).
2. **Inform the user** that the processing is in progress and that they will be notified.
3. **Keep listening for the callback** — if it arrives, re-verify with `confirm` and update.

## Hybrid strategy — primary callback + fallback polling

```
1. Create the transaction → store the token and status = "pending"
2. Configure a callback_url reachable from the internet
3. On callback receipt:
     → Call confirm with the creation token
     → Update your database
4. If the callback does not arrive after 2 minutes:
     → Trigger a fallback polling (async job)
     → 10 attempts, every 5 seconds
5. If polling timeout:
     → Mark "expired" in the database
     → Keep processing the callback if it arrives late
```

<Tip>
  Trigger the fallback polling from a background job (cron, queue worker) — not from the initial HTTP request thread. This avoids blocking the user during the wait.
</Tip>

## Handling `pending` transactions

A transaction stays `pending` until the operator has made its final decision. Several situations explain a prolonged `pending`:

* **Approval mode (Moov Africa)** — the customer must confirm on their mobile app, which can take several minutes.
* **Expired OTP** — the customer did not enter the OTP in time. The transaction will stay `pending` then move to `notcompleted` after expiration.
* **Operator network congestion** — rare but possible during traffic spikes.
* **Payout awaiting funds** — if the merchant account balance is insufficient at the moment of initiation.

For every transaction still `pending` after your polling timeout, keep the token in the database and schedule a deferred re-verification (e.g. +30 min, +2h, +24h) before considering it definitively lost.

```javascript JavaScript — deferred re-verification theme={null}
async function reCheckExpiredTransaction(token) {
  const res = await fetch(
    `https://app.ligdicash.com/pay/v01/redirect/checkout-invoice/confirm/?invoiceToken=${token}`,
    {
      headers: {
        Apikey: process.env.LIGDICASH_API_KEY,
        Authorization: `Bearer ${process.env.LIGDICASH_API_TOKEN}`,
        Accept: "application/json",
      },
    }
  );
  const data = await res.json();

  if (data.status === "completed") {
    await updateOrder(token, "paid");
  } else if (data.status === "notcompleted") {
    await updateOrder(token, "failed");
  }
  // If still "pending", leave the scheduled job for the next check
}
```

## Related pages

* [Polling vs callback](/en/payment-api/status-verification/polling-vs-callback) — choosing the right strategy
* [Callback — security](/en/payment-api/callback/security) — re-verification with `confirm`
* [Callback — idempotency](/en/payment-api/callback/idempotency) — deduplicating the two requests
* [Validation modes](/en/payment-api/direct-payin/validation-modes) — USSD OTP, SMS OTP, approval
* [Response codes and statuses](/en/concepts/response-codes-and-statuses) — full reference
