Developer docs

License API integration

Reference for how each O2ODDS EA validates its license against the central server. This page exists mainly so paying customers can audit exactly what the EA calls and what it stores. Compiled EAs ship with this integration built in — no manual coding required.

Endpoint

All license calls go to a single HTTPS endpoint. HTTPS is required — plain HTTP is refused by .htaccess.

POST https://o2odds.com/api/license.php

Request

JSON body:

{
  "action":   "activate" | "validate" | "deactivate",
  "key":      "O2X-XXXX-XXXX-XXXX-XXXX",
  "account":  "12345678",
  "platform": "MT4" | "MT5" | "cTrader",
  "nonce":    "random-string-per-request"
}

Actions

Response

Always JSON, always HTTP 200 (except 405 for wrong method and 429 when rate-limited). The EA must inspect status, not just the HTTP code.

{
  "ok":               true,
  "status":           "active",
  "product":          "apiary",
  "activations_used": 1,
  "activations_max":  3,
  "valid_until":      null,
  "timestamp":        1720012345,
  "nonce":            "the-same-nonce-you-sent",
  "signature":        "hmac-sha256-hex"
}

Possible status values

Signature verification (important)

The EA must verify the signature field on every response before trusting it. This prevents a spoofed server or a man-in-the-middle from returning a fake active. The signature is:

signature = HMAC_SHA256(
  key + "|" + account + "|" + platform + "|" + status + "|" +
  valid_until + "|" + nonce + "|" + timestamp,
  LICENSE_API_SECRET
)

Where LICENSE_API_SECRET is a 64-character hex string compiled into the EA binary. When it needs rotating (say, if a build leaks), publish a new EA build with the new secret and update db-config.php in the same window.

Offline grace

The EA should cache the last successful response and permit continued operation for a short offline window (default 3 days). This handles brief outages without killing trading. After the grace period expires without a successful check, the EA halts.

MQL4 sample

// MQL4 — outline only. Actual EA builds ship with this ready-integrated.

#property strict

input string LicenseKey = "O2X-XXXX-XXXX-XXXX-XXXX";

int OnInit()
{
   if(!LicenseActivate(LicenseKey))
   {
      Print("License activation failed. See journal.");
      return(INIT_FAILED);
   }
   EventSetTimer(3600);   // heartbeat every hour
   return(INIT_SUCCEEDED);
}

void OnTimer()
{
   LicenseValidate(LicenseKey);
}

void OnDeinit(const int reason)
{
   EventKillTimer();
   if(reason == REASON_REMOVE) LicenseDeactivate(LicenseKey);
}

bool LicenseActivate(const string key)
{
   string body = BuildJson("activate", key);
   string resp;
   if(!CallLicenseApi(body, resp)) return(ApplyOfflineGrace());
   return(VerifyAndAccept(resp));
}

// CallLicenseApi() uses WebRequest() with POST, application/json.
// VerifyAndAccept() parses JSON, checks the HMAC signature, caches
// the response with a wall-clock timestamp, then returns the ok flag.
// Full implementation lives in the compiled EA; this outline is just
// a reference for customer-side audit.

MQL5 and cTrader

The MQL5 version uses WebRequest() the same way. The cTrader (cAlgo / C#) version uses HttpClient with application/json — semantically identical.

What the server records

Every API call is logged to license_events with the license (or license key on failure), action, IP address, account, platform, and outcome. Customers can see the resulting activations on their licenses dashboard.

Privacy

The API stores the broker account number, platform name, and IP address of the machine running the EA — nothing else from the EA. See the privacy policy for the full list of what we collect and why.