Skip to content

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.

This page is the contract
Build these endpoints once. When you save the connection in the dashboard under Integrations, Rubi runs the conformance check below and starts polling only when every required step passes.

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 be null or omitted.

Pagination and incremental sync#

List endpoints accept:

ParameterTypeRules
updated_sincetimestampReturn records whose updated_at is greater than or equal to this value.
cursorstringOpaque value returned as next_cursor by the previous page.
limitintegerDefault 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

json
{ "status": "ok", "provider": "Example Firm Risk API", "version": "1" }

GET /traders

json
{
  "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

json
{
  "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.

json
{
  "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.

json
{
  "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 429 with a Retry-After header in seconds: Rubi waits and resumes.
  • Answer each request within 10 seconds. Rubi retries 5xx responses and timeouts with exponential backoff.

Conformance check#

When you save the connection, Rubi runs a live check and shows the result of each step:

  1. GET /health answers 200 with valid JSON.
  2. Authentication is enforced (a request without the key must not return 200).
  3. GET /traders?limit=5 returns the expected shape; every record has id and updated_at.
  4. Pagination works: when next_cursor is present, the next page loads.
  5. GET /accounts?limit=5 and GET /trades?limit=5 return the expected shape.
  6. GET /payouts?limit=5 returns the expected shape or 404.
  7. 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.