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
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:
{
"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
| 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 |
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 |
received_at | When the master's change reached the engine |
engine_ms, execution_ms, total_ms | How long it took. See Timings |
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 |
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 |
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_minimumisskip, - the follower uses
risk_percentand the master's trade has no stop loss, - the trade is older than
max_delay_seconds, - or Equity Guard has copying paused.
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
| 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
While has_more is true, pass next_cursor back as cursor for the next, older page:
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);The examples use the att helper from the Quickstart. 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.
Followers
Attach accounts to a copier, set each one's rules for lot size, limits, direction, stops, symbols, symbol mapping and sessions, and read its live status.
Equity Guard
What Equity Guard's floors, daily loss limits and profit targets do, and how the followers it pauses, trades it skips and copies it closes show in the API.