Rubi Compatible Provider API, version 1
The contract your own API implements when you do not run Axcera. Rendered verbatim from the specification Rubi's connector is built against.
Prop firms that do not run Axcera expose these read-only endpoints on their own infrastructure. Rubi polls them to keep every trader profile, trading account, trade and payout in sync, then uses that data to prepare interviews and score trading activity.
Transport and security#
- HTTPS only (TLS 1.2 or later). JSON bodies encoded in UTF-8.
- The firm chooses a base URL, for example
https://risk.example-firm.com/rubi/v1. Every path below is relative to it. - Authentication: a static secret generated by the firm, sent as
Authorization: Bearer <api key>. Rotate it at any time and paste the new value in the Rubi dashboard. - Restrict access to the Rubi egress IP ranges shown in the dashboard under Integrations. Rubi never writes to your systems: every endpoint is
GET. - Timestamps are RFC 3339 in UTC (
2026-09-23T14:05:00Z). Money is a JSON number. Unknown optional fields may benullor omitted.
Pagination and incremental sync#
List endpoints accept:
| Parameter | Type | Rules |
|---|---|---|
updated_since | timestamp | Return records whose updated_at is greater than or equal to this value. |
cursor | string | Opaque value returned as next_cursor by the previous page. |
limit | integer | Default 100, maximum 500. |
Responses have the shape { "data": [...], "next_cursor": "..." }. next_cursor is null on the last page. Records must be ordered by updated_at then id, ascending, so that a cursor never skips a record. Rubi re-reads with a 10 minute overlap and upserts by id, so returning a record twice is safe.
Endpoints#
GET /health
{ "status": "ok", "provider": "Example Firm Risk API", "version": "1" }GET /traders
{
"data": [
{
"id": "cus_18422",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Doe",
"country": "GB",
"phone": "+447700900123",
"registered_at": "2026-03-02T09:12:44Z",
"kyc_status": "verified",
"tags": ["vip"],
"updated_at": "2026-09-20T08:00:00Z"
}
],
"next_cursor": "eyJpZCI6ImN1c18xODQyMiJ9"
}id is required, stable and unique. country is ISO 3166-1 alpha-2. kyc_status is one of not_started, pending, verified, failed.
GET /traders/{id}
Returns one trader object (same shape as a list item). 404 when unknown.
GET /accounts
{
"data": [
{
"id": "acc_99812",
"trader_id": "cus_18422",
"platform": "mt5",
"login": "7001234",
"program": "2 Step 100K",
"phase": "evaluation",
"status": "active",
"currency": "USD",
"initial_balance": 100000,
"balance": 101250.5,
"equity": 101190.2,
"started_at": "2026-09-01T00:00:00Z",
"updated_at": "2026-09-22T21:00:00Z"
}
],
"next_cursor": null
}platform: mt4, mt5, ctrader, matchtrader, tradelocker, dxtrade, other. phase: evaluation, verification, funded, other. status: active, passed, breached, closed, suspended.
GET /trades
Accepts updated_since, cursor, limit, and optionally account_id to scope to one account.
{
"data": [
{
"id": "pos_5512009",
"account_id": "acc_99812",
"symbol": "EURUSD",
"side": "buy",
"volume": 1.5,
"open_time": "2026-09-22T13:01:07Z",
"close_time": "2026-09-22T13:42:55Z",
"open_price": 1.10412,
"close_price": 1.10501,
"stop_loss": 1.1021,
"take_profit": null,
"commission": -10.5,
"swap": 0,
"profit": 123.0,
"updated_at": "2026-09-22T13:42:56Z"
}
],
"next_cursor": "c_20260922_5512009"
}id is unique per account (ticket or position id). side is buy or sell. volume is in lots. close_time and close_price are null while the position is open. profit is the net result in account currency.
GET /payouts (recommended)
{
"data": [
{
"id": "pay_7781",
"trader_id": "cus_18422",
"account_id": "acc_99812",
"amount": 4200,
"currency": "USD",
"status": "paid",
"requested_at": "2026-09-10T10:00:00Z",
"paid_at": "2026-09-12T16:30:00Z",
"updated_at": "2026-09-12T16:30:00Z"
}
],
"next_cursor": null
}status: pending, approved, paid, rejected, cancelled. Respond 404 if you do not expose payouts: Rubi then skips them.
Errors, limits and latency#
- Errors use the matching HTTP status and a body
{ "error": { "code": "string", "message": "string" } }. - Support at least 5 requests per second. When throttling, answer
429with aRetry-Afterheader in seconds: Rubi waits and resumes. - Answer each request within 10 seconds. Rubi retries
5xxresponses and timeouts with exponential backoff.
Conformance check#
When you save the connection, Rubi runs a live check and shows the result of each step:
GET /healthanswers200with valid JSON.- Authentication is enforced (a request without the key must not return
200). GET /traders?limit=5returns the expected shape; every record hasidandupdated_at.- Pagination works: when
next_cursoris present, the next page loads. GET /accounts?limit=5andGET /trades?limit=5return the expected shape.GET /payouts?limit=5returns the expected shape or404.- Latency stays under 10 seconds per call.
Polling starts only when every required step passes. The dashboard keeps the latest check, the last error with its endpoint and HTTP status, and the history of every sync run.
Sync cadence#
Rubi runs an initial backfill (all traders and accounts, trades of the last 90 days), then an incremental sync every 5 minutes by default (configurable from 1 to 60 minutes) using updated_since with a 10 minute overlap.