# Quickstart

> Create an API key, connect a master and a follower account, create a copier and attach the follower, then check that it's copying.

This guide takes about ten minutes, most of it waiting for two MetaTrader terminals to start. You will connect two accounts, copy the trades of one to the other, and read back the follower's status and the copier's events.

You need a plan with at least two account slots and two MetaTrader 4 or 5 accounts: one whose trades are copied (the master) and one that copies them (the follower). Demo accounts work.

The examples use cURL with [jq](https://jqlang.org), TypeScript with the built-in `fetch` of Node.js 18 or later, and Python with the `requests` package.

## 1. Create an API key [#1-create-an-api-key]

1. Sign in at [app.algotradertools.com](https://app.algotradertools.com).
2. Open **API keys** in the sidebar and create a key. Leave all four scopes selected.
3. Copy the key. It is shown only once.

## 2. Export the key [#2-export-the-key]

Keep the key out of your source code. The examples read it from an environment variable:

```bash
export ATT_API_KEY="att_…"
```

The TypeScript and Python examples share a small helper that sends the key, encodes JSON and turns error responses into exceptions. Save it next to your script:

TypeScript:

```ts
// att.ts
const API = "https://api.algotradertools.com/v1";

export async function att(method: string, path: string, body?: unknown) {
  const response = await fetch(`${API}${path}`, {
    method,
    headers: {
      Authorization: `Bearer ${process.env.ATT_API_KEY}`,
      ...(body === undefined ? {} : { "Content-Type": "application/json" }),
    },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  if (response.status === 204) return null;
  const payload = await response.json();
  if (!response.ok) {
    throw new Error(`${payload.code}: ${payload.detail} (request ${payload.request_id})`);
  }
  return payload;
}
```

Python:

```python
# att.py
import os

import requests

API = "https://api.algotradertools.com/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['ATT_API_KEY']}"


def att(method, path, body=None):
    response = session.request(method, f"{API}{path}", json=body, timeout=30)
    if response.status_code == 204:
        return None
    payload = response.json()
    if not response.ok:
        raise RuntimeError(f"{payload['code']}: {payload['detail']} (request {payload['request_id']})")
    return payload
```

Put the TypeScript examples in a file such as `quickstart.ts` and run it with `npx tsx quickstart.ts`.

## 3. Connect the master and the follower [#3-connect-the-master-and-the-follower]

Connect the master with its **investor** (read-only) password, since its trades only need to be read. The follower receives trades, so it needs its **master** (trading) password. Each password is stored encrypted with the terminal that runs the account and is never shown again.

cURL:

```bash
curl https://api.algotradertools.com/v1/accounts \
  -H "Authorization: Bearer $ATT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "mt5",
    "server": "ICMarketsSC-Demo",
    "login": 51234567,
    "password": "<investor password>",
    "credential_kind": "investor",
    "nickname": "Gold master"
  }'

curl https://api.algotradertools.com/v1/accounts \
  -H "Authorization: Bearer $ATT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "platform": "mt5",
    "server": "ICMarketsSC-Demo",
    "login": 51239876,
    "password": "<master password>",
    "credential_kind": "master"
  }'
```

TypeScript:

```ts
import { att } from "./att.ts";

const master = await att("POST", "/accounts", {
  platform: "mt5",
  server: "ICMarketsSC-Demo",
  login: 51234567,
  password: process.env.MASTER_INVESTOR_PASSWORD,
  credential_kind: "investor",
  nickname: "Gold master",
});
const follower = await att("POST", "/accounts", {
  platform: "mt5",
  server: "ICMarketsSC-Demo",
  login: 51239876,
  password: process.env.FOLLOWER_PASSWORD,
  credential_kind: "master",
});
console.log(master.id, follower.id);
```

Python:

```python
import os

from att import att

master = att("POST", "/accounts", {
    "platform": "mt5",
    "server": "ICMarketsSC-Demo",
    "login": 51234567,
    "password": os.environ["MASTER_INVESTOR_PASSWORD"],
    "credential_kind": "investor",
    "nickname": "Gold master",
})
follower = att("POST", "/accounts", {
    "platform": "mt5",
    "server": "ICMarketsSC-Demo",
    "login": 51239876,
    "password": os.environ["FOLLOWER_PASSWORD"],
    "credential_kind": "master",
})
print(master["id"], follower["id"])
```

Each request returns `201 Created` with the new account, still in `connecting`:

```json
{
  "id": "4f6c2a1e-8b3d-4e7a-9c5f-1d2e3a4b5c6d",
  "object": "account",
  "name": "Gold master",
  "nickname": "Gold master",
  "platform": "mt5",
  "server": "ICMarketsSC-Demo",
  "login": 51234567,
  "credential_kind": "investor",
  "can_trade": false,
  "status": "connecting",
  "error": null,
  "released": false,
  "role": null,
  "figures": null,
  "created_at": "2026-10-25T14:03:51.204Z"
}
```

Copy the server name exactly as MetaTrader shows it in its login window. A wrong server name fails with `400 server_not_found`, and a wrong password with `400 invalid_credentials`.

## 4. Wait until both are connected [#4-wait-until-both-are-connected]

Each terminal starts and logs in to the broker, which takes two to three minutes. Check the account every 15 seconds or less often until its `status` is `connected`. If it turns to `error` instead, the `error` field says why.

cURL:

```bash
MASTER_ID=4f6c2a1e-8b3d-4e7a-9c5f-1d2e3a4b5c6d
FOLLOWER_ACCOUNT_ID=9b2e7d4c-1a3f-4c8e-b5d6-7e8f9a0b1c2d

for id in $MASTER_ID $FOLLOWER_ACCOUNT_ID; do
  until [ "$(curl -s https://api.algotradertools.com/v1/accounts/$id \
    -H "Authorization: Bearer $ATT_API_KEY" | jq -r .status)" != "connecting" ]; do
    sleep 15
  done
done
```

TypeScript:

```ts
async function connected(id: string) {
  for (;;) {
    const account = await att("GET", `/accounts/${id}`);
    if (account.status === "connected") return account;
    if (account.status !== "connecting") throw new Error(`${account.name}: ${account.error}`);
    await new Promise((resolve) => setTimeout(resolve, 15_000));
  }
}

await Promise.all([connected(master.id), connected(follower.id)]);
```

Python:

```python
import time


def connected(account_id):
    while True:
        account = att("GET", f"/accounts/{account_id}")
        if account["status"] == "connected":
            return account
        if account["status"] != "connecting":
            raise RuntimeError(f"{account['name']}: {account['error']}")
        time.sleep(15)


connected(master["id"])
connected(follower["id"])
```

## 5. Create a copier [#5-create-a-copier]

A copier watches its master and copies the master's trades to its followers. Create one for the master you just connected:

cURL:

```bash
curl https://api.algotradertools.com/v1/copiers \
  -H "Authorization: Bearer $ATT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Gold strategy", "master_id": "'$MASTER_ID'" }'
```

TypeScript:

```ts
const copier = await att("POST", "/copiers", { name: "Gold strategy", master_id: master.id });
```

Python:

```python
copier = att("POST", "/copiers", {"name": "Gold strategy", "master_id": master["id"]})
```

```json
{
  "id": "c7d1e5f2-3a4b-4c6d-8e9f-0a1b2c3d4e5f",
  "object": "copier",
  "name": "Gold strategy",
  "is_active": true,
  "master_id": "4f6c2a1e-8b3d-4e7a-9c5f-1d2e3a4b5c6d",
  "master": {
    "id": "4f6c2a1e-8b3d-4e7a-9c5f-1d2e3a4b5c6d",
    "name": "Gold master",
    "platform": "mt5",
    "server": "ICMarketsSC-Demo",
    "login": 51234567,
    "status": "connected"
  },
  "followers": [],
  "open_copies": 0,
  "created_at": "2026-10-25T14:08:30.512Z",
  "updated_at": "2026-10-25T14:08:30.512Z"
}
```

## 6. Attach the follower [#6-attach-the-follower]

Attach the follower account and give it the rules it should copy with. The follower below sizes its lots by its equity compared with the master's, trades at most 2 lots per copy, and only copies `XAUUSD` on weekdays from 07:00 to 21:00 London time. Every rule you leave out keeps its [default](/docs/followers#rules).

cURL:

```bash
COPIER_ID=c7d1e5f2-3a4b-4c6d-8e9f-0a1b2c3d4e5f

curl https://api.algotradertools.com/v1/copiers/$COPIER_ID/followers \
  -H "Authorization: Bearer $ATT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "'$FOLLOWER_ACCOUNT_ID'",
    "lot_mode": "equity_ratio",
    "lot_value": 1,
    "max_lot": 2,
    "symbol_filter": "include",
    "symbols": ["XAUUSD"],
    "sessions": [{ "days": [0, 1, 2, 3, 4], "start": "07:00", "end": "21:00" }],
    "timezone": "Europe/London"
  }'
```

TypeScript:

```ts
const attached = await att("POST", `/copiers/${copier.id}/followers`, {
  account_id: follower.id,
  lot_mode: "equity_ratio",
  lot_value: 1,
  max_lot: 2,
  symbol_filter: "include",
  symbols: ["XAUUSD"],
  sessions: [{ days: [0, 1, 2, 3, 4], start: "07:00", end: "21:00" }],
  timezone: "Europe/London",
});
```

Python:

```python
attached = att("POST", f"/copiers/{copier['id']}/followers", {
    "account_id": follower["id"],
    "lot_mode": "equity_ratio",
    "lot_value": 1,
    "max_lot": 2,
    "symbol_filter": "include",
    "symbols": ["XAUUSD"],
    "sessions": [{"days": [0, 1, 2, 3, 4], "start": "07:00", "end": "21:00"}],
    "timezone": "Europe/London",
})
```

The response is the new follower, with every rule filled in. Its `status` is `null` until the copier engine first reports on it, usually within a few seconds:

```json
{
  "id": "e2f4a6b8-c0d2-4e46-8a8c-9e0f1a2b3c4d",
  "object": "follower",
  "copier_id": "c7d1e5f2-3a4b-4c6d-8e9f-0a1b2c3d4e5f",
  "account_id": "9b2e7d4c-1a3f-4c8e-b5d6-7e8f9a0b1c2d",
  "account": {
    "id": "9b2e7d4c-1a3f-4c8e-b5d6-7e8f9a0b1c2d",
    "name": "MT5 51239876",
    "platform": "mt5",
    "server": "ICMarketsSC-Demo",
    "login": 51239876,
    "status": "connected"
  },
  "is_active": true,
  "magic": 1840273951,
  "lot_mode": "equity_ratio",
  "lot_value": 1,
  "adjust_contract_size": true,
  "below_minimum": "minimum",
  "max_lot": 2,
  "reverse": false,
  "copy_stops": true,
  "max_slippage_pips": null,
  "max_delay_seconds": 60,
  "copy_existing": false,
  "symbol_filter": "include",
  "symbols": ["XAUUSD"],
  "sessions": [{ "days": [0, 1, 2, 3, 4], "start": "07:00", "end": "21:00" }],
  "timezone": "Europe/London",
  "symbol_map": {},
  "status": null,
  "created_at": "2026-10-25T14:09:02.118Z",
  "updated_at": "2026-10-25T14:09:02.118Z"
}
```

Positions the master already had open aren't copied unless you set `copy_existing`. Without it, only what the master opens from now on is copied.

## 7. Check that it's copying [#7-check-that-its-copying]

Read the copier. Each of its followers has a live `status` from the copier engine:

cURL:

```bash
curl https://api.algotradertools.com/v1/copiers/$COPIER_ID \
  -H "Authorization: Bearer $ATT_API_KEY" | jq '.followers[] | {account: .account.name, status}'
```

TypeScript:

```ts
const current = await att("GET", `/copiers/${copier.id}`);
for (const f of current.followers) console.log(f.account.name, f.status);
```

Python:

```python
current = att("GET", f"/copiers/{copier['id']}")
for f in current["followers"]:
    print(f["account"]["name"], f["status"])
```

```json
{
  "account": "MT5 51239876",
  "status": {
    "state": "copying",
    "message": "",
    "master_connected": true,
    "follower_connected": true,
    "open_copies": 0,
    "last_event_at": null,
    "balance": 5000.0,
    "equity": 5031.2,
    "currency": "USD",
    "heartbeat_at": "2026-10-25T14:09:20.004Z",
    "engine_online": true
  }
}
```

Now open a gold trade on the master. Within a second or so, the copy shows up in the copier's events, which are listed newest first:

cURL:

```bash
curl "https://api.algotradertools.com/v1/copiers/$COPIER_ID/events?limit=5" \
  -H "Authorization: Bearer $ATT_API_KEY"
```

TypeScript:

```ts
const events = await att("GET", `/copiers/${copier.id}/events?limit=5`);
```

Python:

```python
events = att("GET", f"/copiers/{copier['id']}/events?limit=5")
```

```json
{
  "object": "list",
  "data": [
    {
      "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": false,
  "next_cursor": null
}
```

The master traded 1 lot with 10,318.40 USD equity. The follower has 5,031.20 USD equity, so it traded 1 × 5,031.20 ÷ 10,318.40 = 0.4876 lots, rounded down to the broker's lot step. That gives 0.48 lots.

> **If a request fails**
>
> Errors are JSON problem details with a `code` such as `invalid_api_key`, `plan_limit_reached` or `validation_failed`. For `validation_failed`, `errors` names each field that needs fixing. See [Errors](/docs/errors) for every code.

## 8. Pause, resume and clean up [#8-pause-resume-and-clean-up]

* To pause one follower, send `{ "is_active": false }` with `PATCH /v1/copiers/{copier_id}/followers/{follower_id}`. To pause the whole copier, send the same body with `PATCH /v1/copiers/{copier_id}`. Send `true` to resume. See [Copiers](/docs/copiers#pause-and-resume).
* Delete the copier with `DELETE /v1/copiers/{copier_id}`. Add `?close_positions=true` to close the copies it opened as well.
* Disconnect an account with `DELETE /v1/accounts/{account_id}` once no copier uses it.

## Next steps [#next-steps]

- [Followers](/docs/followers): Every rule: sizing modes, symbol filters and mapping, sessions and stops.

- [Copy events](/docs/events): Actions, timings, terminal replies and paging through older events.

- [Errors](/docs/errors): What each error code means and which ones to retry.

- [API reference](/docs/api-reference): Every endpoint, parameter and response schema.
