Developer Portal

Accounts & Funding

REST API for brokers to onboard end-user clients, open trading accounts, and move funds. Used by CRMs, in-house back-office tools, and client cabinet/portal integrations.

View as Markdown

You can:

  • Onboard a client (create a profile, attach trading accounts).
  • Deposit, withdraw, and post balance adjustments.
  • Read orders, positions, portfolios, and deal history.
  • Issue end-user access tokens so clients can call the Trader API.

This API does not configure platform-level objects (account groups, symbols, margin/commission templates). Those are managed by your Helio admins through the full Admin API.

Prerequisites

Before you start, make sure you have:

  • The Helio admin endpoint for your environment (provided by your Helio contact).
  • Either (a) a back-office username + password, or (b) a long-lived admin access token.
  • Your outbound IP whitelisted by Helio operations — non-whitelisted requests are rejected.

Authentication

Every request to this API must include a bearer admin token in the Authorization header:

Authorization: Bearer <ADMIN_ACCESS_TOKEN>

Tokens are valid for 30 days. Refresh by calling Admin Login again before expiry, or whenever a request returns 401 Unauthorized.

Obtain a token

curl -X POST "$HELIO_ADMIN_URL/user/login" \
  -H "Content-Type: application/json" \
  -d '{"username": "ops@yourbroker.com", "password": "***"}'
# => { "token": "eyJhbGciOi..." }
const res = await fetch(`${process.env.HELIO_ADMIN_URL}/user/login`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'ops@yourbroker.com', password: '***' }),
})
const { token } = await res.json()
import os, requests

res = requests.post(
    f"{os.environ['HELIO_ADMIN_URL']}/user/login",
    json={"username": "ops@yourbroker.com", "password": "***"},
)
token = res.json()["token"]
body, _ := json.Marshal(map[string]string{
    "username": "ops@yourbroker.com",
    "password": "***",
})
res, _ := http.Post(os.Getenv("HELIO_ADMIN_URL")+"/user/login",
    "application/json", bytes.NewReader(body))
var out struct{ Token string `json:"token"` }
json.NewDecoder(res.Body).Decode(&out)

Reuse the token

Cache the token and pass it on every subsequent call:

curl "$HELIO_ADMIN_URL/account-groups" \
  -H "Authorization: Bearer $TOKEN"
const res = await fetch(`${HELIO_ADMIN_URL}/account-groups`, {
  headers: { Authorization: `Bearer ${token}` },
})
res = requests.get(
    f"{HELIO_ADMIN_URL}/account-groups",
    headers={"Authorization": f"Bearer {token}"},
)
req, _ := http.NewRequest("GET", HELIO_ADMIN_URL+"/account-groups", nil)
req.Header.Set("Authorization", "Bearer "+token)
res, _ := http.DefaultClient.Do(req)

All requests must originate from a whitelisted IP. Calls from any other source are rejected at the edge before reaching this API.

Onboard a client

Onboarding a new client takes four calls. Keep the IDs returned at each step — later steps need them.

Fetch reference data

Currencies and account groups are configured by your Helio admins. Read what's available in your environment before creating accounts.

  • GET /options/currencies  →  the currencies an account can be denominated in.
  • GET /account-groups  →  the account groups a new account can be assigned to. Note each group's id, whether it's isDemo, and any description your admins have provided.

Responses omit fields that hold their default value, so a live group (isDemo = false) has no isDemo key at all — only demo groups carry isDemo: true. Select a live group with g.isDemo !== true, never !g.isDemo against a possibly-absent field. The same applies to other booleans, zero numbers, and empty collections.

Create a client profile

A profile represents one end-user. One profile can own many trading accounts (e.g. one per currency or strategy).

POST /profiles  →  capture the returned id.

A profile requires a valid, unique email and a password of at least 8 characters (this is the client's login for the Trader API). The password is write-only — it is stored hashed and is not returned in the response.

Create a trading account

POST /accounts with profileId, currency, groupId, and maxLeverage from the previous steps.

maxLeverage is required and must be greater than zero. The account's currency is fixed at creation and cannot be changed. The spreadBetting (isSb) flag is also immutable after creation.

The created account is returned wrapped as { account: { … } }, whereas POST /profiles returns the profile flat at the top level. Read res.account.id here but res.id for profiles.

(Optional) Fund the account

Credit the opening balance with POST /accounts/{accountId}/deposit. Use withdraw to debit, or BalanceTransactionCreate for adjustments.

Funding is permission-gated: deposit requires funding:deposit and withdraw requires funding:withdraw. An onboarding-only token can create profiles and accounts but will get 403 AUTH_NO_PERMISSION here until those permissions are granted. Send amount as a JSON string (e.g. "10000") — monetary values are serialized as strings over the wire.

End-to-end example

const base = process.env.HELIO_ADMIN_URL
const headers = (token) => ({
  'Content-Type': 'application/json',
  Authorization: `Bearer ${token}`,
})

// 1. Login
const { token } = await fetch(`${base}/user/login`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'ops@yourbroker.com', password: '***' }),
}).then((r) => r.json())

// 2. Reference data
const { groups } = await fetch(`${base}/account-groups`, { headers: headers(token) })
  .then((r) => r.json())
// Live groups omit the isDemo field entirely; only demo groups carry isDemo: true.
const liveGroup = groups.find((g) => g.isDemo !== true)

// 3. Create profile (email + password are required; password must be >= 8 chars)
const profile = await fetch(`${base}/profiles`, {
  method: 'POST',
  headers: headers(token),
  body: JSON.stringify({
    email: 'jane.doe@example.com',
    firstName: 'Jane',
    lastName: 'Doe',
    password: 'S3curePassw0rd', // write-only; never returned in the response
  }),
}).then((r) => r.json())

// 4. Create account (maxLeverage is required and must be > 0)
const { account } = await fetch(`${base}/accounts`, {
  method: 'POST',
  headers: headers(token),
  body: JSON.stringify({
    profileId: profile.id,
    currency: 'USD',
    groupId: liveGroup.id,
    maxLeverage: '100', // uint64 is sent/received as a JSON string
  }),
}).then((r) => r.json())

// 5. Deposit opening balance
await fetch(`${base}/accounts/${account.id}/deposit`, {
  method: 'POST',
  headers: headers(token),
  body: JSON.stringify({ amount: '10000', comment: 'Opening deposit' }),
})
import os, requests

base = os.environ["HELIO_ADMIN_URL"]

# 1. Login
token = requests.post(
    f"{base}/user/login",
    json={"username": "ops@yourbroker.com", "password": "***"},
).json()["token"]
auth = {"Authorization": f"Bearer {token}"}

# 2. Reference data
groups = requests.get(f"{base}/account-groups", headers=auth).json()["groups"]
# Live groups omit the isDemo field entirely; only demo groups carry isDemo: true.
live_group = next(g for g in groups if g.get("isDemo") is not True)

# 3. Create profile (email + password are required; password must be >= 8 chars)
profile = requests.post(
    f"{base}/profiles",
    headers=auth,
    json={
        "email": "jane.doe@example.com",
        "firstName": "Jane",
        "lastName": "Doe",
        "password": "S3curePassw0rd",  # write-only; never returned in the response
    },
).json()

# 4. Create account (maxLeverage is required and must be > 0)
account = requests.post(
    f"{base}/accounts",
    headers=auth,
    json={
        "profileId": profile["id"],
        "currency": "USD",
        "groupId": live_group["id"],
        "maxLeverage": "100",  # uint64 is sent/received as a JSON string
    },
).json()["account"]

# 5. Deposit opening balance
requests.post(
    f"{base}/accounts/{account['id']}/deposit",
    headers=auth,
    json={"amount": "10000", "comment": "Opening deposit"},
)

Key concepts

Profiles vs accounts

A profile is the end-user (one per client). An account is a trading book under that profile. One profile can own many accounts — for example, one per currency or one live and one demo.

Account groups

A group is a pre-configured bundle of trading rules — margin call / stop-out levels, margin calculation type, swap-free flag, demo vs live, and the linked symbol profile that determines which symbols the account can trade. Groups are read-only here: brokers select an existing group when creating an account, but groups themselves (and the symbols, margin templates, and commission templates they reference) are configured by your Helio admins outside this API.

Currencies & immutables

An account's currency and spreadBetting flag are fixed at creation and cannot be changed later. Choose deliberately. Other settings (group, leverage cap, statement emails) can be updated via Update Account.

End-user trading

This API does not place orders. Once a client has a profile and account, issue them a Trader API token via Create Session (POST /trade/login) — they then trade against the Trader API directly.

Next steps