QDG Knowledge Base Read-only viewer QWebHub
general

TroyenAPI Reference

Version 1 · Create initial TroyenAPI endpoint reference from controller source, versioning/auth config, and Program.cs

TroyenAPI Reference

TroyenAPI is the customer/external-facing, read-only REST API over the Mongo-backed racing data (meetings, races, race cards, speed maps, stats). Source: TroyenAPI/Controllers/.

Correction to earlier assumption: this API is not JWT-authenticated. Auth is a custom opaque API-key scheme (Authorization: Bearer td-...), validated in-process against a client_api_keys Mongo collection. JWT only exists in the separate TroyenDataWeb admin app. See Auth below.

Routing & versioning

  • Base route: api/v{version:apiVersion}/{Controller}/... (TroyenAPI/Controllers/BaseApiController.cs).
  • Versioning (TroyenAPI/Extensions/ApiVersioningExtensions.cs): default version 1.0, AssumeDefaultVersionWhenUnspecified = true. v1 controllers carry no [ApiVersion] attribute; v2 controllers are [ApiVersion("2.0")]. Version can also be requested via Accept: application/json;v=2.0 (MediaTypeApiVersionReader).
  • v1 controllers are class-level [AllowAnonymous]; v2 controllers have no [AllowAnonymous] and inherit [Authorize] from the base — but see the auth caveat below, since enforcement currently depends on an AllowEmptyKey setting.
  • Swagger/ReDoc: /swagger/{version}/swagger.json per version; ReDoc UI (DocumentTitle: "Troyon Data Helpers API" — likely a copy-paste leftover) is pinned to the v1 spec only. wwwroot/APIDefinition/v1.json is a separate static OpenAPI file not verified against the live Swagger output — treat as possibly stale.
  • CORS: AllowAll (any origin/method/header). HTTPS redirection is disabled in Program.cs.

v1 endpoints

api/v1/Meeting

Verb Route Params Returns
GET meeting-list date (required, yyyy-MM-dd), discipline (T|H|G), countryISO/ISO2/ISO3 List<MeetingListItem>
GET extended-meeting-list same + template (customer-template token; 403 if unresolvable) List<Meetingv2> with embedded race list

api/v1/Race

Verb Route Params Returns
GET race-list/{meetingId} path meetingId List<Race> (non-hidden meetings only, unordered)
GET racecard/{raceId} path raceId; query language="EN", style="AU", ignoreCache=false RaceCard (v1 shape; form lines truncated to last 3)
GET race-search date, countryCode, course, discipline="T", meetingId, raceNumber=1, isIndirect=false, source disambiguation list, or a single RaceCardV2 if exactly one match. Writes lookup corrections via SaveLookup when source given.
GET speedmap/{raceId} path raceId SpeedMap
GET stats/{raceId} path raceId { runnerId, runnerName, stats }[]
GET extended-race-list/{raceId} path raceId RaceCard (includes previous-run form data)
GET next-to-jump discipline="T,H,G", countryISO="", limit=15 (1–100), combined=false { status, timestamp, upcomingRaces: [] }

api/v1/OpenUtility

Verb Route Returns
GET heart-beat plain string "API up and running : Server time - {time}" — health check

v2 endpoints

Same routes/params as v1 unless noted, under api/v2/..., requiring [Authorize] (TokenScheme):

  • Meeting: meeting-list (identical to v1, no template support difference); extended-meeting-list (no template param at all in v2).
  • Race: race-list/{meetingId} (doesn't require non-hidden meeting, orders by race number); racecard/{raceId} returns RaceCardV2 — maps distances to end in "f", normalizes region codes to ISO, reformats finish times, and reads a client header for customer-template shaping; speedmap/{raceId}, stats/{raceId}, extended-race-list/{raceId}, next-to-jump — identical to v1.

Internal-only (not part of the customer surface)

  • POST internal/template-cache/reload (TemplateCacheController) — hot-reloads customer data templates. Gated by an X-Internal-Secret header compared against config, not the customer API-key scheme (/internal is in ApiKeyMiddleware.SkipPrefixes). Exclude from any customer-published version of this doc.

Auth

  • No login/token endpoint exists inside TroyenAPI itself. API keys (prefix td-..., AES-encrypted, SHA256(PassKey)-derived) are minted via Common/Services/ApiKeyService.CreateKeyAsync and stored in client_api_keys in Mongo — the issuance/admin UI for this was not found inside TroyenAPI and is likely in TroyenDataWeb (worth a follow-up look there if the issuance side needs documenting).
  • Validation happens in-process (ApiKeyMiddleware + TokenAuthenticationHandler, scheme name TokenScheme) — decrypt, DB lookup, expiry/allowed-endpoint/rate-limit checks. No external auth service (e.g. QDBAuth) is involved in this path.
  • Operational caveat, verify before relying on it: ApiKeySettings:AllowEmptyKey = true in appsettings.json means requests with no/malformed Authorization header are currently let through as anonymous rather than rejected with 401 — combined with all v1 controllers being [AllowAnonymous], auth is effectively optional in the current configuration for both v1 and v2. Confirm the deployed value of AllowEmptyKey per environment before describing auth as "required" anywhere customer-facing.

Ports / environment

  • Local dev: http://localhost:6101 (Kestrel profile) / http://localhost:53537 (IIS Express) — TroyenAPI/Properties/launchSettings.json.
  • Docker: EXPOSE 8080 (TroyenAPI/Dockerfile).
  • appsettings.json APIBase section lists base URLs for other upstream services (Internal, External, Silk) this API calls — not this API's own public base URL, which isn't set explicitly anywhere found.
  • appsettings.json/launchSettings.json contain live credentials (Mongo connection string, S3 keys, ApiKeySettings:PassKey, InternalSettings:ReloadSecret) — not reproduced here; scrub before any external sharing and rotate if ever exposed.

Known documentation gap

TroyenAPIBruno/*.bru (Meetings.bru, Races.bru, RaceCard.bru, DataDump.bru) — a Bruno collection meant to exercise this API — target a stale host/route shape (http://102.218.213.42:1540/RaceContoller/...) that doesn't match any current controller (there's also no data-dump endpoint in the codebase at all). Treat this collection as outdated; regenerate example requests from the live /swagger/v1/swagger.json / /swagger/v2/swagger.json instead of trusting it.

Updated by Claude on Aug. 11, 2026, 9:46 a.m. · Task: updatewiki create project API doc