{
  "openapi": "3.1.0",
  "info": {
    "title": "Dirtanian API",
    "version": "1.0.0",
    "description": "The Dirtanian public developer API — events, venues, outrides, activities, competitors, challenges, routes, segments and orders, over plain REST. Every response uses the `{ok, data|error}` envelope. Interactive docs: https://api.dirtanian.com",
    "contact": {
      "name": "Dirtanian",
      "url": "https://www.dirtanian.com"
    }
  },
  "servers": [
    {
      "url": "https://api.dirtanian.com/v1",
      "description": "Canonical"
    },
    {
      "url": "https://dirtanian.web.app/api/v1",
      "description": "Legacy mount (still served)"
    },
    {
      "url": "https://us-central1-dirtanian.cloudfunctions.net/apiV1",
      "description": "Direct Cloud Functions URL"
    }
  ],
  "security": [
    {
      "apiKey": []
    },
    {
      "bearer": []
    }
  ],
  "tags": [
    {
      "name": "events",
      "description": "Competitive events — races, enduros, rallies."
    },
    {
      "name": "venues",
      "description": "Riding venues — tracks, parks, trail networks."
    },
    {
      "name": "outrides",
      "description": "Group rides — social or host-led."
    },
    {
      "name": "activities",
      "description": "Recorded efforts — tracked or imported rides/drives."
    },
    {
      "name": "users",
      "description": "Competitor profiles — public fields only."
    },
    {
      "name": "challenges",
      "description": "Tiered community challenges."
    },
    {
      "name": "routes",
      "description": "Plotted, reusable trail routes."
    },
    {
      "name": "segments",
      "description": "Timed sections within a route."
    },
    {
      "name": "orders",
      "description": "Entries, bookings and purchases — read-only."
    },
    {
      "name": "keys",
      "description": "API key management — admins only."
    }
  ],
  "paths": {
    "/": {
      "get": {
        "summary": "API index",
        "description": "The map of the API — resources, filters and docs link. No auth needed.",
        "security": [],
        "responses": {
          "200": {
            "description": "Index",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Envelope"
                }
              }
            }
          }
        }
      }
    },
    "/events": {
      "get": {
        "tags": [
          "events"
        ],
        "summary": "List events",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/archived"
          },
          {
            "name": "discipline",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "displayStatus",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "seriesId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "parentEventUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "events"
        ],
        "summary": "Create an event",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExploreItemWrite"
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          }
        }
      }
    },
    "/events/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "events"
        ],
        "summary": "Fetch an event",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "events"
        ],
        "summary": "Update an event",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExploreItemWrite"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "events"
        ],
        "summary": "Delete an event",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/venues": {
      "get": {
        "tags": [
          "venues"
        ],
        "summary": "List venues",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/archived"
          },
          {
            "name": "discipline",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "displayStatus",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "venues"
        ],
        "summary": "Create a venue",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExploreItemWrite"
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/venues/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "venues"
        ],
        "summary": "Fetch a venue",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "venues"
        ],
        "summary": "Update a venue",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExploreItemWrite"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "venues"
        ],
        "summary": "Delete a venue",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/outrides": {
      "get": {
        "tags": [
          "outrides"
        ],
        "summary": "List outrides",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/archived"
          },
          {
            "name": "discipline",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "outrides"
        ],
        "summary": "Create an outride",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExploreItemWrite"
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/outrides/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "outrides"
        ],
        "summary": "Fetch an outride",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "outrides"
        ],
        "summary": "Update an outride",
        "requestBody": {
          "$ref": "#/components/requestBodies/ExploreItemWrite"
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "outrides"
        ],
        "summary": "Delete an outride",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/activities": {
      "get": {
        "tags": [
          "activities"
        ],
        "summary": "List activities",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "$ref": "#/components/parameters/archived"
          },
          {
            "name": "ownerUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exploreItemId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "routeUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "discipline",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "recorded",
                "imported"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "activities"
        ],
        "summary": "Create an activity",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Activity"
              }
            }
          }
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/activities/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "activities"
        ],
        "summary": "Fetch an activity",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "activities"
        ],
        "summary": "Update an activity",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "activities"
        ],
        "summary": "Delete an activity",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/users": {
      "get": {
        "tags": [
          "users"
        ],
        "summary": "List competitors (public fields)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      }
    },
    "/users/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "users"
        ],
        "summary": "Fetch a competitor (public fields)",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "users"
        ],
        "summary": "Update a competitor's public profile fields",
        "description": "Writable: name, surname, aboutMe, profilePicture, interests, activeRaceNumber. Everything else is platform-managed.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/challenges": {
      "get": {
        "tags": [
          "challenges"
        ],
        "summary": "List challenges",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "ended",
                "archived"
              ]
            }
          },
          {
            "name": "goalType",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "linkedEventId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "challenges"
        ],
        "summary": "Create a challenge",
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/challenges/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "challenges"
        ],
        "summary": "Fetch a challenge",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "challenges"
        ],
        "summary": "Update a challenge",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "challenges"
        ],
        "summary": "Delete a challenge",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/routes": {
      "get": {
        "tags": [
          "routes"
        ],
        "summary": "List routes",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "name": "ownerUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ownerType",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "raceClassUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "routes"
        ],
        "summary": "Create a route",
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/routes/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "routes"
        ],
        "summary": "Fetch a route",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "routes"
        ],
        "summary": "Update a route",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "routes"
        ],
        "summary": "Delete a route",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/segments": {
      "get": {
        "tags": [
          "segments"
        ],
        "summary": "List segments",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "name": "routeUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "createdByUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "segments"
        ],
        "summary": "Create a segment",
        "responses": {
          "201": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/segments/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "segments"
        ],
        "summary": "Fetch a segment",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "segments"
        ],
        "summary": "Update a segment",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "segments"
        ],
        "summary": "Delete a segment",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    },
    "/orders": {
      "get": {
        "tags": [
          "orders"
        ],
        "summary": "List orders (read-only)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limit"
          },
          {
            "$ref": "#/components/parameters/cursor"
          },
          {
            "name": "buyerUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "recipientUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "orderType",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "eventEntry",
                "mediaPurchase",
                "subscription",
                "venueBooking",
                "outrideBooking",
                "clubMembership"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "exploreItemUid",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      }
    },
    "/orders/{uid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/uid"
        }
      ],
      "get": {
        "tags": [
          "orders"
        ],
        "summary": "Fetch an order",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      }
    },
    "/keys": {
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "List API keys (admin)",
        "responses": {
          "200": {
            "$ref": "#/components/responses/List"
          }
        }
      },
      "post": {
        "tags": [
          "keys"
        ],
        "summary": "Create an API key (admin)",
        "description": "The plaintext key is returned exactly once. Sign in with an admin Firebase ID token — API keys can't mint keys.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "scopes"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "expiresAt": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            }
          },
          "responses": {
            "201": {
              "$ref": "#/components/responses/Single"
            }
          }
        }
      }
    },
    "/keys/{keyId}": {
      "parameters": [
        {
          "name": "keyId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "keys"
        ],
        "summary": "Fetch a key summary (admin)",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "patch": {
        "tags": [
          "keys"
        ],
        "summary": "Rename / re-scope / enable a key (admin)",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Single"
          }
        }
      },
      "delete": {
        "tags": [
          "keys"
        ],
        "summary": "Revoke a key (admin)",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Deleted"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "An admin-issued key: `dirt_live_...`. Also accepted as `Authorization: Bearer dirt_live_...`."
      },
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Firebase ID token — admins get full access; profiles with the `api` role get read access."
      }
    },
    "parameters": {
      "uid": {
        "name": "uid",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The resource's document uid."
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        },
        "description": "Page size (1–100)."
      },
      "cursor": {
        "name": "cursor",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Opaque cursor from the previous page's `meta.nextCursor`."
      },
      "archived": {
        "name": "archived",
        "in": "query",
        "schema": {
          "type": "boolean",
          "default": false
        },
        "description": "Include archived records."
      }
    },
    "requestBodies": {
      "ExploreItemWrite": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ExploreItemWrite"
            }
          }
        }
      }
    },
    "responses": {
      "List": {
        "description": "A page of results.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ListEnvelope"
            }
          }
        }
      },
      "Single": {
        "description": "A single resource.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            }
          }
        }
      },
      "Deleted": {
        "description": "Deletion/revocation confirmation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Envelope"
            }
          }
        }
      },
      "Invalid": {
        "description": "The request needs a tweak — the message says exactly what.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "NotFound": {
        "description": "Nothing lives at this address.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {},
          "meta": {
            "type": "object"
          }
        },
        "required": [
          "ok",
          "data"
        ]
      },
      "ListEnvelope": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "type": "array",
            "items": {}
          },
          "meta": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer"
              },
              "limit": {
                "type": "integer"
              },
              "hasMore": {
                "type": "boolean"
              },
              "nextCursor": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthenticated",
                  "forbidden",
                  "not_found",
                  "method_not_allowed",
                  "invalid_request",
                  "internal"
                ]
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "ExploreItemWrite": {
        "type": "object",
        "description": "Writable explore-item fields (events/venues/outrides). Server-managed fields — uid, createdAt, updatedAt, exploreType, ticketsSold, entriesSold, areaBookingsSold, outrideItemsSold, commissionRate, rating — are set by the platform and rejected if sent.",
        "required": [
          "exploreItemName"
        ],
        "properties": {
          "exploreItemName": {
            "type": "string",
            "description": "Display name."
          },
          "exploreItemDescription": {
            "type": "string"
          },
          "eventDateTimes": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            }
          },
          "posterImage": {
            "type": "string",
            "format": "uri"
          },
          "exploreItemImages": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "venue": {
            "type": "object",
            "description": "Embedded venue details (name, location, address, operatingHours)."
          },
          "ownerUids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Owning user uids — drives ownership-scoped access in the apps."
          },
          "discipline": {
            "type": "string",
            "description": "Discipline slug, e.g. `enduro`, `mx`, `4x4`."
          },
          "raceClasses": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "gpxRoutes": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "outrideModel": {
            "type": "object",
            "description": "Outrides only: outRideType (social|hosted), terrain, meetingPoint, maxRiderCount…"
          },
          "archived": {
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "Activity": {
        "type": "object",
        "required": [
          "ownerUid",
          "name"
        ],
        "properties": {
          "uid": {
            "type": "string",
            "readOnly": true
          },
          "ownerUid": {
            "type": "string",
            "description": "The competitor who did the effort."
          },
          "name": {
            "type": "string"
          },
          "discipline": {
            "type": "string"
          },
          "source": {
            "type": "string",
            "enum": [
              "recorded",
              "imported"
            ]
          },
          "exploreItemId": {
            "type": "string",
            "description": "Linked event/venue/outride uid."
          },
          "routeUid": {
            "type": "string"
          },
          "distance": {
            "type": "number",
            "description": "Metres."
          },
          "duration": {
            "type": "number",
            "description": "Seconds."
          },
          "averageSpeed": {
            "type": "number"
          },
          "maxSpeed": {
            "type": "number"
          },
          "gpxUrl": {
            "type": "string",
            "format": "uri"
          },
          "startPoint": {
            "type": "object",
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              }
            }
          },
          "endPoint": {
            "type": "object",
            "properties": {
              "latitude": {
                "type": "number"
              },
              "longitude": {
                "type": "number"
              }
            }
          }
        },
        "additionalProperties": true
      },
      "UserPublic": {
        "type": "object",
        "description": "The public projection of a competitor profile. Personal data (email, phone, dob, address, medical info, dependents, payment details) is never exposed by the API.",
        "properties": {
          "uid": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "surname": {
            "type": "string"
          },
          "profilePicture": {
            "type": "string",
            "format": "uri"
          },
          "aboutMe": {
            "type": "string"
          },
          "activeRaceNumber": {
            "type": "string"
          },
          "clubIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "interests": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "achievements": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "followerCount": {
            "type": "integer"
          },
          "followingCount": {
            "type": "integer"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Order": {
        "type": "object",
        "description": "Read-only — the payment engine owns every write.",
        "properties": {
          "uid": {
            "type": "string"
          },
          "orderNumber": {
            "type": "string"
          },
          "orderType": {
            "type": "string",
            "enum": [
              "eventEntry",
              "mediaPurchase",
              "subscription",
              "venueBooking",
              "outrideBooking",
              "clubMembership"
            ]
          },
          "status": {
            "type": "string"
          },
          "buyerUid": {
            "type": "string"
          },
          "recipientUid": {
            "type": "string"
          },
          "currency": {
            "type": "string",
            "const": "ZAR"
          },
          "subtotal": {
            "type": "number"
          },
          "platformFeeTotal": {
            "type": "number"
          },
          "totalAmount": {
            "type": "number"
          },
          "exploreItemUid": {
            "type": "string"
          },
          "paidAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKeySummary": {
        "type": "object",
        "properties": {
          "keyId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "First characters of the key, for recognition — the full key is never stored."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": "string",
            "format": "date-time"
          },
          "requestCount": {
            "type": "integer"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          },
          "revokedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    }
  }
}