# How to Prevent Duplicate MT5 Orders From Retries and Double Clicks

> Why a retry or a double click can open two MT5 positions, and how one request identifier — an idempotency key — makes every order safe to send again.

Published 2026-10-13 by fxapis. Canonical: https://fxapis.com/blog/prevent-duplicate-mt5-orders

## In short

- A duplicate happens when the same decision is sent twice: a double click, a retry after a timeout, a webhook delivered twice.
- The fix is one identifier per decision, sent with every attempt. fxapis calls it the Idempotency-Key and honours it for 24 hours.
- The same key returns the first result instead of placing a second order — even while the first is still running.
- Make the key when the decision is made, not when the request is sent. A new key on each retry protects nothing.

A user taps **Buy**. Nothing seems to happen, so they tap again. Two requests leave their phone, and they end up with two positions where they wanted one. **The fix is one identifier per decision, sent with every attempt — an idempotency key.**

## Where duplicates come from

The same decision gets sent more than once in ordinary ways:

- **A double click or double tap** on a slow screen.
- **A retry after a timeout.** Your request reached the broker, the answer did not reach you, and your code tried again.
- **A webhook delivered twice** by the system that sent it.
- **A queue job run twice** after a worker restarts.

The timeout is the hard one. When a request times out, you do not know whether the order was placed. Retrying risks a second position; not retrying risks none at all.

## One key per decision

Send an `Idempotency-Key` header with every order. fxapis remembers the key for 24 hours:

| Situation | What you get |
|---|---|
| Same key, same order, first call finished | The first response, replayed — no second order |
| Same key, first call still running | `409 IDEMPOTENCY_IN_FLIGHT` — try again shortly |
| Same key, a different order | `409 IDEMPOTENCY_KEY_REUSED` — refused |
| New key | A new order |

So a retry with the same key is always safe, whether or not the first attempt reached the broker.

## The double-click example

The key has to be made **when the user decides**, not when the request is sent. Make it once when the order screen opens or the button is first pressed, and reuse it for every send that follows:

```js
// One key per decision: created once, reused by every click and every retry.
const orderKey = crypto.randomUUID();

async function buy() {
  const res = await fetch("/api/orders", {
    method: "POST",
    headers: { "Content-Type": "application/json", "Idempotency-Key": orderKey },
    body: JSON.stringify({ symbol: "EURUSD", side: "buy", volume: "0.10" }),
  });
  return res.json();
}
```

Your backend passes the same key on to fxapis. Two taps send the same key, and the second gets the first one's result. Disabling the button helps too, but only the key also covers retries and timeouts.

The common mistake is generating the key inside the retry loop. A new key on every attempt tells the server each attempt is a new order — exactly what you were trying to prevent.

## Picking the key

A random UUID per decision works for most cases. When the decision has a natural identity, build the key from it, so the same decision always maps to the same key:

- A member taking a signal: `signal_9931:member_4821`.
- A copier following a master trade: master deal + follower account.
- A queued job: the job's own id.

Open, close and each partial close are separate decisions, with separate keys.

## The one result you must not retry

An idempotency key makes retries safe; it does not make every answer a reason to retry. If an order comes back `ORDER_UNRESOLVED`, fxapis does not know whether the broker executed it. **Do not resend it under a new key.** Poll the order — fxapis confirms it against the broker's records and moves it to what actually happened.

And when an order is rejected with `retryable: true` — the price moved — sending again is a new decision, so it gets a new key. Resending under the old key only replays the rejection.

## Next steps

- [Idempotent MT5 orders](https://docs.fxapis.com/guides/idempotency) — replays, the unknown outcome, key patterns for signals, copiers and queues, and a checklist.
- [The three failures](https://docs.fxapis.com/trading#the-three-failures) — which errors are safe to resend.
- [Copy trading](https://fxapis.com/solutions/copy-trading) — one order across many accounts, each with its own result and no duplicates.

## Questions

### What is an idempotency key?

A unique value you send with a request so the server can recognise a repeat. With fxapis, a market order, pending order, close or multi-account order sent again with the same Idempotency-Key within 24 hours returns the first answer and never places a second order.

### Should I generate a new idempotency key when I retry?

No. A retry must reuse the same key — that is what lets the server tell it is a retry. Use a new key only for a new decision, such as a new order after a retryable rejection.

### What if I send the same key with a different order?

fxapis refuses it with 409 IDEMPOTENCY_KEY_REUSED, so a key can never be applied to an order it was not made for.

### Does an idempotency key stop trades placed from the MetaTrader app?

No. It deduplicates requests sent to fxapis. Trades placed on the same account from MetaTrader or another platform are separate; they appear in the account's deals with no fxapis order behind them.
