# Copy events

> Read what the copier engine did on each follower, such as opens, closes, stop changes and skipped trades, with the terminal's reply and timings.

Copy events are the copier's log of what happened on each follower. Every time the copier engine acts on a follower, or decides not to, it records an event: a copy opened, closed in full or in part, its stop loss or take profit changed, a master trade skipped, or a copy recovered after a restart. Events are kept for 90 days. To read them, your API key needs the `copiers:read` scope.

## List a copier's events [#list-a-copiers-events]

```bash
curl "https://api.algotradertools.com/v1/copiers/c7d1e5f2-3a4b-4c6d-8e9f-0a1b2c3d4e5f/events?limit=20" \
  -H "Authorization: Bearer $ATT_API_KEY"
```

| Query parameter | Description                            |
| --------------- | -------------------------------------- |
| `follower_id`   | Only this follower's events            |
| `limit`         | 1 to 100 events per page, default 50   |
| `cursor`        | The `next_cursor` of the previous page |

Events come newest first, in the [list format](/docs/limits#pagination):

```json
{
  "object": "list",
  "data": [
    {
      "id": 48214,
      "object": "copy_event",
      "follower_id": "e2f4a6b8-c0d2-4e46-8a8c-9e0f1a2b3c4d",
      "account_id": "9b2e7d4c-1a3f-4c8e-b5d6-7e8f9a0b1c2d",
      "master_ticket": 3518820457,
      "action": "skip",
      "ok": true,
      "message": "Opened outside this follower's trading sessions.",
      "detail": { "symbol": "XAUUSD", "volume": 1.0, "side": "sell" },
      "received_at": "2026-10-25T21:04:12.559Z",
      "engine_ms": null,
      "execution_ms": null,
      "total_ms": null
    },
    {
      "id": 48213,
      "object": "copy_event",
      "follower_id": "e2f4a6b8-c0d2-4e46-8a8c-9e0f1a2b3c4d",
      "account_id": "9b2e7d4c-1a3f-4c8e-b5d6-7e8f9a0b1c2d",
      "master_ticket": 3518820446,
      "action": "open",
      "ok": true,
      "message": "Opened 0.48 XAUUSD (buy) at 2391.84.",
      "detail": {
        "symbol": "XAUUSD",
        "side": "buy",
        "volume": 0.48,
        "stopLoss": 2380.0,
        "takeProfit": null,
        "price": 2391.84,
        "retcode": 10009,
        "outcome": "applied",
        "ticket": 3518820991
      },
      "received_at": "2026-10-25T14:21:07.211Z",
      "engine_ms": 0.31,
      "execution_ms": 186.4,
      "total_ms": 186.7
    }
  ],
  "has_more": true,
  "next_cursor": "WyIyMDI2LTEwLTI1VDE0OjIxOjA3LjIxMTAwMCswMDowMCIsICI0ODIxMyJd"
}
```

## The copy event object [#the-copy-event-object]

| Field                                   | Description                                                                                       |
| --------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `id`                                    | The event's ID, an integer                                                                        |
| `follower_id`                           | The follower it happened on                                                                       |
| `account_id`                            | That follower's account                                                                           |
| `master_ticket`                         | The ticket of the master position it is about                                                     |
| `action`                                | What the engine did. See [Actions](#actions)                                                      |
| `ok`                                    | `false` when the action failed                                                                    |
| `message`                               | What happened, in a sentence: the size and price of an open, or why a trade was skipped or failed |
| `detail`                                | The request and the terminal's reply. See [Detail](#detail)                                       |
| `received_at`                           | When the master's change reached the engine                                                       |
| `engine_ms`, `execution_ms`, `total_ms` | How long it took. See [Timings](#timings)                                                         |

### Actions [#actions]

| `action`        | Meaning                                                                                                                                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `open`          | A copy of a new master position was opened or failed to open, or the follower's pending order filled and became the copy                                                                                        |
| `close`         | A copy was closed. Either the master's position closed, or the copy was closed on the follower account itself: by its stop loss or take profit, by hand, or by [Equity Guard](/docs/equity-guard#closed-copies) |
| `partial_close` | Part of a copy was closed, in proportion to what the master closed                                                                                                                                              |
| `modify`        | A copy's stop loss or take profit was changed to follow the master, or the follower's pending order was moved with the master's                                                                                 |
| `skip`          | A master trade wasn't copied, and `message` says why                                                                                                                                                            |
| `adopt`         | After a restart, the engine found a copy on the follower that it had no record of, and took it over                                                                                                             |
| `place`         | A pending order was placed on the follower for the master's, or placing it failed. See [Pending orders](/docs/followers#pending-orders)                                                                         |
| `cancel`        | The follower's pending order was cancelled. Either the master's order was cancelled, expired or filled without the follower's, or `copy_pending` was turned off                                                 |

A trade is skipped, for example, when:

* the symbol isn't one the follower copies,
* no follower symbol matches,
* the trade opens outside the follower's sessions,
* its size is below the broker's minimum and `below_minimum` is `skip`,
* the follower uses `risk_percent` and the master's trade has no stop loss,
* the trade is older than `max_delay_seconds`,
* or [Equity Guard](/docs/equity-guard#skipped-trades) has copying paused.

### Detail [#detail]

`detail` holds what was sent to the follower's terminal and what the terminal answered. Its keys depend on the action. For a skip, it describes the master's position instead. Some of the keys:

| Key                        | Description                                                            |
| -------------------------- | ---------------------------------------------------------------------- |
| `symbol`, `side`, `volume` | The symbol, `buy` or `sell`, and the lots                              |
| `type`                     | A pending order's type, such as `BuyLimit`                             |
| `price`                    | The price it was filled or closed at                                   |
| `ticket`                   | The copy's ticket on the follower                                      |
| `stopLoss`, `takeProfit`   | The stops sent with the order or the change                            |
| `retcode`, `outcome`       | The terminal's return code and its outcome, such as `applied`          |
| `timing`                   | Where the copy's time went outside the engine. See [Timings](#timings) |

### Timings [#timings]

| Field          | Measures                                                                                         |
| -------------- | ------------------------------------------------------------------------------------------------ |
| `engine_ms`    | From the master's change reaching the engine to the follower's order going out                   |
| `execution_ms` | From the order going out to the follower's terminal answering: mostly the broker's own execution |
| `total_ms`     | Both together                                                                                    |

All three are `null` for events that sent no order, such as skips. They don't include the time MetaTrader takes to report the master's trade. `detail.timing` adds that, and more, as far as the terminals report it:

| Key                             | Description                                                                                     |
| ------------------------------- | ----------------------------------------------------------------------------------------------- |
| `masterFilledAt`                | When the master's trade filled, in UTC                                                          |
| `masterSentAt`                  | When the master's terminal reported the trade                                                   |
| `masterBridgeMs`                | From `masterFilledAt` to `masterSentAt`                                                         |
| `masterToEngineMs`              | From the master's terminal sending the report to the report reaching the engine                 |
| `bridgeQueueMs`, `bridgeExecMs` | Inside the follower's terminal: the wait before it takes the order, then the broker's execution |
| `filledAt`                      | When the copy filled, in UTC                                                                    |
| `fillToFillMs`                  | From the master's fill to the copy's                                                            |

A key is there only when the terminals report the data it needs. An MT5 master's terminal often reports a trade's fill only after the copy is done. So `masterFilledAt`, `masterBridgeMs` and `fillToFillMs` can be added a few seconds after the event first appears. The brokers' clocks and the engine's clock differ by a few milliseconds, so figures measured across two clocks are accurate to a few milliseconds at best.

## Page through older events [#page-through-older-events]

While `has_more` is `true`, pass `next_cursor` back as `cursor` for the next, older page:

TypeScript:

```ts
const copierId = "c7d1e5f2-3a4b-4c6d-8e9f-0a1b2c3d4e5f";
let cursor: string | null = null;
do {
  const query = new URLSearchParams({ limit: "100", ...(cursor ? { cursor } : {}) });
  const page = await att("GET", `/copiers/${copierId}/events?${query}`);
  for (const event of page.data) {
    if (!event.ok) console.log(event.received_at, event.action, event.message);
  }
  cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
```

Python:

```python
copier_id = "c7d1e5f2-3a4b-4c6d-8e9f-0a1b2c3d4e5f"
cursor = None
while True:
    query = {"limit": 100, **({"cursor": cursor} if cursor else {})}
    page = att("GET", f"/copiers/{copier_id}/events?{urlencode(query)}")
    for event in page["data"]:
        if not event["ok"]:
            print(event["received_at"], event["action"], event["message"])
    if not page["has_more"]:
        break
    cursor = page["next_cursor"]
```

The examples use the `att` helper from the [Quickstart](/docs/quickstart#2-export-the-key). The Python one also needs `from urllib.parse import urlencode`.

To follow new events as they happen, poll the first page every few seconds and stop at the newest `id` you have already seen.

Removing a follower deletes its events, and deleting a copier deletes all of the copier's events.
