Telemetry
contract · SentinelTelemetryV1
A contract strict enough to be worth trusting.
Your expert advisor posts one JSON object. The server validates it against fixed rules, normalises it, and keys it by identity. Anything that doesn’t fit is rejected with a reason.
The payload
29 fields in. 31 stored.
Grouped as they are defined in the wire model. The two extra stored fields are added by the server: a composite identityKey and the lastSeenUtc receipt timestamp.
| Group | Count | Carries |
|---|---|---|
| identity | 10 | accountKey, accountLogin, brokerServer, terminalName, magic, name, family, symbol, timeframe, contractVersion |
| version & time | 3 | timestampUtc, telemetryVersion, guardVersion |
| lot & risk | 5 | baseLot, requestedLot, finalLot, appliedMultiplier, riskMultiplierSource |
| decision | 5 | allowTrading, blockReason, lastDecision, sentinelAction, dashboardAction |
| execution quality | 4 | spread, slippage, positionLots, expectedLots |
| tickets | 2 | orderTicket, positionTicket |
X-Sentinel-Telemetry-Key: ••••••••
{
"contractVersion": "SentinelTelemetryV1",
"accountKey": "broker::184422",
"magic": 184422,
"symbol": "EURUSD",
"family": "breakout",
"telemetryVersion": "1.4.0",
"guardVersion": "10D.4",
"baseLot": 0.10,
"requestedLot": 0.10,
"finalLot": 0.0750,
"appliedMultiplier": 0.75,
"allowTrading": true
}
Identity key. Rows are keyed by accountKey|magic|SYMBOL|family. If accountKey is absent it is derived as {brokerServer}::{accountLogin}.
Validation
Six rules that must hold.
Fail any of them and the payload is rejected with 400 — it never reaches the store, and it never quietly becomes a half-row you’d have to debug later.
| Rule | Constraint | Why |
|---|---|---|
| contractVersion | = SentinelTelemetryV1 | Version drift between fleet and platform is caught immediately, not months later. |
| magic | > 0 | An expert without a magic number cannot be attributed to a strategy. |
| symbol | required | Uppercased on arrival so broker casing never splits one instrument into two. |
| family | one of 5 | A closed vocabulary keeps cross-account comparison meaningful. |
| appliedMultiplier | 0.0 – 5.0 | A bounded range means a bad input can never look like a plausible risk instruction. |
| versions | non-empty | Both telemetryVersion and guardVersion, so every row is attributable to a build. |
- breakout
- meanreversion
- trendpullback
- cs28
- unknown
Common aliases are normalised on arrival — brk → breakout, mr → meanreversion, trd → trendpullback, cs → cs28. Lot values are rounded to four decimal places, away from zero.
Freshness
Five states, and honesty about what they measure.
A row is stale five minutes after the last time the API heard from that expert. The clock is server-side: lastSeenUtc is stamped on receipt, not taken from the terminal. So freshness means “time since we heard from it”, which is exactly the thing you want to alert on — a terminal that stops talking is the failure mode that matters.
Legacy rows. Fleet rows derived from older, non-V1 sources use a wider 10-minute window and are labelled as such. We don’t blend the two.
-
OK / FULL
Full contract satisfied and seen within the window.
-
PARTIAL
Recent, but version fields are incomplete — the row is usable, not authoritative.
-
STALE
Full contract, but nothing received inside the window. The expert has gone quiet.
-
LEGACY
Derived from a pre-V1 source. Held to the wider window, never mixed with V1 rows.
-
MISSING
Expected by the registry, never seen. The absence is itself a signal.
Surface
One write. Three reads.
| Method | Route | Returns |
|---|---|---|
| POST | /api/ea-telemetry/v1/ingest | Accepts one validated payload. Every attempt — allowed or denied — writes an audit entry with endpoint, actor, reason and accountKey. |
| GET | /api/ea-telemetry/v1/latest | Latest row per identity, optionally filtered by accountKey. |
| GET | /api/ea-telemetry/v1/magic/{magic} | Everything reporting under one magic number. |
| GET | /api/ea-telemetry/v1/summary | Counts by state, including how many rows are past the freshness window. |
Ingest is deliberately outside the blocked-route table: telemetry keeps flowing while every apply, override and config route returns 403.
Obedience
Did the terminal actually do it?
Instruction and execution are different things, and the gap between them is where fleets quietly drift. The lot audit closes it arithmetically: baseLot × expectedMultiplier against the lot the broker really filled, with a 5% tolerance.
- OK
- MISMATCH
- MISSING_DATA
Evidence-bound. The audit is only as good as what the expert reports. Terminals that don’t send lot and multiplier fields resolve to MISSING_DATA — they are never scored as obedient by default.
Getting connected
One include, one call.
The MQL5 client is a single header with one send function. The only terminal-side requirement is allowing the endpoint in Tools → Options → Expert Advisors. The HTTP request times out at 8000 ms, so a slow network degrades into a missed heartbeat rather than a stalled expert.
The store keeps the latest value only. One row per accountKey|magic|symbol|family, last write wins. There is no time-series history and no retention window to configure. Quantisentry is an observation plane, not a data warehouse — if you need tick-level history, keep your own.
Payload signing is not shipped. The envelope has fields for a payload hash and signature, and nothing writes or verifies them today. Transport security is TLS plus a header key. We would rather tell you that than let you assume otherwise.