{
  "openapi": "3.0.3",
  "info": {
    "title": "Tino Maps API",
    "version": "1.1.0",
    "termsOfService": "https://maps.tino.mn/terms",
    "contact": {
      "name": "Tino Maps",
      "url": "https://maps.tino.mn",
      "email": "sales@tino.mn"
    },
    "license": {
      "name": "Proprietary — see the Terms of Use",
      "url": "https://maps.tino.mn/terms"
    },
    "x-logo": {
      "url": "https://maps.tino.mn/ui/img/tino-logo-white.webp",
      "altText": "Tino Maps",
      "backgroundColor": "#3566E6",
      "href": "https://maps.tino.mn/"
    },
    "description": "Places, addresses, routing and live traffic for **Mongolia**. Search understands Mongolian Cyrillic, Latin transliteration and the way Mongolian addresses are written. One REST API, one key.\n\nPlace responses follow the Grab Places Partner format, so an integration built for that contract needs little adaptation.\n\n# Getting started\n\n1. Create an account in the [developer console](https://maps.tino.mn/console) and verify your email address.\n2. Create an API key in the console. The key is shown once: keep it in your server's secret store.\n3. Call the API from your backend. The base URL is `https://maps.tino.mn`, and every endpoint in this reference is a `GET` that returns JSON.\n\n```bash\ncurl -G \"https://maps.tino.mn/maps/place/v2/search\" \\\n  --data-urlencode \"keywords=улсын их дэлгүүр\" \\\n  --data-urlencode \"language=en\" \\\n  -H \"X-API-Key: $TINO_MAPS_KEY\"\n```\n\nThe Free plan is self-serve: 1,000 requests a day at 1 request per second. See [Plans and limits](#section/Plans-and-limits).\n\nThe playground on the [Tino Maps home page](https://maps.tino.mn/) is a demo, not part of the API. It is rate-limited per visitor, returns basic place fields only and may not be called from your own software. Build with your own key.\n\n# Authentication\n\nEvery request needs an API key.\n\n| Send the key as | Status |\n|---|---|\n| `X-API-Key: <key>` header | Recommended. |\n| `Authorization: Bearer <key>` header | Accepted. |\n| `api_key=<key>` query parameter | **Deprecated.** Still accepted so that existing integrations keep working, but a key in a URL leaks into server logs, proxies and browser history. Move to a header: this method will be removed. |\n\n**Keep keys on your server.** An API key is a secret: anyone who has it can spend your quota and read the API in your name. Do not put it in browser JavaScript or in a mobile app you distribute. Call Tino Maps from your backend and send your clients only the results. Use a separate key for each application, so that you can revoke one without touching the others, and revoke a key in the console as soon as it may have leaked.\n\nTo show a map on a website, use a Map Application key instead (see [Map display](#section/Map-display)). It works only with the map SDK, and only from the website origins you list.\n\n| HTTP | `status.code` | Meaning |\n|---|---|---|\n| 401 | `UNAUTHORIZED` | No key was sent. |\n| 401 | `INVALID_API_KEY` | The key does not exist. |\n| 403 | `INVALID_API_KEY` | The key has been revoked. |\n| 403 | `EXPIRED_API_KEY` | The key has expired. |\n| 403 | `ACCOUNT_DISABLED` | The account that owns the key has been disabled. |\n| 403 | `MAP_APPLICATION_KEY_SCOPE` | A Map Application key was used for this API. Use a regular API key. |\n\n<!-- ReDoc-Inject: <security-definitions> -->\n\n# Plans and limits\n\n| Plan | Requests per day | Requests per second | How to get it |\n|---|---|---|---|\n| Free | 1,000 | 1 | Self-serve in the [console](https://maps.tino.mn/console) |\n| Basic | 50,000 | 10 | [sales@tino.mn](mailto:sales@tino.mn) |\n| Pro | 500,000 | 50 | [sales@tino.mn](mailto:sales@tino.mn) |\n| Enterprise | Custom | Custom | [sales@tino.mn](mailto:sales@tino.mn) |\n\n- **Limits apply per account.** Every key the account holds or has held, deleted keys included, draws from the same daily quota and the same per-second limit, so creating more keys does not add capacity.\n- An account can hold up to 5 active keys, Map Application keys included, and can create at most 6 keys in any 24 hours; deleted keys count toward the 6.\n- The daily quota resets at midnight, Ulaanbaatar time (UTC+8).\n- A request counts toward the quota once its key is accepted and it is within the limits, whatever the endpoint then answers: `304 Not Modified` and the endpoint's own 4xx and 5xx answers count too, such as `400` for a missing parameter or `404` for an unknown place. A request refused before that does not count: the key and account refusals listed under [Authentication](#section/Authentication) (`401`, `403`) and `429`. Nor does a request to a path or with a method the API does not have (`404`, `405`).\n- To move to a paid plan, email [sales@tino.mn](mailto:sales@tino.mn) from your account's address with the volume you expect.\n\nOver a limit, the API answers `429 Too Many Requests` with a `Retry-After` header in seconds. The body repeats it as `data.retry_after`.\n\n| `status.code` | Meaning | `Retry-After` |\n|---|---|---|\n| `RATE_LIMIT_EXCEEDED` | Too many requests this second. | `1`. Slow down, then retry. |\n| `DAILY_LIMIT_EXCEEDED` | Today's quota is used up. | Seconds until the quota resets. Do not retry before then. |\n\n# Responses and errors\n\nResponses are JSON. Every response carries a `uuid`, unique to the response, and a `status`. A successful response has `status.code` `SUCCESS`:\n\n```json\n{\n  \"uuid\": \"6f1c2d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f\",\n  \"status\": { \"code\": \"SUCCESS\", \"message\": \"Request successful\" },\n  \"places\": []\n}\n```\n\nAn error uses the same envelope, with the error in `status`:\n\n```json\n{\n  \"uuid\": \"0b9a8c7d-6e5f-4a3b-2c1d-0e9f8a7b6c5d\",\n  \"status\": { \"code\": \"INVALID_REQUEST\", \"message\": \"Parameter 'location' is required (format: latitude,longitude)\" }\n}\n```\n\nBranch on the HTTP status and `status.code`. `status.message` is written for people and may change.\n\n| HTTP | `status.code` | Meaning |\n|---|---|---|\n| 400 | `INVALID_REQUEST` | A required parameter is missing or malformed. |\n| 400 | `VALIDATION_ERROR` | A parameter is out of range, such as search text longer than 200 characters. |\n| 401, 403 | | See [Authentication](#section/Authentication). |\n| 404 | `NOT_FOUND` | The place or path does not exist. |\n| 404 | `ROUTE_NOT_FOUND` | There is no route between the coordinates. |\n| 405 | `INVALID_REQUEST` | The HTTP method is not supported for this path. |\n| 429 | `RATE_LIMIT_EXCEEDED`, `DAILY_LIMIT_EXCEEDED` | See [Plans and limits](#section/Plans-and-limits). |\n| 500 | `INTERNAL_ERROR` | Something failed on our side. Retry with backoff. |\n| 502 | `UPSTREAM_UNAVAILABLE` | Routing is temporarily unavailable. Retry with backoff. |\n| 503 | `INTERNAL_ERROR`, `UPSTREAM_UNAVAILABLE` | A feature is temporarily unavailable, such as the traffic layer or one travel mode. |\n\nEvery response has an `X-Request-ID` header. Include it when you contact us about a request.\n\n**Forward compatibility.** Fields and values are added without notice, for example a new field on a place or a new `business_type` value. Ignore fields you do not know, treat a `business_type` you do not know as `other`, and do not depend on the order of keys.\n\n**Place ids.** `poi_id` is an opaque string. Most ids are UUIDs, but some results carry an id in another format. Store it exactly as returned and pass it back unchanged. A result that is not a stored place, such as an address point in `nearby` or an area result of `reverse-geo` with `mode=explore`, has an empty `poi_id`: it has no detail, and must not be used as a key.\n\n**Caching.** Many read endpoints send an `ETag` and `Cache-Control: private, max-age=…`. Send the ETag back in `If-None-Match` to get `304 Not Modified` when nothing has changed; a 304 still counts toward your quota. A response belongs to the key that requested it: never serve one caller's cached response to another.\n\n# Coordinates and languages\n\n- Places, entrances and `/maps/v1/matrix` take a point as `lat,lon`, for example `location=47.918,106.917`.\n- `/maps/v1/direction` and `/maps/v1/directions/multi` take `coordinates=lon,lat`, unless you add `lat_first=true`.\n- `/maps/v1/traffic/cells` takes `bbox=minLon,minLat,maxLon,maxLat`.\n- Tino Maps covers Mongolia. `nearby`, `explore`, `trending` and `reverse-geo` return an empty list for a point outside Mongolia's bounding box (latitude 41.5 to 52.2, longitude 87.7 to 119.9).\n- The `language` parameter localizes names, addresses and `rider_label`: `mn` (the default), `en`, `ru`, `zh`, `ko` or `ja`. Any other value means `mn`. Where a place has no translation, `ru` falls back to the Mongolian Cyrillic name and the other languages to the English name or a Latin transliteration.\n\n# Custom places\n\nPublish your own places, such as branches, parcel lockers or pickup points, and the same endpoints serve them alongside every other place.\n\n**Adding places.** In the [console](https://maps.tino.mn/console), open **Places** and choose **New place**, or **Import many** to paste up to 500 places at a time. An import takes CSV with the header `name,category,lat,lon,address,phone,district,khoroo` (`city`, `name_en` and `website` columns are also read) or a JSON array of objects with the fields below. An import of more than 500 rows is rejected as a whole, so split a large file into batches. A row that fails validation is skipped and listed with the reason.\n\n| Field | Rules |\n|---|---|\n| `name` | Required, 1 to 200 characters. |\n| `category` | Required. One of the [business types](#tag/Places), for example `parcel_locker`. |\n| `lat`, `lon` | Required. Inside Mongolia. In the console, drag the marker on the map. |\n| `name_en`, `address`, `english_address`, `description`, `keywords` | Optional. `keywords` is a list of extra search terms. |\n| `city`, `district` | Optional, up to 100 characters each. |\n| `khoroo`, `horoolol`, `building` | Optional, up to 50 characters each. |\n| `phone` | Optional, up to 50 characters. |\n| `website`, `email` | Optional, up to 255 characters each. |\n| `operating_hours` | Optional. A JSON object by day, for example `{\"mon\": {\"open\": \"09:00\", \"close\": \"18:00\"}, \"sun\": {\"closed\": true}}`. |\n| `icon_url` | Optional logo. Upload it in the console: JPEG, PNG or WebP up to 2 MiB; SVG is not accepted. Save one logo as your default to reuse it on every place. |\n\n**Visibility.** The places of a verified account go live at once. A new account is unverified: its places are marked *Pending review* in the console until Tino verifies the account, which publishes them. Pending places are not hidden from every endpoint, so do not add a place before you are ready for people to see it. To ask for verification, email [sales@tino.mn](mailto:sales@tino.mn) from your account's address.\n\n**Reading your places.** Add `owner=<your account id>` to [search](#operation/search) or [nearby](#operation/nearby), with `business_type` if you need it. An owner-scoped `nearby` returns every match, nearest first. The console does not show your account id yet: email [sales@tino.mn](mailto:sales@tino.mn) and we will send it.\n\n**Deleting.** A deleted place leaves search and nearby results at once. Its id keeps resolving in [detail](#operation/placeDetail), so saved references do not break, and you can restore it from the console.\n\n# Map display\n\n**Beta, by invitation.** Map Applications let a website show the Tino Maps basemap with a key that is restricted to that website. They are not open for self-serve sign-up yet: email [sales@tino.mn](mailto:sales@tino.mn) with your website's domain, and we will enable your account. Outside the beta, the map SDK refuses Map Application keys with `403 MAP_APPLICATIONS_BETA`.\n\nIn the console's **Map Applications** tab, create a web application and list the origins it runs on. You get a Map Application key and this snippet:\n\n```html\n<div id=\"map\" style=\"height:480px\"></div>\n<script src=\"https://maps.tino.mn/ui/tino-maps.js\"></script>\n<script>\n  TinoMaps.create({\n    key: \"YOUR_MAP_APPLICATION_KEY\",\n    container: \"map\",\n    language: \"mn\"\n  });\n</script>\n```\n\n- **Allowed origins.** A web Map Application key works only on pages served from the origins you list. An origin is a scheme, a host and, when it is not the default, a port, such as `https://www.example.mn`: no paths and no wildcards, at most 20 per application. `https://example.mn` and `https://www.example.mn` are different origins. The map SDK refuses any other origin with `403 FORBIDDEN`.\n- **Map SDK only.** A Map Application key works only with the map SDK. The Places, Routing and Traffic APIs refuse it with `403 MAP_APPLICATION_KEY_SCOPE`, so call them from your backend with a regular API key.\n- `language` is `mn` or `en`; anything else shows Mongolian. `TinoMaps.create` returns a Promise for the map, and the Promise rejects when the key or the origin is refused, so add a `.catch` that handles it. The rejection is an `Error` whose `status` is the HTTP status and whose `code` is the server's code, such as `FORBIDDEN` or `MAP_APPLICATIONS_BETA`.\n- `tino-maps.js` is for web pages. There is no Tino SDK for iOS or Android yet; for a native app, talk to [sales@tino.mn](mailto:sales@tino.mn).\n- Keep the map's logo and attribution visible. The script shows them by default.\n\n# Attribution and terms\n\nWherever you show data from the Tino Maps API, show the attribution **© Tino Maps** near it: on a map, in a corner of the map; in a list, below the list. Keep any other attribution the API or the map display returns.\n\nThe [Tino Maps API Terms of Use](https://maps.tino.mn/terms) are a draft under legal review and are not in force yet. In short, the draft asks you to use the API only through your own key; not to bulk-download, scrape or crawl the data, or use it to build a competing dataset or map; to cache only what serving your application needs (place ids may be stored, other content for at most 30 days); and not to resell or redistribute the data.\n\nQuestions: [sales@tino.mn](mailto:sales@tino.mn)."
  },
  "servers": [
    {
      "url": "https://maps.tino.mn",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Places",
      "description": "Search, autocomplete, nearby points, geocoding, reverse geocoding and place details.\n\nEvery place has a `business_type`. The current values are below; more may be added, and a value you do not recognise should be treated as `other`. `business_type_label` carries the same category as a display label in the requested language.\n\n| `business_type` | Label (English) |\n|---|---|\n| `restaurant` | Restaurant |\n| `cafe` | Cafe |\n| `bar` | Bar |\n| `supermarket` | Supermarket |\n| `shopping` | Shopping |\n| `pharmacy` | Pharmacy |\n| `hospital` | Hospital |\n| `school` | School |\n| `bank` | Bank |\n| `atm` | ATM |\n| `gas_station` | Petrol station |\n| `hotel` | Hotel |\n| `government` | Government office |\n| `entertainment` | Entertainment |\n| `culture` | Arts and culture |\n| `religion` | Place of worship |\n| `park` | Park |\n| `sports` | Sports |\n| `gym` | Gym |\n| `beauty` | Beauty |\n| `automotive` | Automotive |\n| `services` | Services |\n| `tourism` | Travel and tourism |\n| `transport` | Transport |\n| `parking` | Parking |\n| `parcel_locker` | Parcel locker |\n| `building` | Building |\n| `residential` | Residential |\n| `other` | Other |"
    },
    {
      "name": "Explore",
      "description": "Discovery for map views and feeds — a ranked selection of notable places around a point, and the places worth visiting at this time of day."
    },
    {
      "name": "Entrances",
      "description": "Building entrances and curated pickup points, with the floor and entrance number where known. Coverage is limited and growing: an empty list means that no entrance is recorded, not that the building has none.\n\n[Reverse geocoding](#operation/reverseGeocode) also attaches `nearest_entrance` to its results when an entrance of the building is recorded."
    },
    {
      "name": "Routing",
      "description": "Directions for driving, walking, motorcycle, tricycle and cycling, all three of walking, two-wheel and four-wheel in one call, and a travel-time matrix. Driving routes carry a live-traffic adjustment where Tino has enough probe data."
    },
    {
      "name": "Traffic",
      "description": "Live congestion for a map viewport, measured from the GPS of Tino drivers. **A road with no data is unknown, never free-flowing.**"
    }
  ],
  "security": [
    {
      "ApiKeyHeader": []
    },
    {
      "BearerAuth": []
    }
  ],
  "paths": {
    "/maps/place/v2/search": {
      "get": {
        "tags": [
          "Places"
        ],
        "operationId": "search",
        "summary": "Search places",
        "description": "Full-text search over place names and addresses. It understands Mongolian Cyrillic, Latin transliteration, English and the stored translations of names, as well as common typos and abbreviations. Add `location` to rank places near the user higher.\n\n`keywords` is optional when you filter by `district`, `khoroo`, `owner` or `business_type`, for example to list every parcel locker in one khoroo.\n\nWhen `keywords` is set, `total` is an estimate: use it to decide whether to fetch another page, not as an exact count.",
        "parameters": [
          {
            "name": "keywords",
            "in": "query",
            "description": "Search text in any supported language, up to 200 characters. Required unless `district`, `khoroo`, `owner` or `business_type` is set.",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "example": "хаан банк"
          },
          {
            "name": "location",
            "in": "query",
            "description": "`lat,lon` of the user, to rank nearby places higher. Ignored when it is malformed or outside Mongolia.",
            "schema": {
              "type": "string"
            },
            "example": "47.918,106.917"
          },
          {
            "name": "radius",
            "in": "query",
            "description": "The search area around `location`, in meters, at most 100,000. Left out, a search with `keywords` uses 10,000. Matches inside the area come first, an exact name match is returned even from farther away, and when nothing inside the area matches, the search widens. Ignored without `location`.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 100000
            }
          },
          {
            "$ref": "#/components/parameters/BusinessTypeFilter"
          },
          {
            "name": "district",
            "in": "query",
            "description": "Only places in this district (дүүрэг), by exact name. Makes `keywords` optional.",
            "schema": {
              "type": "string"
            },
            "example": "Баянгол"
          },
          {
            "name": "khoroo",
            "in": "query",
            "description": "Only places in a khoroo (хороо) whose name contains this text. Makes `keywords` optional.",
            "schema": {
              "type": "string"
            },
            "example": "16"
          },
          {
            "$ref": "#/components/parameters/OwnerParam"
          },
          {
            "name": "city",
            "in": "query",
            "description": "Only places in this city, by exact name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results. Larger values are capped at 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of results to skip, for paging. Larger values are capped at 1,000.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000,
              "default": 0
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "`relevance` (the default) or `distance`, nearest first. `distance` needs `location`.",
            "schema": {
              "type": "string",
              "enum": [
                "relevance",
                "distance"
              ],
              "default": "relevance"
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`delivery` ranks places you can order from (restaurants, cafes, supermarkets, shops, pharmacies, bars) higher; typed addresses still match. Other values leave the ranking unchanged.",
            "schema": {
              "type": "string",
              "enum": [
                "ride",
                "delivery",
                "explore"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching places.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/autocomplete": {
      "get": {
        "tags": [
          "Places"
        ],
        "operationId": "autocomplete",
        "summary": "Autocomplete place names",
        "description": "Suggestions for partial text while the user types, for example a pickup or a destination. Matches the start of names in any supported language and tolerates typos.",
        "parameters": [
          {
            "name": "input",
            "in": "query",
            "required": true,
            "description": "The text typed so far, 2 to 200 characters, in any supported language.",
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            },
            "example": "шангри"
          },
          {
            "name": "location",
            "in": "query",
            "description": "`lat,lon` of the user, to rank nearby suggestions higher. Ignored when malformed.",
            "schema": {
              "type": "string"
            },
            "example": "47.918,106.917"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of suggestions. Larger values are capped at 20.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "default": 7
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`delivery` ranks places you can order from higher. Other values leave the ranking unchanged.",
            "schema": {
              "type": "string",
              "enum": [
                "ride",
                "delivery",
                "explore"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suggestions, best first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/nearby": {
      "get": {
        "tags": [
          "Places"
        ],
        "operationId": "nearby",
        "summary": "Places near a point",
        "description": "Places around a point. What you get depends on `mode`:\n\n- **`ride`**: every active point within the radius, nearest first, including residential buildings, parking and unnamed address points, so that a driver can be sent to any of them. Use it to choose pickup and drop-off points. `radius` defaults to 1,000 m; `limit` defaults to 10, at most 50.\n- **`explore`**: the same ranked selection of notable places as [explore](#operation/explore). `radius` defaults to 2,000 m; `limit` defaults to 30, at most 100.\n- **`delivery`**: the same selection, with places you can order from ranked first. Defaults as for `explore`.\n\nIf you leave out `mode` you get `ride`, unless Tino configured your key otherwise, so send `mode` explicitly. A request with `owner` always uses `ride`. `zoom` and `sort` apply to `explore` and `delivery` only.\n\nA point outside Mongolia returns an empty list.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "description": "`lat,lon` of the point.",
            "schema": {
              "type": "string"
            },
            "example": "47.918,106.917"
          },
          {
            "name": "radius",
            "in": "query",
            "description": "Search radius in meters, at most 100,000. The default depends on `mode` (see above).",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 100000
            }
          },
          {
            "$ref": "#/components/parameters/BusinessTypeFilter"
          },
          {
            "$ref": "#/components/parameters/OwnerParam"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results. The default and the maximum depend on `mode` (see above); larger values are capped.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of results to skip, for paging. Larger values are capped at 1,000.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000,
              "default": 0
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`ride` for pickup and drop-off points (every point, nearest first), `explore` for a discovery map, `delivery` for a delivery map.",
            "schema": {
              "type": "string",
              "enum": [
                "ride",
                "explore",
                "delivery"
              ]
            }
          },
          {
            "name": "zoom",
            "in": "query",
            "description": "`explore` and `delivery` only: the map zoom level, which sets how selective the results are. See [explore](#operation/explore).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 22,
              "default": 14
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "`explore` and `delivery` only: `relevance` (the default) or `distance`.",
            "schema": {
              "type": "string",
              "enum": [
                "relevance",
                "distance"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Places near the point.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/geocode": {
      "get": {
        "tags": [
          "Places"
        ],
        "operationId": "geocode",
        "summary": "Geocode an address",
        "description": "Finds the coordinates of an address or place name in Mongolia. Returns up to 5 matches, best first; an empty list means no match.",
        "parameters": [
          {
            "name": "address",
            "in": "query",
            "required": true,
            "description": "The address or place name.",
            "schema": {
              "type": "string"
            },
            "example": "Сүхбаатарын талбай"
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Matches, best first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/reverse-geo": {
      "get": {
        "tags": [
          "Places"
        ],
        "operationId": "reverseGeocode",
        "summary": "Reverse geocode a point",
        "description": "The place or address at a point: the label for a user's current location, or for a pin dropped on a map. Results are nearest first and carry a driver-ready `driver_label`. When the nearest match is only a broad area, the top result's label names a nearby landmark instead, so that a dropped pin still gives a driver something to navigate by.\n\nEach result carries `nearest_entrance` when an entrance of that building is recorded.\n\nA point outside Mongolia returns an empty list.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "description": "`lat,lon` of the point.",
            "schema": {
              "type": "string"
            },
            "example": "47.9185,106.9170"
          },
          {
            "name": "radius",
            "in": "query",
            "description": "Search radius in meters. Larger values are capped at 300.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 300,
              "default": 100
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results, nearest first. Larger values are capped at 10.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 5
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "How to describe the point. `ride` prefers the building over the shops inside it, which suits pickups. `delivery` ranks businesses first. `explore` describes a point outside Ulaanbaatar by its sum and aimag rather than by a nearby place; these area results have an empty `poi_id`. If you leave it out you get `ride`, unless Tino configured your key otherwise.",
            "schema": {
              "type": "string",
              "enum": [
                "ride",
                "delivery",
                "explore"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Places at the point, nearest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/detail": {
      "get": {
        "tags": [
          "Places"
        ],
        "operationId": "placeDetail",
        "summary": "Place details",
        "description": "Everything about one place, by the `poi_id` another endpoint returned, including photos when available. An id keeps resolving after the place was merged into another record; the response then has `place.redirected_from` set to the id you sent and `place.poi_id` set to the id to keep.",
        "parameters": [
          {
            "name": "poi_id",
            "in": "query",
            "required": true,
            "description": "A `poi_id` returned by another endpoint, exactly as returned.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "The place.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaceResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/explore": {
      "get": {
        "tags": [
          "Explore"
        ],
        "operationId": "explore",
        "summary": "Notable places for a map view",
        "description": "A ranked selection of notable places around a point, for a map view: fewer pins, each worth showing. Address-only points are left out. Residential buildings, parking, ATMs and places of type `other` are left out too, unless you ask for their type with `business_type`. The bar for inclusion rises as the map zooms out:\n\n| `zoom` | Shows |\n|---|---|\n| 12 or less | Only the most significant places, such as landmarks, malls, hospitals and hotels. |\n| 13 to 15 | Notable places with good data. |\n| 16 or more | Most places with any good data. |\n\nNaming a `business_type` lowers the bar, so that places of that type show even when the map is zoomed out. Places that Tino hides from the map are never returned.\n\nResults are ranked by quality and popularity (`sort=relevance`) or nearest first (`sort=distance`). `total` is a lower bound: when it is greater than `offset` plus the number of places returned, there is another page.\n\nA point outside Mongolia returns an empty list.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "description": "`lat,lon` of the map centre.",
            "schema": {
              "type": "string"
            },
            "example": "47.918,106.917"
          },
          {
            "name": "radius",
            "in": "query",
            "description": "Search radius in meters, at most 100,000.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 100000,
              "default": 2000
            }
          },
          {
            "name": "business_type",
            "in": "query",
            "description": "Only these business types, comma-separated (up to 20). Any type you name is shown even if it is normally left out.",
            "style": "form",
            "explode": false,
            "schema": {
              "type": "array",
              "maxItems": 20,
              "items": {
                "$ref": "#/components/schemas/BusinessType"
              }
            }
          },
          {
            "name": "zoom",
            "in": "query",
            "description": "The map zoom level, 1 to 22. Lower zoom levels return only the most significant places.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 22,
              "default": 14
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results. Larger values are capped at 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Number of results to skip, for paging. Larger values are capped at 1,000.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000,
              "default": 0
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "`relevance` (the default) or `distance`, nearest first.",
            "schema": {
              "type": "string",
              "enum": [
                "relevance",
                "distance"
              ],
              "default": "relevance"
            }
          },
          {
            "name": "mode",
            "in": "query",
            "description": "`explore` (the default), or `delivery` to rank places you can order from first. `ride` is ignored here.",
            "schema": {
              "type": "string",
              "enum": [
                "explore",
                "delivery"
              ],
              "default": "explore"
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Places, best first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedPlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/trending": {
      "get": {
        "tags": [
          "Explore"
        ],
        "operationId": "trending",
        "summary": "Trending places",
        "description": "Places worth visiting near a point at this time of day. Tino's curated picks come first (`pinned: true`), then places within 5 km ranked by how well their type suits the time of day, by whether they list opening hours for `day_of_week`, and by how much attention they get in the Tino app. Residential buildings are left out.\n\nA point outside Mongolia returns an empty list.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "description": "`lat,lon` of the user.",
            "schema": {
              "type": "string"
            },
            "example": "47.918,106.917"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results. Larger values are capped at 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "time_context",
            "in": "query",
            "description": "The time of day to suit. Defaults to the current time in Ulaanbaatar: `morning` 06:00–12:00, `afternoon` 12:00–17:00, `evening` 17:00–21:00, `night` otherwise.",
            "schema": {
              "type": "string",
              "enum": [
                "morning",
                "afternoon",
                "evening",
                "night"
              ]
            }
          },
          {
            "name": "day_of_week",
            "in": "query",
            "description": "ISO weekday, 1 (Monday) to 7 (Sunday). Places with opening hours on that day rank higher.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 7
            }
          },
          {
            "$ref": "#/components/parameters/LanguageParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Places, curated picks first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlacesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/entrances": {
      "get": {
        "tags": [
          "Entrances"
        ],
        "operationId": "nearbyEntrances",
        "summary": "Entrances near a point",
        "description": "Building entrances and curated pickup points near a point, nearest first. Among the doors of one building, the most likely way in comes first. Service, emergency and exit doors are left out unless you ask for them with `type`.",
        "parameters": [
          {
            "name": "location",
            "in": "query",
            "required": true,
            "description": "`lat,lon` of the point.",
            "schema": {
              "type": "string"
            },
            "example": "47.918,106.917"
          },
          {
            "name": "radius",
            "in": "query",
            "description": "Search radius in meters. Larger values are capped at 1,000.",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1000,
              "default": 200
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of results. Larger values are capped at 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Only entrances of this type. Naming `service`, `emergency` or `exit` returns those doors, which are otherwise left out.",
            "schema": {
              "type": "string",
              "enum": [
                "main",
                "pickup",
                "yes",
                "staircase",
                "service",
                "emergency",
                "exit"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entrances, nearest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntrancesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/place/v2/entrances/poi": {
      "get": {
        "tags": [
          "Entrances"
        ],
        "operationId": "placeEntrances",
        "summary": "Entrances of a building",
        "description": "Every recorded entrance of one building, the most likely way in first — main entrances, then pickup points, then other doors. Service, emergency and exit doors are left out.",
        "parameters": [
          {
            "name": "poi_id",
            "in": "query",
            "required": true,
            "description": "The building's `poi_id`. Only a UUID id can have entrances; an id in another format gets an empty list.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The building's entrances.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntrancesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/maps/v1/direction": {
      "get": {
        "tags": [
          "Routing"
        ],
        "operationId": "direction",
        "summary": "Directions",
        "description": "A route through two or more points: the geometry as an encoded polyline, distance, duration and, on request, turn-by-turn steps. Driving by default; set `profile` for walking, motorcycle, tricycle or cycling.\n\nA driving route carries `traffic_adjustment` when live probe data covers enough of it. When `traffic_adjustment.mode` is `applied`, `duration` includes the live-traffic correction and `raw_duration` keeps the routing engine's estimate; `traffic_adjustment.segments` colours the route line by observed speed. Other profiles are never traffic-adjusted.",
        "parameters": [
          {
            "name": "coordinates",
            "in": "query",
            "required": true,
            "description": "The points in order: repeat `coordinates` once per point, at least 2. The first is the origin and the last the destination. Format `lon,lat`, or `lat,lon` with `lat_first=true`.\n\nTo say which way the vehicle travels at a point, append `_<bearing>` in compass degrees, 0 to 360: `106.9176,47.9189_90` heads east. At a pickup, this lets the route arrive on the rider's side of the road. A malformed suffix is rejected with 400 and a message that names it.",
            "schema": {
              "type": "array",
              "minItems": 2,
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "106.9176,47.9189",
              "106.9663,47.8430"
            ]
          },
          {
            "name": "lat_first",
            "in": "query",
            "description": "`true` to send `coordinates` as `lat,lon`.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "profile",
            "in": "query",
            "description": "The travel profile. The aliases `0W` (walking), `2W` (motorcycle) and `4W` (driving) are accepted. An unknown profile is rejected with 400.",
            "schema": {
              "type": "string",
              "enum": [
                "driving",
                "walking",
                "motorcycle",
                "tricycle",
                "cycling",
                "0W",
                "2W",
                "4W"
              ],
              "default": "driving"
            }
          },
          {
            "name": "overview",
            "in": "query",
            "description": "How much route geometry to return, for example `full`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "steps",
            "in": "query",
            "description": "`true` to include turn-by-turn steps.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "geometries",
            "in": "query",
            "description": "The geometry encoding, for example `polyline`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "annotations",
            "in": "query",
            "description": "Comma-separated annotations. Needs `overview=full`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "alternatives",
            "in": "query",
            "description": "The number of alternative routes to return.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DirectionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/RouteNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/RouteUnavailable"
          }
        }
      }
    },
    "/maps/v1/directions/multi": {
      "get": {
        "tags": [
          "Routing"
        ],
        "operationId": "directionsMulti",
        "summary": "Walking, two-wheel and four-wheel directions in one call",
        "description": "Routes the same points once per travel mode — `0w` on foot, `2w` by motorcycle, `4w` by car — and returns the routes together, keyed by mode. A mode that fails is listed under `errors` and the others are still returned with 200.\n\nWhen every requested mode fails, the response is `404 ROUTE_NOT_FOUND` if no mode has a route between the points, or `502 UPSTREAM_UNAVAILABLE` if any mode failed because routing was temporarily unavailable. A mode that is not available on the server returns `503 UPSTREAM_UNAVAILABLE`.",
        "parameters": [
          {
            "name": "coordinates",
            "in": "query",
            "required": true,
            "description": "The points in order, as for [directions](#operation/direction), including the optional `_<bearing>` suffix.",
            "schema": {
              "type": "array",
              "minItems": 2,
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "106.9176,47.9189",
              "106.9663,47.8430"
            ]
          },
          {
            "name": "modes",
            "in": "query",
            "description": "Comma-separated modes out of `0w`, `2w` and `4w`. Defaults to all three. An unknown mode is rejected with 400.",
            "schema": {
              "type": "string"
            },
            "example": "0w,4w"
          },
          {
            "name": "lat_first",
            "in": "query",
            "description": "`true` to send `coordinates` as `lat,lon`.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "overview",
            "in": "query",
            "description": "How much route geometry to return, for example `full`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "steps",
            "in": "query",
            "description": "`true` to include turn-by-turn steps.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "geometries",
            "in": "query",
            "description": "The geometry encoding, for example `polyline`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "annotations",
            "in": "query",
            "description": "Comma-separated annotations. Needs `overview=full`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "At least one mode has a route.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MultiDirectionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/RouteNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/RouteUnavailable"
          },
          "503": {
            "description": "A requested mode is not available on the server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "uuid": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
                  "status": {
                    "code": "UPSTREAM_UNAVAILABLE",
                    "message": "Route profile is not configured for mode '2w'"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/maps/v1/matrix": {
      "get": {
        "tags": [
          "Routing"
        ],
        "operationId": "matrix",
        "summary": "Travel-time and distance matrix",
        "description": "Travel time and distance for every origin–destination pair, for example to rank nearby drivers by arrival time. Up to 30 origins and 30 destinations. **Durations are in nanoseconds**; divide by 1,000,000,000 for seconds.",
        "parameters": [
          {
            "name": "origin",
            "in": "query",
            "required": true,
            "description": "Origins as `lat,lon` pairs separated by `|`, at most 30.",
            "schema": {
              "type": "string"
            },
            "example": "47.9189,106.9176|47.9101,106.8830"
          },
          {
            "name": "destination",
            "in": "query",
            "required": true,
            "description": "Destinations as `lat,lon` pairs separated by `|`, at most 30.",
            "schema": {
              "type": "string"
            },
            "example": "47.8430,106.9663"
          },
          {
            "name": "mode",
            "in": "query",
            "description": "The travel mode.",
            "schema": {
              "type": "string",
              "enum": [
                "ModeDriving",
                "ModeWalking",
                "ModeBicycling",
                "ModeTransit"
              ],
              "default": "ModeDriving"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The matrix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatrixResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/RouteNotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/RouteUnavailable"
          }
        }
      }
    },
    "/maps/v1/traffic/cells": {
      "get": {
        "tags": [
          "Traffic"
        ],
        "operationId": "trafficCells",
        "summary": "Live traffic for a viewport",
        "description": "Live congestion in a map viewport, built from the GPS of Tino drivers over the last `window_seconds` (about 20 minutes), as a GeoJSON FeatureCollection that you can hand straight to a map's GeoJSON source. Each feature is a short line along one direction of travel through a cell of about 100 m, with at most two directions per cell.\n\n**No feature means no data — never free-flowing traffic.** A road appears only where enough different drivers passed recently, so draw every other road as unknown, not as clear. `cells_with_data` counts the cells in your viewport that have data, before the `classes` filter: with `classes=jam,slow`, no features and `cells_with_data` above 0 means \"nothing congested here\", while `cells_with_data` 0 means \"no coverage here\".\n\nTo protect drivers' privacy, a direction is published only when at least `min_drivers` different drivers contributed, `speed_kmh` is rounded down to 5 km/h, the driver count is a range, and `heading` is rounded.\n\nA `bbox` wider than 1 degree on either axis is narrowed about its centre and flagged `bbox_clamped`, not rejected. At most 5,000 features are returned, those nearest the centre, and `truncated` is set when the cap applied. The layer is rebuilt about every 30 seconds and responses carry `Cache-Control: private, max-age=15`, so polling faster than every 15 seconds gains nothing. `generated_at` can move backwards between polls; do not discard a response because of it.",
        "parameters": [
          {
            "name": "bbox",
            "in": "query",
            "required": true,
            "description": "The viewport as `minLon,minLat,maxLon,maxLat`. A malformed or inverted box is rejected with 400.",
            "schema": {
              "type": "string"
            },
            "example": "106.85,47.88,106.98,47.95"
          },
          {
            "name": "classes",
            "in": "query",
            "description": "Comma-separated classes to return, out of `jam`, `slow` and `free`. Defaults to all three. An unknown class is rejected with 400.",
            "schema": {
              "type": "string"
            },
            "example": "jam,slow"
          }
        ],
        "responses": {
          "200": {
            "description": "Traffic features in the viewport.",
            "headers": {
              "Cache-Control": {
                "description": "`private, max-age=15`",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrafficCellsResponse"
                },
                "example": {
                  "uuid": "5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b",
                  "status": {
                    "code": "SUCCESS",
                    "message": "Request successful"
                  },
                  "data": {
                    "generated_at": "2026-10-14T08:15:30Z",
                    "window_seconds": 1200,
                    "cell_size_projected_m": 150,
                    "cell_size_ground_m": 100.5,
                    "min_drivers": 3,
                    "feature_count": 1,
                    "cells_with_data": 14,
                    "truncated": false,
                    "bbox_clamped": false,
                    "bbox": [
                      106.85,
                      47.88,
                      106.98,
                      47.95
                    ],
                    "geojson": {
                      "type": "FeatureCollection",
                      "features": [
                        {
                          "type": "Feature",
                          "geometry": {
                            "type": "LineString",
                            "coordinates": [
                              [
                                106.91702,
                                47.91843
                              ],
                              [
                                106.91851,
                                47.91851
                              ]
                            ]
                          },
                          "properties": {
                            "class": "jam",
                            "speed_kmh": 5,
                            "drivers": "3-4",
                            "heading": 90
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/TrafficUnavailable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Recommended. Send `X-API-Key: <your key>`."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Accepted. Send `Authorization: Bearer <your key>`."
      },
      "ApiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "api_key",
        "description": "**Deprecated.** `?api_key=<your key>` still works for existing integrations, but a key in a URL leaks into logs, proxies and browser history. Use a header: this method will be removed."
      }
    },
    "parameters": {
      "LanguageParam": {
        "name": "language",
        "in": "query",
        "description": "Response language for names, addresses and `rider_label`. Any other value means `mn`.",
        "schema": {
          "type": "string",
          "enum": [
            "mn",
            "en",
            "ru",
            "zh",
            "ko",
            "ja"
          ],
          "default": "mn"
        }
      },
      "BusinessTypeFilter": {
        "name": "business_type",
        "in": "query",
        "description": "Only places of these business types, comma-separated (up to 20), for example `restaurant,cafe`.",
        "style": "form",
        "explode": false,
        "schema": {
          "type": "array",
          "maxItems": 20,
          "items": {
            "$ref": "#/components/schemas/BusinessType"
          }
        }
      },
      "OwnerParam": {
        "name": "owner",
        "in": "query",
        "description": "Only the custom places of one account, by its account id. See [Custom places](#section/Custom-places). A value that is not a UUID is rejected with 400.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "A parameter is missing, malformed or out of range.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missing": {
                "summary": "Missing or malformed parameter (INVALID_REQUEST)",
                "value": {
                  "uuid": "0b9a8c7d-6e5f-4a3b-8c1d-0e9f8a7b6c5d",
                  "status": {
                    "code": "INVALID_REQUEST",
                    "message": "Parameter 'location' is required (format: latitude,longitude)"
                  }
                }
              },
              "tooLong": {
                "summary": "Parameter out of range (VALIDATION_ERROR)",
                "value": {
                  "uuid": "1c0b9a8d-7e6f-4b5c-9d2e-1f0a9b8c7d6e",
                  "status": {
                    "code": "VALIDATION_ERROR",
                    "message": "Parameter 'keywords' is too long"
                  }
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "No API key, or a key that does not exist.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missing": {
                "summary": "No key sent (UNAUTHORIZED)",
                "value": {
                  "uuid": "2d1c0b9a-8f7e-4c6d-8e3f-2a1b0c9d8e7f",
                  "status": {
                    "code": "UNAUTHORIZED",
                    "message": "API key is required. Provide it via the X-API-Key header (preferred) or Authorization: Bearer <key>. The api_key query parameter still works but is deprecated — it can leak the key into request logs."
                  }
                }
              },
              "unknown": {
                "summary": "Unknown key (INVALID_API_KEY)",
                "value": {
                  "uuid": "3e2d1c0b-9a8f-4d7e-9f4a-3b2c1d0e9f8a",
                  "status": {
                    "code": "INVALID_API_KEY",
                    "message": "Invalid API key."
                  }
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key is known but may not be used for this request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "revoked": {
                "summary": "Revoked key (INVALID_API_KEY)",
                "value": {
                  "uuid": "4f3e2d1c-0b9a-4e8f-8a5b-4c3d2e1f0a9b",
                  "status": {
                    "code": "INVALID_API_KEY",
                    "message": "API key has been deactivated."
                  }
                }
              },
              "expired": {
                "summary": "Expired key (EXPIRED_API_KEY)",
                "value": {
                  "uuid": "5a4f3e2d-1c0b-4f9a-9b6c-5d4e3f2a1b0c",
                  "status": {
                    "code": "EXPIRED_API_KEY",
                    "message": "API key has expired."
                  }
                }
              },
              "accountDisabled": {
                "summary": "Account disabled (ACCOUNT_DISABLED)",
                "value": {
                  "uuid": "6b5a4f3e-2d1c-4a0b-8c7d-6e5f4a3b2c1d",
                  "status": {
                    "code": "ACCOUNT_DISABLED",
                    "message": "The account that owns this API key has been disabled."
                  }
                }
              },
              "mapApplicationKey": {
                "summary": "Map Application key used outside the map SDK (MAP_APPLICATION_KEY_SCOPE)",
                "value": {
                  "uuid": "7c6b5a4f-3e2d-4b1c-9d8e-7f6a5b4c3d2e",
                  "status": {
                    "code": "MAP_APPLICATION_KEY_SCOPE",
                    "message": "Map Application keys only work with the Tino Maps SDK (/maps/sdk/v1). Use a server key for data APIs."
                  }
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "The place does not exist.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "uuid": "8d7c6b5a-4f3e-4c2d-8e9f-8a7b6c5d4e3f",
              "status": {
                "code": "NOT_FOUND",
                "message": "Place not found"
              }
            }
          }
        }
      },
      "RouteNotFound": {
        "description": "There is no route between the coordinates.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "uuid": "9e8d7c6b-5a4f-4d3e-9f0a-9b8c7d6e5f4a",
              "status": {
                "code": "ROUTE_NOT_FOUND",
                "message": "No route available for these coordinates"
              }
            }
          }
        }
      },
      "RouteUnavailable": {
        "description": "Routing is temporarily unavailable. Retry with backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "uuid": "af9e8d7c-6b5a-4e4f-8a1b-0c9d8e7f6a5b",
              "status": {
                "code": "UPSTREAM_UNAVAILABLE",
                "message": "Route service is temporarily unavailable"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "A limit was exceeded. Wait for `Retry-After` seconds before retrying.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait. `1` for the per-second limit; the seconds until the quota resets for the daily limit.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitErrorResponse"
            },
            "examples": {
              "perSecond": {
                "summary": "Per-second limit (RATE_LIMIT_EXCEEDED)",
                "value": {
                  "uuid": "b0af9e8d-7c6b-4f5a-9b2c-1d0e9f8a7b6c",
                  "status": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Rate limit exceeded. Please wait before making more requests."
                  },
                  "data": {
                    "retry_after": 1
                  }
                }
              },
              "daily": {
                "summary": "Daily quota used up (DAILY_LIMIT_EXCEEDED)",
                "value": {
                  "uuid": "c1b0af9e-8d7c-4a6b-8c3d-2e1f0a9b8c7d",
                  "status": {
                    "code": "DAILY_LIMIT_EXCEEDED",
                    "message": "Daily request quota exceeded for this account. All of the account's API keys share one daily quota."
                  },
                  "data": {
                    "retry_after": 28800
                  }
                }
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Something failed on our side. Retry with backoff.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "uuid": "d2c1b0af-9e8d-4b7c-9d4e-3f2a1b0c9d8e",
              "status": {
                "code": "INTERNAL_ERROR",
                "message": "Failed to search places"
              }
            }
          }
        }
      },
      "TrafficUnavailable": {
        "description": "The traffic layer is switched off or not yet available.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "uuid": "e3d2c1b0-af9e-4c8d-8e5f-4a3b2c1d0e9f",
              "status": {
                "code": "INTERNAL_ERROR",
                "message": "Traffic layer is temporarily unavailable"
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Status": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "`SUCCESS`, or an error code. See [Responses and errors](#section/Responses-and-errors).",
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "A human-readable message. It may change; do not parse it.",
            "example": "Request successful"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          }
        }
      },
      "RateLimitErrorResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "data": {
            "type": "object",
            "properties": {
              "retry_after": {
                "type": "integer",
                "description": "Seconds to wait, the same as the `Retry-After` header."
              }
            }
          }
        }
      },
      "BusinessType": {
        "type": "string",
        "description": "A place category. See the list under [Places](#tag/Places).",
        "enum": [
          "restaurant",
          "cafe",
          "bar",
          "supermarket",
          "shopping",
          "pharmacy",
          "hospital",
          "school",
          "bank",
          "atm",
          "gas_station",
          "hotel",
          "government",
          "entertainment",
          "culture",
          "religion",
          "park",
          "sports",
          "gym",
          "beauty",
          "automotive",
          "services",
          "tourism",
          "transport",
          "parking",
          "parcel_locker",
          "building",
          "residential",
          "other"
        ]
      },
      "Location": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number",
            "format": "double",
            "example": 47.9187
          },
          "longitude": {
            "type": "number",
            "format": "double",
            "example": 106.9172
          }
        }
      },
      "AdministrativeArea": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Usually `city`, `district` or `khoroo`; other types may appear.",
            "example": "district"
          },
          "name": {
            "type": "string",
            "example": "Сүхбаатар"
          }
        }
      },
      "Place": {
        "type": "object",
        "description": "A place — a business, landmark, building or address point. Optional fields are left out when empty.",
        "required": [
          "poi_id",
          "name",
          "location",
          "country",
          "country_code",
          "formatted_address",
          "business_type"
        ],
        "properties": {
          "poi_id": {
            "type": "string",
            "description": "Opaque id, usually a UUID. Store it as returned and pass it to [detail](#operation/placeDetail). It is empty for a point that is not a stored place, such as an address point in `nearby` results or an area result of `reverse-geo` with `mode=explore`; such a result has no detail and must not be used as a key.",
            "example": "7d2c4c3e-2a0b-4c56-9f0e-3b9f6a1d2e45"
          },
          "name": {
            "type": "string",
            "description": "Display name, in the requested language where a translation exists. Never empty.",
            "example": "Улсын их дэлгүүр"
          },
          "name_en": {
            "type": "string",
            "nullable": true
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "country": {
            "type": "string",
            "example": "Mongolia"
          },
          "country_code": {
            "type": "string",
            "example": "MN"
          },
          "formatted_address": {
            "type": "string",
            "description": "The full address, in the requested language where a translation exists."
          },
          "street": {
            "type": "string",
            "nullable": true
          },
          "postal_code": {
            "type": "string",
            "nullable": true
          },
          "plus_code": {
            "type": "string",
            "nullable": true
          },
          "business_type": {
            "type": "string",
            "description": "The category — normally one of the [business types](#tag/Places). Treat a value you do not recognise as `other`; new values may be added.",
            "example": "shopping"
          },
          "business_type_label": {
            "type": "string",
            "description": "`business_type` as a display label in the requested language.",
            "example": "Shopping"
          },
          "business_subtype": {
            "type": "string",
            "nullable": true,
            "description": "A finer category, when known."
          },
          "phone": {
            "type": "string",
            "nullable": true
          },
          "website": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true,
            "description": "A free-text description, when set."
          },
          "opening_hours": {
            "$ref": "#/components/schemas/OpeningHours"
          },
          "open_now": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the place is open at the time of the request, Ulaanbaatar time. Absent when the hours are unknown."
          },
          "administrative_areas": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AdministrativeArea"
            }
          },
          "distance_m": {
            "type": "number",
            "nullable": true,
            "description": "Distance in meters from the point in the request. Present only when the request had one."
          },
          "distance_text": {
            "type": "string",
            "description": "The distance for display.",
            "example": "1.2km"
          },
          "driver_label": {
            "type": "string",
            "description": "The dispatch label for drivers, always in Mongolian and building-first: landmark or area, microdistrict, building, entrance. The district (дүүрэг) is included only when nothing else identifies the point."
          },
          "rider_label": {
            "type": "string",
            "description": "The same content as `driver_label`, in the requested language. Set whenever `driver_label` is; when both are absent, show `name`."
          },
          "area_name": {
            "type": "string",
            "nullable": true,
            "description": "The area, such as a хороолол or хотхон."
          },
          "landmark": {
            "type": "string",
            "nullable": true,
            "description": "A nearby landmark that identifies the place."
          },
          "building": {
            "type": "string",
            "nullable": true
          },
          "apartment": {
            "type": "string",
            "nullable": true,
            "description": "Apartment number (тоот)."
          },
          "floor": {
            "type": "string",
            "nullable": true,
            "description": "Floor (давхар)."
          },
          "entrance": {
            "type": "string",
            "nullable": true,
            "description": "The place's own entrance (орц). For where to walk in from a pin, see `nearest_entrance`."
          },
          "nearest_entrance": {
            "description": "On reverse-geo results only, when one is recorded — the entrance of this building nearest the requested point, with its floor and entrance number. Absent means that no entrance is recorded, not that the front door is the way in.",
            "allOf": [
              {
                "$ref": "#/components/schemas/EntrancePlace"
              }
            ]
          },
          "pickup_locations": {
            "type": "array",
            "nullable": true,
            "description": "Curated pickup points around the building, when known.",
            "items": {
              "$ref": "#/components/schemas/PickupLocation"
            }
          },
          "name_translations": {
            "type": "object",
            "nullable": true,
            "description": "The name by language code (`en`, `ru`, `zh`, `ko`, `ja`), where translated. Only when `language` is `mn` (the default). For any other language the translated value is already in `name` and this field is left out; request `language=mn` to get the original and the translations.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "address_translations": {
            "type": "object",
            "nullable": true,
            "description": "The address by language code, where translated. Only when `language` is `mn` (the default). For any other language the translated value is already in `formatted_address` and this field is left out; request `language=mn` to get the original and the translations.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "photo_urls": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "images": {
            "type": "array",
            "nullable": true,
            "description": "Place photos, usually on [detail](#operation/placeDetail). `description` can carry pickup guidance, and `category_key` says what a photo shows, such as `outside`, `inside` or `menu`.",
            "items": {
              "$ref": "#/components/schemas/PlaceImage"
            }
          },
          "icon_url": {
            "type": "string",
            "nullable": true,
            "description": "The image to draw for the place on a map — its own icon or, for a place that belongs to a business, that business's logo (`brand.logo_url`). Absent when it has neither."
          },
          "brand": {
            "description": "The business the place belongs to, such as the bank that its branches and ATMs belong to. Absent for other places.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PlaceBrand"
              }
            ]
          },
          "short_name": {
            "type": "string",
            "nullable": true,
            "description": "A short display name, when available (usually on search and autocomplete results)."
          },
          "score": {
            "type": "number",
            "nullable": true,
            "description": "A raw relevance value, when available (usually on search and autocomplete results). It is unbounded — compare it only within one response, never against a fixed threshold."
          },
          "pinned": {
            "type": "boolean",
            "description": "On `trending` results, one of Tino's curated picks."
          },
          "redirected_from": {
            "type": "string",
            "description": "On `detail` only, when the id you sent belongs to a record that was merged into this one."
          }
        }
      },
      "OpeningHours": {
        "type": "object",
        "nullable": true,
        "description": "Opening hours by day, when known, usually in one of two shapes. Days without known hours are left out.\n\n- **Structured:** the keys are days (`mon` … `sun`) and each value is an object with `open` and `close` as 24-hour `HH:MM` strings, `closed: true` for a closed day, and `shifts`, a list of `open`/`close` pairs for the extra periods of a split day.\n- **Text:** the keys are day names (such as `monday`) and each value is the day's hours as text. For a `language` other than `mn`, \"closed\" and \"open 24 hours\" are translated.\n\nOther shapes may appear, because a business can publish its own hours: skip a day you cannot read instead of rejecting the place.",
        "additionalProperties": {},
        "example": {
          "mon": {
            "open": "09:00",
            "close": "18:00"
          },
          "sat": {
            "open": "10:00",
            "close": "14:00",
            "shifts": [
              {
                "open": "15:00",
                "close": "19:00"
              }
            ]
          },
          "sun": {
            "closed": true
          }
        }
      },
      "PlaceImage": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "category_key": {
            "type": "string"
          }
        }
      },
      "PlaceBrand": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "name_en": {
            "type": "string",
            "nullable": true
          },
          "logo_url": {
            "type": "string",
            "description": "Absolute https URL of the business's logo."
          }
        }
      },
      "PickupLocation": {
        "type": "object",
        "properties": {
          "lat": {
            "type": "number",
            "format": "double"
          },
          "lon": {
            "type": "number",
            "format": "double"
          },
          "order": {
            "type": "integer",
            "description": "Preference order, 1 first."
          },
          "description_mn": {
            "type": "string",
            "description": "Where to wait, in Mongolian.",
            "example": "1-р орц"
          },
          "description_en": {
            "type": "string",
            "nullable": true,
            "description": "Where to wait, in English, when available."
          }
        }
      },
      "PlacesResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "places": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Place"
            }
          }
        }
      },
      "PaginatedPlacesResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "places": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Place"
            }
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "The number of matches. It can be an estimate; when it is greater than `offset` plus the number of places returned, there is another page."
          },
          "limit": {
            "type": "integer",
            "description": "The limit applied, after capping."
          },
          "offset": {
            "type": "integer",
            "description": "The offset applied, after capping."
          }
        }
      },
      "PlaceResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "place": {
            "$ref": "#/components/schemas/Place"
          }
        }
      },
      "EntrancePlace": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "location": {
            "$ref": "#/components/schemas/Location"
          },
          "entrance_type": {
            "type": "string",
            "description": "`main`, `pickup` (a curated kerbside pickup point), `yes` (a door with no further detail), `staircase`, `service`, `emergency` or `exit`.",
            "example": "main"
          },
          "building_poi_id": {
            "type": "string",
            "nullable": true,
            "description": "The building's `poi_id`, when linked."
          },
          "distance_m": {
            "type": "number",
            "nullable": true
          },
          "distance_text": {
            "type": "string"
          },
          "address": {
            "type": "string",
            "nullable": true,
            "description": "Street and house number, when known."
          },
          "level": {
            "type": "string",
            "nullable": true,
            "description": "The floor the entrance serves, as free text.",
            "example": "1-р давхар"
          },
          "unit": {
            "type": "string",
            "nullable": true,
            "description": "The entrance or staircase identifier.",
            "example": "2 орц"
          }
        }
      },
      "EntrancesResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "entrances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntrancePlace"
            }
          }
        }
      },
      "DirectionResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "data": {
            "$ref": "#/components/schemas/DirectionData"
          }
        }
      },
      "DirectionData": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "Ok"
          },
          "waypoints": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Waypoint"
            }
          },
          "routes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Route"
            }
          }
        }
      },
      "Waypoint": {
        "type": "object",
        "properties": {
          "hint": {
            "type": "string"
          },
          "distance": {
            "type": "number",
            "description": "Meters from the requested point to where it was placed on the road network."
          },
          "name": {
            "type": "string"
          },
          "location": {
            "type": "array",
            "description": "[lon, lat]",
            "items": {
              "type": "number"
            }
          }
        }
      },
      "Route": {
        "type": "object",
        "properties": {
          "distance": {
            "type": "number",
            "description": "Meters."
          },
          "duration": {
            "type": "number",
            "description": "Seconds. Includes the live-traffic correction when `traffic_adjustment.mode` is `applied`."
          },
          "raw_duration": {
            "type": "number",
            "description": "Seconds, as estimated by the routing engine before any live-traffic correction. Present when an adjustment was applied."
          },
          "traffic_adjustment": {
            "$ref": "#/components/schemas/TrafficAdjustment"
          },
          "geometry": {
            "type": "string",
            "description": "Encoded polyline."
          },
          "traffic_light": {
            "type": "integer",
            "description": "Number of traffic lights on the route, when known."
          },
          "legs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Leg"
            }
          }
        }
      },
      "TrafficAdjustment": {
        "type": "object",
        "description": "How live probe traffic affected a driving route. Aggregate data only; never identifies a vehicle.",
        "properties": {
          "source": {
            "type": "string",
            "example": "tino_probe"
          },
          "mode": {
            "type": "string",
            "enum": [
              "applied",
              "shadow",
              "insufficient",
              "no_change"
            ],
            "description": "`applied`: `duration` includes the correction. `shadow`: the correction was computed but not applied. `insufficient`: not enough live data on the route. `no_change`: live data agrees with the estimate."
          },
          "base_duration": {
            "type": "number",
            "description": "The routing engine's duration, in seconds."
          },
          "estimated_duration": {
            "type": "number",
            "description": "The live-traffic estimate, in seconds."
          },
          "multiplier": {
            "type": "number"
          },
          "coverage": {
            "type": "number",
            "description": "The share of the route's distance covered by live data, 0 to 1."
          },
          "confidence": {
            "type": "number"
          },
          "covered_cells": {
            "type": "integer"
          },
          "distinct_drivers": {
            "type": "integer"
          },
          "segments": {
            "type": "array",
            "description": "Optional live-traffic colouring of the route line. Contiguous ranges over the points of the decoded `geometry`, each starting at the previous one's `end_index`, the first at point 0 and the last ending at the final point. Left out when no stretch has enough live data.",
            "items": {
              "$ref": "#/components/schemas/TrafficSegment"
            }
          }
        }
      },
      "TrafficSegment": {
        "type": "object",
        "description": "One coloured stretch of the route line, classed by observed speed.",
        "properties": {
          "start_index": {
            "type": "integer",
            "description": "Index of the stretch's first point in the decoded `geometry`."
          },
          "end_index": {
            "type": "integer",
            "description": "Index of the stretch's last point in the decoded `geometry`."
          },
          "class": {
            "type": "string",
            "enum": [
              "jam",
              "slow",
              "free",
              "unknown"
            ]
          },
          "speed_kmh": {
            "type": "number",
            "description": "Observed speed in km/h, rounded down to 5. Left out for `unknown`."
          }
        }
      },
      "Leg": {
        "type": "object",
        "properties": {
          "distance": {
            "type": "number"
          },
          "duration": {
            "type": "number"
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Step"
            }
          }
        }
      },
      "Step": {
        "type": "object",
        "properties": {
          "distance": {
            "type": "number"
          },
          "duration": {
            "type": "number"
          },
          "name": {
            "type": "string"
          },
          "maneuver": {
            "$ref": "#/components/schemas/Maneuver"
          }
        }
      },
      "Maneuver": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "modifier": {
            "type": "string"
          },
          "location": {
            "type": "array",
            "description": "[lon, lat]",
            "items": {
              "type": "number"
            }
          }
        }
      },
      "MultiDirectionResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "routes": {
            "type": "object",
            "description": "Routes by mode (`0w`, `2w`, `4w`), for the modes that succeeded.",
            "additionalProperties": {
              "$ref": "#/components/schemas/DirectionData"
            }
          },
          "errors": {
            "type": "object",
            "description": "Why each failed mode failed. Present only when a mode failed.",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "MatrixResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "data": {
            "$ref": "#/components/schemas/MatrixData"
          }
        }
      },
      "MatrixData": {
        "type": "object",
        "properties": {
          "origin_addresses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "destination_addresses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "type": "array",
            "description": "One row per origin, in request order.",
            "items": {
              "$ref": "#/components/schemas/MatrixRow"
            }
          }
        }
      },
      "MatrixRow": {
        "type": "object",
        "properties": {
          "elements": {
            "type": "array",
            "description": "One element per destination, in request order.",
            "items": {
              "$ref": "#/components/schemas/MatrixElement"
            }
          }
        }
      },
      "MatrixElement": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "OK"
          },
          "duration": {
            "type": "integer",
            "format": "int64",
            "description": "Nanoseconds."
          },
          "duration_in_traffic": {
            "type": "integer",
            "format": "int64",
            "description": "Nanoseconds."
          },
          "distance": {
            "$ref": "#/components/schemas/DistanceValue"
          }
        }
      },
      "DistanceValue": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string",
            "example": "12.4km"
          },
          "value": {
            "type": "number",
            "description": "Meters."
          }
        }
      },
      "TrafficCellsResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "data": {
            "$ref": "#/components/schemas/TrafficCellsData"
          }
        }
      },
      "TrafficCellsData": {
        "type": "object",
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the layer behind this response was built. It can move backwards between polls."
          },
          "window_seconds": {
            "type": "integer",
            "description": "How far back the observations reach."
          },
          "cell_size_projected_m": {
            "type": "number",
            "description": "Cell size on the Web Mercator grid."
          },
          "cell_size_ground_m": {
            "type": "number",
            "description": "Cell size on the ground at this viewport's latitude. Use this one for real distances."
          },
          "min_drivers": {
            "type": "integer",
            "description": "The fewest different drivers behind any published direction."
          },
          "feature_count": {
            "type": "integer",
            "description": "Features in this response, after the `classes` filter."
          },
          "cells_with_data": {
            "type": "integer",
            "description": "Cells in this viewport with any published direction, before the `classes` filter."
          },
          "truncated": {
            "type": "boolean",
            "description": "`true` when the feature cap cut the response."
          },
          "bbox_clamped": {
            "type": "boolean",
            "description": "`true` when the requested `bbox` was narrowed."
          },
          "bbox": {
            "type": "array",
            "description": "The viewport actually served, `[minLon, minLat, maxLon, maxLat]`.",
            "minItems": 4,
            "maxItems": 4,
            "items": {
              "type": "number"
            }
          },
          "geojson": {
            "$ref": "#/components/schemas/TrafficFeatureCollection"
          }
        }
      },
      "TrafficFeatureCollection": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "FeatureCollection"
            ]
          },
          "features": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TrafficFeature"
            }
          }
        }
      },
      "TrafficFeature": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "Feature"
            ]
          },
          "geometry": {
            "$ref": "#/components/schemas/TrafficLineString"
          },
          "properties": {
            "$ref": "#/components/schemas/TrafficCellProperties"
          }
        }
      },
      "TrafficLineString": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "LineString"
            ]
          },
          "coordinates": {
            "type": "array",
            "description": "`[lon, lat]` pairs in the direction of travel, so that an arrow can be drawn without recomputing the bearing.",
            "items": {
              "type": "array",
              "minItems": 2,
              "maxItems": 2,
              "items": {
                "type": "number"
              }
            }
          }
        }
      },
      "TrafficCellProperties": {
        "type": "object",
        "properties": {
          "class": {
            "type": "string",
            "enum": [
              "jam",
              "slow",
              "free"
            ]
          },
          "speed_kmh": {
            "type": "number",
            "description": "Observed speed in km/h, rounded down to 5."
          },
          "drivers": {
            "type": "string",
            "description": "How many different drivers contributed, as a range such as `3-4`, `5-9` or `10+`.",
            "example": "5-9"
          },
          "heading": {
            "type": "integer",
            "description": "The direction of travel in degrees, 0 = north, rounded.",
            "example": 90
          }
        }
      }
    }
  }
}
