G2I2 · SAR Haramain API docs Sign in Playground Sandbox ↗
G2I2 GraphQL API

Live Haramain railway data, over GraphQL.

A single, typed GraphQL endpoint for the Saudi Arabia Railways Haramain High-Speed line (HHR): timetables, topology, real-time train movements, and the live signalling mesh.

The API is a Backend-for-Frontend over SAR's MONR monitoring system. It authenticates upstream, holds the session, fans the live feed out to many subscribers, and re-exposes everything as clean, English-typed GraphQL with real-time subscriptions over graphql-ws.

Everything here is interactive — run queries in the playground and start live subscriptions right from these docs.

At a glance

CapabilityType
Discover lines & sectionsQuery
Timetables & circulation detailQuery
Live train positionsSubscription
Live signalling meshSubscription
Drive control-room viewersMutation

Connecting

Everything is served from one origin. Queries and mutations are HTTP POST; subscriptions use a WebSocket on the same path with the graphql-transport-ws subprotocol.

ChannelEndpoint
GraphQL · HTTP
Subscriptions · WebSocket

Two ways to try it

1. The Playground (recommended). Built into these docs. Click Run in playground on any operation and it loads a ready-to-run example with real values filled in — just press Run. The easiest way to explore.

2. Apollo Sandbox (advanced). The full GraphQL IDE — schema explorer, autocomplete, variables & headers panels. Open the HTTP endpoint in a browser.

In Sandbox, clicking a field in the schema explorer generates a query whose required variables default to null — running it as-is fails with "Variable … must not be null". Fill the Variables panel first, e.g. { "lineId": "HHR", "baseNumber": "00162", "serviceDate": "0" }. The Playground avoids this entirely by using inline values.

Quickstart

Fetch the available lines and their sections — no auth or SDK required.

Authentication

Every operation requires a bearer token (JWT). Your provider issues you a username and password; exchange them for a token with the login mutation, then send that token on every request. Only login and schema introspection are public.

1. Log in to get a token:

mutation {
  login(username: "YOUR_USER", password: "YOUR_PASSWORD") {
    accessToken
    expiresIn
    role
  }
}

2. Send the token on queries & mutations as an HTTP header:

Authorization: Bearer <accessToken>

3. For subscriptions (WebSocket), pass the token in the connection_init payload:

{ "type": "connection_init", "payload": { "authorization": "Bearer <accessToken>" } }

In this portal you can just use Sign in (top-right) — the playground and live feed then send your token automatically. Tokens expire after expiresIn seconds; log in again to refresh.

RoleAllowed
readAll queries and subscriptions.
controlEverything read can do, plus the live-viewer mutations.
The centerTrain / centerElement mutations drive operators' real control-room viewers, so they require the control role. The gateway also authenticates to the upstream MONR system with a service account on your behalf — you never handle those credentials.

Caching & freshness

Slow timetable/topology reads are cached server-side (transparent to you). Identical queries seconds apart may return identical data — that's expected. Live subscriptions are never cached.

OperationCache TTL
lineSections / sectionPoints~10 min
sectionTrains~20 s
trainDetail~15 s
searchTrains~10 s

Queries

Read timetables and topology. All queries are HTTP POST to the GraphQL endpoint.

Subscriptions coming soon

Real-time data over graphql-ws. Click Stream on any subscription — frames appear in the docked console at the bottom, which stays open as you read.

Mutations

Side-effecting actions that drive operator viewers. Restricted in production.


Types

The object types returned by the operations above.

Errors

Errors use the standard GraphQL envelope:

{ "errors": [ { "message": "…", "path": ["…"] } ], "data": null }

A field returning null with no error usually means "no data" (e.g. an empty time window). If the upstream MONR server is temporarily unreachable, the API automatically serves recent sample data instead of erroring — the badge in the header shows sample data in that state, and flips back to live data when the upstream returns.

Data dictionary

Units & time. Train km/pk are kilometres; distanceMeters is metres. Times (departureTime, time, serviceDate…) are epoch milliseconds. circulationDate is dd/MM/yyyy; actualTime/diff are H:mm:ss; delay is seconds (grid) or a short string like 0m (live).

On the live trainPositions feed the upstream only populates id, status, km, direction, delay, trackCircuits (and sometimes the scheduled/estimated times). pk, speed, ertmsLevel/ertmsMode, origin, destination, operator and punctualityThreshold are not provided on the live feed and return null (shown as ). Use km for position.
FieldValues
Train.directionODD (Makkah→Madinah) · EVEN (reverse)
Train.statusRunning · Finished · Next to Run …
SinopticElement.typeCV (track circuit) · SV/SE (signal) · DSV (switch) · BAU (block)
ElementPart.statecolour/state code: I (inactive) · V · R · Az · B · A

Integrate

Plain GraphQL — queries & mutations over HTTP POST, subscriptions over WebSocket (graphql-transport-ws). Copy-paste for your stack.

Postman

A ready-made Postman collection with every query, mutation and subscription — all inputs are variables, so you just pick an environment and hit Send. Import the collection and the environment(s) below.

Setup

  1. In Postman: Import → drop in g2i2-graphql.postman_collection.json and the environment file(s) you want.
  2. Top-right environment selector → choose G2I2 — Local / Production / Staging.
  3. Open any request under Queries or Mutations and press Send. Edit inputs (lineId, train numbers …) once in the environment — every request picks them up.
The collection points at {{baseUrl}}/graphql; each environment sets baseUrl and wsUrl for you. Time-window inputs for sectionTrains / trainDetail are filled automatically by a pre-request script.

Authentication

Set the username / password variables to your credentials, then run Auth → Login once. Its test script saves the token into accessToken, and the collection's bearer auth attaches it to every request automatically. Re-run Login when the token expires.

Subscriptions in Postman

Subscriptions live in the Subscriptions (WebSocket) folder as WebSocket requests using the graphql-transport-ws subprotocol. Open one, click Connect, then send the two saved messages in order — connection_init, then the subscribe frame. Live frames stream into the Messages panel.

Live subscriptions are gated to coming soon in production — run them against the Local or Staging environment.
— frames standby
Operation
Response
// pick an operation, or press Run
Subscription
Live monitor
// choose a subscription, edit if you like, then Subscribe