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.
#Book a deposit, withdrawal or credit on the user's account
Scope balance:write (off by default; only the Octolit CRM owner or an admin with the "Edit transactions" right can put it on a key). The brand's "Money posting" switch must be on (403 brand_disabled otherwise).
The money is booked by the Octolit CRM's own transaction service - the same path a back-office "Add transaction" takes (balance update, first-deposit date, client e-mails, postbacks; for an MT account the balance call to the MT server). status: approved (default) counts at once; status: pending waits for approval in the Octolit CRM and then raises deposit.status / withdrawal.status like any other transaction.
Idempotency key (required: idempotency_key in the body or the Idempotency-Key header, 1-128 characters, unique per company). Repeat a call with the same key and body as often as you like: you get the stored answer again (200, replayed: true) and nothing is booked twice. The same key with a different body answers 409 idempotency_conflict; a posting still being booked 409 in_progress.
A withdrawal over the account's free margin or a credit_out over its credit answers 409 insufficient_funds and books nothing. Every posting leaves an audit row and a line in the client's CRM history naming the key.
Path parameters
Your client id (1-191 letters, digits, . _ @ : + -). Stored on the client as external_user_id.
Headers
The idempotency key when it is not in the body.
Request body · application/json
> 0, at most 10 decimals (send a string to keep the exact decimals).
3-letter code or id; default the account's currency.
One of the user's accounts; default the first one.
Payment method text shown in the Octolit CRM (default partner_api). 0-64 characters.
Your reference (invoice / PSP id); shown in the Octolit CRM and sent back. 0-128 characters.
0-255 characters.
Required unless sent as the Idempotency-Key header. 1-128 characters.
Responses
The same key and body again - the stored answer (replayed = true), nothing booked.
Booked.
insufficient_scope, ip_not_allowed, api_disabled, company_inactive, brand_disabled, sso_disabled, trending_disabled (trading on behalf also: brand_inactive, client_inactive, trading_inactive, trading_denied).
not_found / user_not_found / account_not_found / symbol_not_found.
idempotency_conflict, in_progress or insufficient_funds (details.available).
invalid_request (details.errors per field) or money_rejected (the Octolit CRM's transaction service refused).
curl -X POST "https://api.your-brand.com/api/external/v1/users/cust_10042/transactions" \
-H "X-Api-Key: $OCTOLIT_PARTNER_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}'const res = await fetch("https://api.your-brand.com/api/external/v1/users/cust_10042/transactions", {
method: "POST",
headers: {
"X-Api-Key": process.env.OCTOLIT_PARTNER_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}),
});
if (!res.ok) throw new Error(`POST failed: ${res.status} ${await res.text()}`);
const data = await res.json();
console.log(data);import os, requests
r = requests.request(
"POST",
"https://api.your-brand.com/api/external/v1/users/cust_10042/transactions",
headers={"X-Api-Key": os.environ["OCTOLIT_PARTNER_KEY"]},
json={
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001",
},
timeout=10,
)
r.raise_for_status()
print(r.json())<?php
$ch = curl_init("https://api.your-brand.com/api/external/v1/users/cust_10042/transactions");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("OCTOLIT_PARTNER_KEY"),
"Content-Type: " . "application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"kind" => "deposit",
"amount" => "100.00",
"currency" => "USD",
"account_id" => 55,
"status" => "approved",
"method" => "card",
"reference" => "INV-1001",
"note" => "Card deposit",
"idempotency_key" => "dep-INV-1001",
]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data);using var http = new HttpClient();
using var req = new HttpRequestMessage(HttpMethod.Post, "https://api.your-brand.com/api/external/v1/users/cust_10042/transactions");
req.Headers.Add("X-Api-Key", Environment.GetEnvironmentVariable("OCTOLIT_PARTNER_KEY"));
req.Content = new StringContent("""
{
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}
""", Encoding.UTF8, "application/json");
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/users/cust_10042/transactions"))
.header("X-Api-Key", System.getenv("OCTOLIT_PARTNER_KEY"))
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("""
{
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}
"""))
.build();
var res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body());body := strings.NewReader(`{
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}`)
req, err := http.NewRequest("POST", "https://api.your-brand.com/api/external/v1/users/cust_10042/transactions", body)
if err != nil {
log.Fatal(err)
}
req.Header.Set("X-Api-Key", os.Getenv("OCTOLIT_PARTNER_KEY"))
req.Header.Set("Content-Type", "application/json")
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/users/cust_10042/transactions")!)
req.httpMethod = "POST"
req.setValue(ProcessInfo.processInfo.environment["OCTOLIT_PARTNER_KEY"], forHTTPHeaderField: "X-Api-Key")
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = """
{
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}
""".data(using: .utf8)
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/users/cust_10042/transactions")
.header("X-Api-Key", System.getenv("OCTOLIT_PARTNER_KEY"))
.method("POST", """{
"kind": "deposit",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"status": "approved",
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"idempotency_key": "dep-INV-1001"
}""".toRequestBody("application/json".toMediaType()))
.build()
client.newCall(req).execute().use { res ->
println("${res.code} ${res.body?.string()}")
} {
"transaction": {
"id": 1042,
"kind": "deposit",
"status": "approved",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"created_at": "2026-11-11T09:30:00.000000Z"
},
"balance": {
"currency": "USD",
"balance": "10250.00",
"equity": "10312.40",
"margin": "108.45",
"free_margin": "10203.95",
"margin_level": "9509.3",
"open_pl": "string",
"credit": "string",
"as_of": "2026-11-11T09:30:00Z",
"accounts": [
{
"id": 1042,
"name": "Main",
"currency": "USD",
"demo": false,
"type": {
"id": 1042,
"name": "Main",
"provider": "string"
},
"balance": "10250.00",
"equity": "10312.40",
"margin": "108.45",
"free_margin": "10203.95",
"margin_level": "9509.3",
"open_pl": "string",
"closed_pl": "string",
"credit": "string",
"mt_login": "string",
"created_at": "2026-11-11T09:30:00.000000Z"
}
]
},
"idempotency_key": "dep-INV-1001",
"replayed": false
}{
"transaction": {
"id": 1042,
"kind": "deposit",
"status": "approved",
"amount": "100.00",
"currency": "USD",
"account_id": 55,
"method": "card",
"reference": "INV-1001",
"note": "Card deposit",
"created_at": "2026-11-11T09:30:00.000000Z"
},
"balance": {
"currency": "USD",
"balance": "10250.00",
"equity": "10312.40",
"margin": "108.45",
"free_margin": "10203.95",
"margin_level": "9509.3",
"open_pl": "string",
"credit": "string",
"as_of": "2026-11-11T09:30:00Z",
"accounts": [
{
"id": 1042,
"name": "Main",
"currency": "USD",
"demo": false,
"type": {
"id": 1042,
"name": "Main",
"provider": "string"
},
"balance": "10250.00",
"equity": "10312.40",
"margin": "108.45",
"free_margin": "10203.95",
"margin_level": "9509.3",
"open_pl": "string",
"closed_pl": "string",
"credit": "string",
"mt_login": "string",
"created_at": "2026-11-11T09:30:00.000000Z"
}
]
},
"idempotency_key": "dep-INV-1001",
"replayed": false
}