Partner API
The company-level API for brokers, white-labels and introducing partners. Your backend manages clients by its own ids, opens accounts, books money, signs users in and reads what they trade.
#Basics
| Base URL | https://api.your-brand.com/api/external/v1 (SSO login: /api/sso/brands/{brand_id}/login) |
|---|---|
| Auth | X-Api-Key: <key> or Authorization: Bearer <key>. Keys are created in the CRM, Settings › API & Webhooks. Details → |
| Rate limit | Company-wide requests per minute (default 120); 429 rate_limited + Retry-After. Details → |
| Errors | { "error": { "code", "message", "details" } }. All codes → |
| Live documents | GET /openapi.yaml and GET /guide.txt on your own host always match the version you run. |
curl "https://api.your-brand.com/api/external/v1/me" \
-H "X-Api-Key: $OCTOLIT_PARTNER_KEY"const res = await fetch("https://api.your-brand.com/api/external/v1/me", {
method: "GET",
headers: {
"X-Api-Key": process.env.OCTOLIT_PARTNER_KEY,
},
});
if (!res.ok) throw new Error(`GET failed: ${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);import os, requests
r = requests.request(
"GET",
"https://api.your-brand.com/api/external/v1/me",
headers={"X-Api-Key": os.environ["OCTOLIT_PARTNER_KEY"]},
timeout=10,
)
r.raise_for_status()
print(r.json())<?php
$ch = curl_init("https://api.your-brand.com/api/external/v1/me");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("OCTOLIT_PARTNER_KEY"),
],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);using var http = new HttpClient();
using var req = new HttpRequestMessage(HttpMethod.Get, "https://api.your-brand.com/api/external/v1/me");
req.Headers.Add("X-Api-Key", Environment.GetEnvironmentVariable("OCTOLIT_PARTNER_KEY"));
using var res = await http.SendAsync(req);
res.EnsureSuccessStatusCode();
Console.WriteLine(await res.Content.ReadAsStringAsync());var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://api.your-brand.com/api/external/v1/me"))
.header("X-Api-Key", System.getenv("OCTOLIT_PARTNER_KEY"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
var res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body());var body io.Reader
req, err := http.NewRequest("GET", "https://api.your-brand.com/api/external/v1/me", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("X-Api-Key", os.Getenv("OCTOLIT_PARTNER_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))var req = URLRequest(url: URL(string: "https://api.your-brand.com/api/external/v1/me")!)
req.httpMethod = "GET"
req.setValue(ProcessInfo.processInfo.environment["OCTOLIT_PARTNER_KEY"], forHTTPHeaderField: "X-Api-Key")
let (data, response) = try await URLSession.shared.data(for: req)
print((response as! HTTPURLResponse).statusCode, String(decoding: data, as: UTF8.self))val client = OkHttpClient()
val req = Request.Builder()
.url("https://api.your-brand.com/api/external/v1/me")
.header("X-Api-Key", System.getenv("OCTOLIT_PARTNER_KEY"))
.get()
.build()
client.newCall(req).execute().use { res ->
println("${res.code} ${res.body?.string()}")
} #All operations
#Account
Check the key you are calling with and fetch the live copies of this documentation from your own CRM host.
#Clients
Read every client of your company, page by change time. email / phone come masked unless the key carries clients:read_email / clients:read_phone.
#Users
Create or update clients by your client id (external_id), read them back and pause / resume their trading. The same external_id always maps to the same client in your company.
/users/{external_id} A user by your client id · clients:read PUT /users/{external_id} Create or update a user by your client id · users:write POST /users/{external_id}/trading/disable Pause trading for a user · trading:control POST /users/{external_id}/trading/enable Resume trading for a user · trading:control #Single sign-on
Two directions: your backend asks for a one-time login link (POST …/sso), or your platform signs a short JWT and sends the browser to /api/sso/brands/{brand_id}/login. Sessions can be listed and ended.
/users/{external_id}/sso A one-time login link for a user (your backend asks) · sso:issue GET /users/{external_id}/sessions The user's live sessions · sso:issue DELETE /users/{external_id}/sessions End every session of the user · sso:issue DELETE /users/{external_id}/sessions/{session_id} End one session GET /api/sso/brands/{brand_id}/login Sign a client in with a token your system signed (browser link) POST /api/sso/brands/{brand_id}/login Sign a client in with a token your system signed (server to server) #Trading accounts
Open live or demo accounts under the brand's rules and read accounts, balance and positions of a user.
/users/{external_id}/accounts The user's trading accounts with their balances · accounts:read POST /users/{external_id}/accounts Open a trading account (live or demo) · accounts:write GET /users/{external_id}/balance The user's balance, equity and margin (all accounts) · accounts:read GET /users/{external_id}/positions The user's positions · accounts:read #Money
Book a deposit, withdrawal or credit on a user's account from your backend. Scope balance:write is off by default, an idempotency key is required and every posting is audited.
#MT links
Record which MT4 / MT5 login on which server belongs to a user. Link only - execution stays on your MT server.
#Market
Market data for your middleware: current quotes from the platform's price mirror, the platform's own chart history and what the brand's clients trade most.
#Trading on behalf
Place, change and close orders FOR a mapped user through the same order path the trader uses. Leverage, spread, lot rules, margin and every guard apply; every action is written to the client's history as done by your key. MetaTrader accounts are not traded here (409 mt_account).
/users/{external_id}/orders The user's pending orders · accounts:read POST /users/{external_id}/orders Place an order for the user · trading:act GET /users/{external_id}/orders/lookup Find the order / position this key placed with a client_order_id · accounts:read PATCH /users/{external_id}/orders/{id} Change a pending order (price, stop loss, take profit) · trading:act DELETE /users/{external_id}/orders/{id} Cancel a pending order · trading:act PATCH /users/{external_id}/positions/{id} Change the stop loss / take profit of an open position · trading:act POST /users/{external_id}/positions/{id}/close Close an open position at market · trading:act #Scopes
clients:readRead the clients of the company, page by change time (GET /clients?updated_since=…).clients:read_emailE-mail addresses in clear instead of masked.clients:read_phonePhone numbers in clear instead of masked.clients:read_ftdFirst-deposit date on clients.clients:read_commentsClient comments.clients:read_custom_fieldsCustom fields of the client.users:writePUT /users/{external_id} - create or update a client by your id.sso:issuePOST /users/{external_id}/sso - one-time login links; list and end sessions.accounts:readAccounts, balance and positions of a user; quotes, bars and trending for your middleware.accounts:writeOpen a trading account for a user.trading:controlPause and resume trading for a user.mt:linkRecord which MT4 / MT5 login belongs to a user.trading:actPlace, change and close orders FOR a user through the platform's own order path; every action lands in the client's history as done by your key.balance:writePOST /users/{external_id}/transactions - book a deposit, withdrawal or credit. OFF by default: only the CRM owner (or an admin with the "Edit transactions" right) can put it on a key, and the brand's "Money posting" switch must be on.#Typical flows
Onboard a client
PUT /users/{external_id} → POST …/accounts → POST …/sso. Three calls from signup to a live trader session.
Sign users in from your platform
Sign a 5-minute JWT, send the browser to /api/sso/brands/{brand_id}/login. The platform creates unknown clients when the brand allows it.
Mirror deposits and withdrawals
Your cashier posts POST …/transactions with an idempotency key; deposit.status / withdrawal.status events keep you in sync.
MT4 / MT5 brokers
Keep execution on your MT server, use the platform for CRM, dashboards and sign-in; POST …/mt_links ties logins to clients.
Trade on behalf
A managed-account or copy-trading backend places orders for mapped users through the platform's own guards (trading:act).
React to events
Clients, logins, trades, margin calls and money status changes pushed to you, signed and retried.
#Data protection
- Export (right of access): the CRM client page offers a zip with profile, contacts, custom fields, sign-ins, accounts, positions, transactions, notes and documents. Every export is written to the client's history.
- Erasure (right to be forgotten): personal data is removed, the client row stays as "Erased client <id>" for money history, your
external_idis cleared andclient.updatedgoes out. Refused while positions, pending money or a non-zero balance exist. The sameexternal_idcreates a new client afterwards. - Reduced-data mode (per brand): clients created by SSO get no name or e-mail from the token, and sign-in rows keep only the network part of the IP.
- Retention: sign-in history 365 days and webhook delivery log 30 days by default; login links are one-time and removed hourly.
GET /me answers as expected · PUT /users/test creates in the right brand · a login link signs the test user in and fails on second use · your SSO token signs in and a replay fails with token_replayed · Send test event verifies · one demo trade produces trade.opened / trade.closed · a test posting repeated answers replayed: true · keys have IP allow-lists and expiry.