# OpenSportTaxonomy > One canonical, hierarchical, open vocabulary of sports for fitness applications, with > bidirectional mappings to every major platform. A sport is a single string: dots (`.`) > separate a sport from its disciplines, plusses (`+`) attach modifiers. Example: > `cycling.road+stationary+virtual`. The string is the identity. The machine-readable > source of truth for the catalogue, labels, modifiers, and version is one file, > `schema.yaml`. ## The canonical string format - A sport string is a `code` plus zero or more `+modifier`s, modifiers sorted alphabetically and de-duplicated. The canonical string IS the identity — not catalogue membership. - `code` is a dotted hierarchy: `cycling` contains `cycling.road`, `cycling.gravel`, etc. Querying a parent should include its children. - `+modifier` describes a circumstance that does not change the movement (`+stationary`, `+virtual`, `+assisted`, `+roller`, `+race`). Independent; absence means unspecified, not "the opposite". - Well-formed grammar (segment = `[a-z]+(?:_[a-z]+)*`): `/^[a-z]+(?:_[a-z]+)*(?:\.[a-z]+(?:_[a-z]+)*)*(?:\+[a-z]+(?:_[a-z]+)*)*$/` - Three nested validity tiers: well-formed ⊆ known-atoms (code + modifiers are declared) ⊆ standard (exact string is a catalogue entry). Any well-formed string is valid and storable. - Structured equivalent: `{ "sport": "cycling.road", "modifiers": ["stationary", "virtual"] }`. The string is canonical; the structured form is derived from it. ## Source of truth (fetch these directly) - [schema.yaml](https://raw.githubusercontent.com/sweatstack/open-sport-taxonomy/main/schema.yaml): The catalogue. Two flat lists — `sports:` (each a canonical `sport` string + hand-crafted `label`) and `modifiers:` (each a `code`, `label`, optional `group`), plus `version:`. To label a string: exact match in `sports:` wins; else look up the bare code's label (a catalogue lookup, not string reversal) and append ` (modifier labels)` — only derive from the raw string for an uncatalogued code. This `raw.githubusercontent.com` URL is canonical; pin a spec snapshot via tag, e.g. `.../spec/v0.10.0/schema.yaml`. (The docs domain `/schema.yaml` 302-redirects here as a convenience.) - [mappings/](https://github.com/sweatstack/open-sport-taxonomy/tree/main/mappings): Bidirectional platform translation tables — `strava.yaml`, `garmin_fit.yaml`, `garmin_training_api.yaml`, `apple_healthkit.yaml`, `polar.yaml`, `suunto.yaml`, `wahoo.yaml`. Decode = platform → most-specific OST string; encode = OST → platform (lossy). Unmatched values use each file's `fallback`. ## Specifications - [docs/taxonomy.md](https://github.com/sweatstack/open-sport-taxonomy/blob/main/docs/taxonomy.md): Terminology, the rule for what is a modality vs discipline vs modifier, the three validity tiers, and operations (resolve, sub-sport containment, modifier groups). - [docs/translation.md](https://github.com/sweatstack/open-sport-taxonomy/blob/main/docs/translation.md): Language-agnostic encode/decode specification, coarsening rules, `null` vs `generic`. - [docs/reference.md](https://github.com/sweatstack/open-sport-taxonomy/blob/main/docs/reference.md): Generated browser of every standard sport, combination, and modifier. ## Reference implementation - [python/README.md](https://github.com/sweatstack/open-sport-taxonomy/blob/main/python/README.md): The `open-sport-taxonomy` Python package (`pip install open-sport-taxonomy`). `Sport("cycling+stationary").label` → "indoor cycling"; `.is_standard`, `.uses_known_atoms`, `.resolve()`; `platforms.garmin_fit.decode(2, 0)` / `.encode(...)`. Exposes `version` (package) and `taxonomy_version` (spec). ## Optional - [For-agents page](https://open-sport-taxonomy.sweatstack.no/for-agents.html): The above as a single readable HTML brief. - [GitHub repository](https://github.com/sweatstack/open-sport-taxonomy): Full source, issues (report a missing sport here), and `AGENTS.md` for contributing.