{"components":{"schemas":{"Address":{"description":"Every field is optional because every field is missing somewhere: much of the world has no postcodes and a great deal of it has no house numbers.","properties":{"city":{"type":"string"},"country":{"type":"string"},"country_code":{"description":"ISO 3166-1 alpha-2, upper case","type":"string"},"county":{"type":"string"},"house_number":{"type":"string"},"neighbourhood":{"type":"string"},"postcode":{"type":"string"},"road":{"type":"string"},"state":{"type":"string"},"suburb":{"type":"string"}},"type":"object"},"AutocompleteRequest":{"properties":{"bias":{"$ref":"#/components/schemas/Point"},"countries":{"items":{"type":"string"},"type":"array"},"input":{"type":"string"},"language":{"type":"string"},"limit":{"type":"integer"},"restrict_to":{"$ref":"#/components/schemas/BoundingBox"}},"required":["input"],"type":"object"},"BoundingBox":{"properties":{"max_latitude":{"type":"number"},"max_longitude":{"type":"number"},"min_latitude":{"type":"number"},"min_longitude":{"type":"number"}},"type":"object"},"Costing":{"description":"Travel mode. Defaults to this deployment's configured default (motor_scooter).","enum":["auto","motor_scooter","motorcycle","bicycle","pedestrian","truck","bus","taxi"],"type":"string"},"DescribeLocationRequest":{"properties":{"language":{"type":"string"},"limit":{"default":3,"type":"integer"},"point":{"$ref":"#/components/schemas/Point"},"radius_km":{"default":0.6,"description":"How far to look for landmarks. A landmark three kilometres away is a different neighbourhood","maximum":3,"type":"number"}},"required":["point"],"type":"object"},"ElevationRequest":{"description":"One of `points` or `polyline` is required.","properties":{"points":{"items":{"$ref":"#/components/schemas/Point"},"type":"array"},"polyline":{"type":"string"},"polyline_precision":{"default":6,"enum":[5,6],"type":"integer"},"samples":{"minimum":2,"type":"integer"}},"type":"object"},"Error":{"description":"Branch on `error`, not on the status code: `unroutable` and `invalid_coordinate` are both 422 and need opposite handling.","properties":{"docs":{"type":"string"},"error":{"example":"unroutable","type":"string"},"message":{"type":"string"}},"type":"object"},"FareOptions":{"properties":{"country":{"description":"ISO 3166-1. Omitted, it is derived from the pickup, then from the deployment's own setting","type":"string"},"passengers":{"type":"integer"},"rain":{"description":"Supplied by you. Rain is the largest single swing in a motorcycle fare on this continent, and this service has no weather feed","type":"boolean"},"return_trip":{"description":"Price the empty leg back as well","type":"boolean"},"stops":{"description":"Intermediate drops. Derived from the itinerary on /v1/route","type":"integer"},"surge":{"description":"Capped by the tariff's max_surge","type":"number"},"surge_reason":{"description":"Echoed into the breakdown so an app can explain it","type":"string"},"tariff":{"$ref":"#/components/schemas/Tariff"},"vehicle":{"description":"The generic term. The LOCAL word -- okada, moto, zémidjan -- comes back on the answer","enum":["motorcycle","three_wheeler","car","van","truck","bicycle"],"type":"string"},"waiting_minutes":{"type":"number"}},"type":"object"},"FareRequest":{"description":"Send origin and destination to have the trip routed, or distance_km and seconds to price one you measured yourself. Sending both is refused: the fare would rest on one of two different distances and the response could not say which.","properties":{"at":{"description":"The pickup instant. Decides which time-of-day window applies, in the LOCAL zone of the pickup","format":"date-time","type":"string"},"costing":{"description":"Travel mode. Defaults to this deployment's configured default (motor_scooter).","enum":["auto","motor_scooter","motorcycle","bicycle","pedestrian","truck","bus","taxi"],"type":"string"},"country":{"description":"ISO 3166-1. Omitted, it is derived from the pickup, then from the deployment's own setting","type":"string"},"destination":{"$ref":"#/components/schemas/Point"},"distance_km":{"description":"A distance you measured yourself","type":"number"},"origin":{"$ref":"#/components/schemas/Point"},"passengers":{"type":"integer"},"rain":{"description":"Supplied by you. Rain is the largest single swing in a motorcycle fare on this continent, and this service has no weather feed","type":"boolean"},"return_trip":{"description":"Price the empty leg back as well","type":"boolean"},"seconds":{"type":"number"},"stops":{"description":"Intermediate drops. Derived from the itinerary on /v1/route","type":"integer"},"surge":{"description":"Capped by the tariff's max_surge","type":"number"},"surge_reason":{"description":"Echoed into the breakdown so an app can explain it","type":"string"},"tariff":{"$ref":"#/components/schemas/Tariff"},"vehicle":{"description":"The generic term. The LOCAL word -- okada, moto, zémidjan -- comes back on the answer","enum":["motorcycle","three_wheeler","car","van","truck","bicycle"],"type":"string"},"waiting_minutes":{"type":"number"}},"type":"object"},"GeocodeRequest":{"description":"One of `query` or `address` is required.","properties":{"address":{"$ref":"#/components/schemas/Address"},"bias":{"$ref":"#/components/schemas/Point"},"countries":{"items":{"type":"string"},"type":"array"},"language":{"type":"string"},"limit":{"type":"integer"},"query":{"type":"string"},"restrict_to":{"$ref":"#/components/schemas/BoundingBox"}},"type":"object"},"GeofenceCheckRequest":{"properties":{"at":{"format":"date-time","type":"string"},"kind":{"enum":["delivery","no_go","surcharge","hub","alert"],"type":"string"},"point":{"$ref":"#/components/schemas/Point"},"points":{"items":{"$ref":"#/components/schemas/Point"},"type":"array"}},"type":"object"},"GeofencePathRequest":{"properties":{"at":{"format":"date-time","type":"string"},"points":{"items":{"$ref":"#/components/schemas/Point"},"type":"array"},"polyline":{"description":"A route's own shape","type":"string"},"polyline_precision":{"default":6,"enum":[5,6],"type":"integer"}},"type":"object"},"GeofenceTrackRequest":{"properties":{"pings":{"items":{"properties":{"at":{"description":"Required. A track without times has no dwell and no entry moment, which is the whole point of this endpoint","format":"date-time","type":"string"},"point":{"$ref":"#/components/schemas/Point"}},"required":["point","at"],"type":"object"},"type":"array"}},"required":["pings"],"type":"object"},"GeolocateRequest":{"description":"Accepted and always refused. See the endpoint description.","properties":{"cell_towers":{"items":{"type":"object"},"type":"array"},"consider_ip":{"type":"boolean"},"wifi_access_points":{"items":{"type":"object"},"type":"array"}},"type":"object"},"IsochroneRequest":{"properties":{"costing":{"$ref":"#/components/schemas/Costing"},"minutes":{"items":{"exclusiveMinimum":0,"maximum":60,"type":"number"},"maxItems":4,"type":"array"},"origin":{"$ref":"#/components/schemas/Point"}},"required":["origin","minutes"],"type":"object"},"LocateCountryRequest":{"properties":{"at":{"format":"date-time","type":"string"},"point":{"$ref":"#/components/schemas/Point"}},"required":["point"],"type":"object"},"MapMatchRequest":{"properties":{"costing":{"$ref":"#/components/schemas/Costing"},"include_attributes":{"type":"boolean"},"include_directions":{"type":"boolean"},"points":{"items":{"properties":{"accuracy_metres":{"type":"number"},"latitude":{"type":"number"},"longitude":{"type":"number"},"timestamp":{"description":"Epoch seconds. Worth sending.","type":"number"}},"type":"object"},"maxItems":2000,"minItems":2,"type":"array"},"search_radius_metres":{"type":"number"}},"required":["points"],"type":"object"},"MatrixRequest":{"description":"origins × destinations may be at most 256 on this deployment.","properties":{"at":{"format":"date-time","type":"string"},"costing":{"$ref":"#/components/schemas/Costing"},"destinations":{"items":{"$ref":"#/components/schemas/Point"},"minItems":1,"type":"array"},"fare":{"country":{"description":"ISO 3166-1. Omitted, it is derived from the pickup, then from the deployment's own setting","type":"string"},"passengers":{"type":"integer"},"rain":{"description":"Supplied by you. Rain is the largest single swing in a motorcycle fare on this continent, and this service has no weather feed","type":"boolean"},"return_trip":{"description":"Price the empty leg back as well","type":"boolean"},"stops":{"description":"Intermediate drops. Derived from the itinerary on /v1/route","type":"integer"},"surge":{"description":"Capped by the tariff's max_surge","type":"number"},"surge_reason":{"description":"Echoed into the breakdown so an app can explain it","type":"string"},"tariff":{"$ref":"#/components/schemas/Tariff"},"vehicle":{"description":"The generic term. The LOCAL word -- okada, moto, zémidjan -- comes back on the answer","enum":["motorcycle","three_wheeler","car","van","truck","bicycle"],"type":"string"},"waiting_minutes":{"type":"number"}},"origins":{"items":{"$ref":"#/components/schemas/Point"},"minItems":1,"type":"array"},"traffic":{"description":"Adjust every cell by an AREA AVERAGE of observed speeds. A matrix has no shapes, so it cannot measure a route it did not compute; the adjustment is labelled `area_average` and a caller who needs per-route traffic asks /v1/route about the pair that matters","type":"boolean"}},"required":["origins","destinations"],"type":"object"},"NearestRoadsRequest":{"properties":{"costing":{"$ref":"#/components/schemas/Costing"},"per_point":{"minimum":1,"type":"integer"},"points":{"items":{"$ref":"#/components/schemas/Point"},"maxItems":100,"type":"array"}},"required":["points"],"type":"object"},"PlaceDetailsRequest":{"properties":{"id":{"type":"string"},"language":{"type":"string"}},"required":["id"],"type":"object"},"PlaceSearchRequest":{"properties":{"categories":{"items":{"type":"string"},"type":"array"},"language":{"type":"string"},"limit":{"type":"integer"},"near":{"$ref":"#/components/schemas/Point"},"open_now":{"type":"boolean"},"query":{"type":"string"},"radius_metres":{"maximum":50000,"type":"number"},"restrict_to":{"$ref":"#/components/schemas/BoundingBox"}},"type":"object"},"PlusCodeDecodeRequest":{"properties":{"code":{"description":"A full code, a short code, or the compound form with the town after a comma","example":"8HXM+22, Kampala","type":"string"},"near":{"$ref":"#/components/schemas/Point"}},"required":["code"],"type":"object"},"PlusCodeEncodeRequest":{"properties":{"length":{"default":10,"description":"Significant digits. 10 is about 14 m, which is what a delivery needs; 11 is about 3.5 m, finer than a phone's GPS","type":"integer"},"point":{"$ref":"#/components/schemas/Point"},"short":{"description":"Also return the speakable form, \"MQPX+9G, Kampala\". Needs a geocoder to name the locality","type":"boolean"}},"required":["point"],"type":"object"},"Point":{"description":"A WGS84 coordinate. The field names are spelled out in full on purpose: half the world's geospatial APIs order a pair lat,lon and the other half lon,lat, and abbreviating buys an entire class of silent bug.","properties":{"latitude":{"example":0.3476,"maximum":90,"minimum":-90,"type":"number"},"longitude":{"example":32.5825,"maximum":180,"minimum":-180,"type":"number"}},"required":["latitude","longitude"],"type":"object"},"ReverseGeocodeRequest":{"properties":{"granularity":{"default":"building","enum":["building","street","neighbourhood","locality","region","country"],"type":"string"},"language":{"type":"string"},"limit":{"type":"integer"},"point":{"$ref":"#/components/schemas/Point"}},"required":["point"],"type":"object"},"RouteRequest":{"properties":{"at":{"format":"date-time","type":"string"},"costing":{"$ref":"#/components/schemas/Costing"},"fare":{"country":{"description":"ISO 3166-1. Omitted, it is derived from the pickup, then from the deployment's own setting","type":"string"},"passengers":{"type":"integer"},"rain":{"description":"Supplied by you. Rain is the largest single swing in a motorcycle fare on this continent, and this service has no weather feed","type":"boolean"},"return_trip":{"description":"Price the empty leg back as well","type":"boolean"},"stops":{"description":"Intermediate drops. Derived from the itinerary on /v1/route","type":"integer"},"surge":{"description":"Capped by the tariff's max_surge","type":"number"},"surge_reason":{"description":"Echoed into the breakdown so an app can explain it","type":"string"},"tariff":{"$ref":"#/components/schemas/Tariff"},"vehicle":{"description":"The generic term. The LOCAL word -- okada, moto, zémidjan -- comes back on the answer","enum":["motorcycle","three_wheeler","car","van","truck","bicycle"],"type":"string"},"waiting_minutes":{"type":"number"}},"locations":{"items":{"$ref":"#/components/schemas/Point"},"maxItems":26,"minItems":2,"type":"array"},"traffic":{"description":"Adjust the duration by what this deployment's own vehicles have observed on the roads this route uses. `seconds` stays the free-flow value; the adjusted one is inside the `traffic` block, so a client that does not read it gets the number this endpoint always gave","type":"boolean"}},"required":["locations"],"type":"object"},"Tariff":{"description":"A price list. Every field is a number an operator can point at and defend: there is no hidden coefficient, because a fare that cannot be explained to the person paying it is a fare that gets argued about at the kerb.\n\nSent with a request, it OVERLAYS the resolved tariff field by field -- send one number to change one number. A zero means \"not specified\" and keeps the underlying value.","properties":{"bands":{"description":"Flat prices by distance, which is how a stage actually prices. Present, they replace base/per_km/per_minute for the trips they cover","items":{"properties":{"label":{"type":"string"},"price":{"type":"number"},"up_to_km":{"type":"number"}},"type":"object"},"type":"array"},"base":{"description":"Flag-fall, charged once","type":"number"},"booking_fee":{"description":"Added AFTER multipliers: a surge that multiplied it would charge more for the same booking","type":"number"},"cash_step":{"description":"The increment a fare is SETTLED in, which is not the same question as `decimals`. UGX 500, KES 10, NGN 50, GHS 0.5","type":"number"},"commission_percent":{"description":"The platform's take. It SPLITS the fare; it does not add to it","type":"number"},"currency":{"description":"ISO 4217","type":"string"},"decimals":{"description":"The currency's ISO minor unit","type":"integer"},"free_waiting_minutes":{"type":"number"},"included_km":{"description":"Distance the base fare already covers, before per-km charging starts","type":"number"},"max_passengers":{"type":"integer"},"max_surge":{"type":"number"},"minimum":{"description":"The floor. On this continent it is the most important number in the table: most urban motorcycle trips are priced by it","type":"number"},"negotiable":{"description":"The fare is agreed at the kerb rather than metered","type":"boolean"},"notes":{"items":{"type":"string"},"type":"array"},"per_extra_passenger":{"type":"number"},"per_km":{"type":"number"},"per_minute":{"description":"What makes a jam cost something. With no traffic reported, these are free-flow minutes","type":"number"},"per_stop":{"description":"Per intermediate drop","type":"number"},"rain_factor":{"description":"Applied when the caller says it is raining. An INPUT, not a prediction: this service has no weather feed","type":"number"},"reviewed":{"description":"YYYY-MM, on the built-in tables","type":"string"},"source":{"enum":["built-in-reference","derived-reference","operator"],"type":"string"},"spread_percent":{"description":"Half-width of the quoted range, as a fraction","type":"number"},"symbol":{"type":"string"},"vehicle":{"enum":["motorcycle","three_wheeler","car","van","truck","bicycle"],"type":"string"},"waiting_per_minute":{"type":"number"},"windows":{"description":"Time-of-day multipliers, evaluated in the LOCAL zone of the pickup. The first match wins; they do not compound","items":{"properties":{"days":{"description":"0 = Sunday; empty means every day","items":{"type":"integer"},"type":"array"},"factor":{"type":"number"},"from_hour":{"type":"number"},"name":{"type":"string"},"to_hour":{"type":"number"}},"type":"object"},"type":"array"}},"type":"object"},"TimezoneConvertRequest":{"properties":{"at":{"format":"date-time","type":"string"},"from":{"$ref":"#/components/schemas/Point"},"to":{"$ref":"#/components/schemas/Point"}},"required":["from","to"],"type":"object"},"TimezoneRequest":{"properties":{"at":{"format":"date-time","type":"string"},"point":{"$ref":"#/components/schemas/Point"}},"required":["point"],"type":"object"},"TourRequest":{"properties":{"costing":{"$ref":"#/components/schemas/Costing"},"include_shape":{"default":true,"type":"boolean"},"origin":{"$ref":"#/components/schemas/Point"},"return_to_origin":{"default":true,"type":"boolean"},"stops":{"items":{"properties":{"id":{"description":"Yours, and must be unique. An ambiguous delivery sequence is a parcel handed to the wrong customer.","type":"string"},"point":{"$ref":"#/components/schemas/Point"}},"required":["id","point"],"type":"object"},"maxItems":25,"type":"array"}},"required":["origin","stops"],"type":"object"},"TrafficConditionsRequest":{"properties":{"at":{"format":"date-time","type":"string"},"box":{"$ref":"#/components/schemas/BoundingBox"},"costing":{"description":"Travel mode. Defaults to this deployment's configured default (motor_scooter).","enum":["auto","motor_scooter","motorcycle","bicycle","pedestrian","truck","bus","taxi"],"type":"string"},"points":{"items":{"$ref":"#/components/schemas/Point"},"type":"array"},"polyline":{"type":"string"},"polyline_precision":{"default":6,"enum":[5,6],"type":"integer"},"segments":{"items":{"properties":{"direction":{"type":"integer"},"way_id":{"format":"int64","type":"integer"}},"type":"object"},"type":"array"},"worse_than":{"default":"moderate","enum":["free","light","moderate","heavy","standstill"],"type":"string"}},"type":"object"},"TrafficReportRequest":{"properties":{"costing":{"description":"Travel mode. Defaults to this deployment's configured default (motor_scooter).","enum":["auto","motor_scooter","motorcycle","bicycle","pedestrian","truck","bus","taxi"],"type":"string"},"segments":{"description":"Speeds already matched to way ids","items":{"properties":{"at":{"format":"date-time","type":"string"},"direction":{"default":1,"enum":[1,-1],"type":"integer"},"free_flow_kph":{"description":"Required. Without it this service cannot tell a jam from a slow road: 20 km/h is a standstill on a bypass and free-flowing through a market","type":"number"},"name":{"type":"string"},"speed_kph":{"type":"number"},"way_id":{"description":"OpenStreetMap way","format":"int64","type":"integer"}},"required":["way_id","speed_kph","free_flow_kph"],"type":"object"},"type":"array"},"traces":{"description":"Position fixes, map-matched here","items":{"properties":{"points":{"items":{"properties":{"accuracy_metres":{"type":"number"},"at":{"format":"date-time","type":"string"},"point":{"$ref":"#/components/schemas/Point"}},"required":["point","at"],"type":"object"},"type":"array"}},"type":"object"},"type":"array"}},"type":"object"},"ValidateAddressRequest":{"description":"One of `query` or `address` is required.","properties":{"address":{"$ref":"#/components/schemas/Address"},"bias":{"$ref":"#/components/schemas/Point"},"countries":{"items":{"type":"string"},"type":"array"},"language":{"type":"string"},"query":{"type":"string"}},"type":"object"}},"securitySchemes":{"apiKeyHeader":{"in":"header","name":"X-API-Key","type":"apiKey"},"apiKeyQuery":{"description":"The only option for tile, static-map and iframe URLs, because a browser will not attach a header to those.","in":"query","name":"key","type":"apiKey"},"bearerAuth":{"description":"`Authorization: Bearer \u003ckey\u003e`","scheme":"bearer","type":"http"}}},"info":{"description":"Every service in this document is answered by one binary in front of a Valhalla routing engine, with optional geocoding, timezone and basemap backends.\n\nSeveral services are optional and a given deployment may not have them. `GET /v1/capabilities` is unauthenticated and reports exactly what this instance can answer, together with the configuration that would enable the rest -- call it at startup rather than discovering a 501 in front of a user.\n\nDistances carry a `method` field saying how they were derived. A `great_circle` value is a real answer to price from, but it is not a road distance; decide once, in one place, what your product does with it.","license":{"name":"Map data: ODbL (OpenStreetMap contributors)"},"summary":"Self-hosted routing, geocoding, places and maps over OpenStreetMap.","title":"duara-geo","version":"2.0.0"},"openapi":"3.1.0","paths":{"/healthz":{"get":{"description":"503 until the routing engine reports a built tileset, which takes ten to twenty minutes on a first start. Use `/livez` for liveness -- pointing a liveness probe at this kills the container mid-build and the deployment never converges.","operationId":"health","responses":{"200":{"description":"Ready"},"503":{"description":"The engine has no tileset yet"}},"security":[],"summary":"Readiness","tags":["Platform"]}},"/v1/autocomplete":{"post":{"description":"Returns rows pre-split into the two lines a mobile list renders. An input under two characters returns an empty list rather than an error.","operationId":"autocomplete","requestBody":{"content":{"application/json":{"example":{"bias":{"latitude":0.3476,"longitude":32.5825},"input":"ntin"},"schema":{"$ref":"#/components/schemas/AutocompleteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Type-ahead address suggestions","tags":["Places"]}},"/v1/capabilities":{"get":{"description":"Unauthenticated. Call it at startup and render the product this instance supports.","operationId":"capabilities","responses":{"200":{"description":"Capability report"}},"security":[],"summary":"What this deployment can answer","tags":["Platform"]}},"/v1/countries":{"get":{"description":"Currency and the cash increment fares settle in, IANA time zone, driving side, the local word for a motorcycle taxi, the Geofabrik extract to build from, and the bounding box.\n\nUnauthenticated, like `/v1/capabilities`: it is how a developer finds out what a deployment is for before anybody issues them a key.","operationId":"countries","parameters":[{"description":"Filter to a UN subregion: Eastern Africa, Western Africa, and so on","in":"query","name":"region","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The country table"}},"security":[],"summary":"Every country this service knows, with what a client needs about each","tags":["Africa"]}},"/v1/countries/{code}":{"get":{"operationId":"country","parameters":[{"description":"ISO 3166-1 alpha-2 or alpha-3, case-insensitive","in":"path","name":"code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"A country"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No such country."}},"security":[],"summary":"One country","tags":["Africa"]}},"/v1/describe-location":{"post":{"description":"\"180 m south-west of Shell Kabalagala, off Ggaba Road, Kabalagala, Kampala\".\n\nMost of the places this service is used have no house numbers and many have no road signs, so landmark-relative directions are not a workaround -- they are the addressing system, and they are what the rider already navigates by. Candidates are ranked by PROMINENCE before distance: the nearest tagged feature is often a hairdresser with no sign, and the useful one is the fuel station two hundred metres away that everybody navigates by.\n\n`method` says what the sentence was built from: `landmark`, `address`, or `plus_code` when neither was available. The Plus Code is on every response regardless.","operationId":"describeLocation","requestBody":{"content":{"application/json":{"example":{"point":{"latitude":0.3476,"longitude":32.5825}},"schema":{"$ref":"#/components/schemas/DescribeLocationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Describe a coordinate the way a person would","tags":["Places"]}},"/v1/elevation":{"post":{"description":"Accepts points or an encoded polyline, and can resample to a fixed number of evenly spaced samples -- which is what turns a route into a chart. Answers 501 rather than zeroes when the engine was built without a terrain model.","operationId":"elevation","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.5825},{"latitude":0.3556,"longitude":32.6136}],"samples":50},"schema":{"$ref":"#/components/schemas/ElevationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Ground elevation along a path","tags":["Environment"]}},"/v1/embed":{"get":{"operationId":"embed","parameters":[{"description":"`lat,lng`","in":"query","name":"center","required":false,"schema":{"type":"string"}},{"description":"","in":"query","name":"zoom","required":false,"schema":{"type":"integer"}},{"description":"`lat,lng` -- repeatable","in":"query","name":"marker","required":false,"schema":{"type":"string"}},{"description":"`lat,lng`; with `destination`, draws the route","in":"query","name":"origin","required":false,"schema":{"type":"string"}},{"description":"`lat,lng`","in":"query","name":"destination","required":false,"schema":{"type":"string"}},{"description":"A place name to find and centre on","in":"query","name":"q","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"An HTML page"}},"summary":"An interactive map as one iframe","tags":["Maps"]}},"/v1/fare":{"post":{"description":"Turns a distance and a duration into a number somebody can hand over in cash.\n\n**The minimum dominates.** Most urban motorcycle trips are under three kilometres, so most fares are decided by the floor rather than by the rate.\n\n**The total is rounded to the cash step** -- 500 shillings, 10 Kenyan shillings, 50 naira -- because that is what changes hands at the kerb. The rounding is a line in the breakdown, so the components sum to the total.\n\n**Time-of-day surcharges are applied in the LOCAL time of the pickup.** A 22:00 night rate applied against UTC begins at one in the morning in Lagos. Where no local zone can be resolved, the windows are skipped and the response says so rather than guessing.\n\n**A fare is a range.** In a market where the number is agreed at the kerb, a single figure is a precision this calculation does not have.\n\nSend `origin` and `destination` to have the trip routed, or `distance_km` and `seconds` to price one you measured yourself -- not both.","operationId":"fare","requestBody":{"content":{"application/json":{"example":{"destination":{"latitude":0.3556,"longitude":32.6136},"origin":{"latitude":0.3476,"longitude":32.5825},"vehicle":"motorcycle"},"schema":{"$ref":"#/components/schemas/FareRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Price a trip","tags":["Fares"]}},"/v1/geocode":{"post":{"description":"Every result carries `quality` (rooftop, street, neighbourhood, locality, region) and a `confidence`. Read them before using the coordinate: a geocoder returns a city centroid for a query it did not understand, and that is a valid coordinate kilometres from the door.","operationId":"geocode","requestBody":{"content":{"application/json":{"example":{"bias":{"latitude":0.3476,"longitude":32.5825},"query":"Kampala Road"},"schema":{"$ref":"#/components/schemas/GeocodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Turn an address into coordinates","tags":["Places"]}},"/v1/geofence/check":{"post":{"description":"Answers the checkout question: do we deliver here, does a surcharge apply, is this a no-go area.\n\n`served` and `delivery_zones_defined` are two fields because they answer two questions. A deployment with no delivery zones serves everywhere its map covers; one with them serves inside them and nowhere else. A single boolean would refuse every order the first deployment has.\n\nZones whose hours exclude the queried instant are still returned, with `open` false: \"the curfew applies here but not now\" is a different answer from \"no zone applies\".","operationId":"geofenceCheck","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.5825}]},"schema":{"$ref":"#/components/schemas/GeofenceCheckRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Which zones contain these points","tags":["Zones"]}},"/v1/geofence/path":{"post":{"description":"Crossing points are interpolated along the segment that straddles the boundary rather than snapped to the nearest vertex, because a route shape has a vertex every few hundred metres on a straight and that is money on a per-kilometre zone charge.","operationId":"geofencePath","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.54},{"latitude":0.3476,"longitude":32.63}]},"schema":{"$ref":"#/components/schemas/GeofencePathRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Which zones a route crosses, and where","tags":["Zones"]}},"/v1/geofence/track":{"post":{"description":"Produces enter/exit events with dwell times. Entry and exit moments between two pings are interpolated and marked `interpolated`, because pings arrive every thirty seconds at best and an entry taken from the first ping inside can be minutes late.\n\nA zone entered and left entirely between two pings is **not** reported. This service will not draw a straight line between two observations and claim the rider crossed what is on it.","operationId":"geofenceTrack","requestBody":{"content":{"application/json":{"example":{"pings":[{"at":"2026-09-12T08:00:00Z","point":{"latitude":0.3476,"longitude":32.5825}},{"at":"2026-09-12T08:12:00Z","point":{"latitude":0.3556,"longitude":32.6136}}]},"schema":{"$ref":"#/components/schemas/GeofenceTrackRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Zone entries and exits from a vehicle's position reports","tags":["Zones"]}},"/v1/geofences":{"get":{"operationId":"geofences","parameters":[{"description":"delivery, no_go, surcharge, hub or alert","in":"query","name":"kind","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Zones"}},"summary":"List the zones","tags":["Zones"]},"post":{"description":"Guarded by the **admin token**, not an API key. A zone decides where a platform delivers and what it charges; an API key that could redraw one is an API key that can change the business, and API keys travel in query strings and browser code.\n\nAccepts this service's own format or a GeoJSON FeatureCollection, because zones are drawn on a map and every drawing tool emits GeoJSON. A `Point` feature with a `radius_metres` property becomes a circle.","operationId":"geofenceUpsert","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"required":true},"responses":{"200":{"description":"Saved"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The admin token is required."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Key management is not enabled: no GEO_ADMIN_TOKEN is set."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The zone would match nothing. Read `message`."}},"summary":"Create or replace zones","tags":["Zones"]}},"/v1/geofences.geojson":{"get":{"description":"Circles are emitted as 64-point polygons here and stay circles in the store: the drawing form and the authoritative form are different things.","operationId":"geofencesGeoJSON","parameters":[{"description":"Filter by kind","in":"query","name":"kind","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"A GeoJSON FeatureCollection"}},"summary":"The zones as a FeatureCollection, for drawing","tags":["Zones"]}},"/v1/geolocate":{"post":{"description":"**This endpoint always answers 501, deliberately.**\n\nInferring a position from surrounding wifi and cell radios needs a radio map: a database of which access points and towers are where, built by driving every street with a receiver. No open dataset does it at usable quality, and a service that fell back to the centroid of the caller's country would return a coordinate that gets used to price a delivery.\n\nIt exists so that a codebase migrating from another platform gets a clear, explained refusal at this one call rather than a 404 that looks like a deployment fault. Use the browser's own Geolocation API, which asks the device rather than the network, and send the coordinate it returns to `/v1/reverse-geocode`.","operationId":"geolocate","requestBody":{"content":{"application/json":{"example":{"consider_ip":false},"schema":{"$ref":"#/components/schemas/GeolocateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Radio positioning -- a documented refusal","tags":["Environment"]}},"/v1/isochrone":{"post":{"description":"Returns a GeoJSON FeatureCollection of polygons, sorted largest first so a client painting them in order gets the widest underneath. Expensive: this is a planning tool, not a pricing input.","operationId":"isochrone","requestBody":{"content":{"application/json":{"example":{"minutes":[10,20,30],"origin":{"latitude":0.3476,"longitude":32.5825}},"schema":{"$ref":"#/components/schemas/IsochroneRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"The area reachable from a point within N minutes","tags":["Routing"]}},"/v1/locate-country":{"post":{"description":"One call for the four questions a platform asks about every new pin. Countries are resolved against generalised boundaries at about two kilometres of accuracy; a few island territories fall back to a bounding box and are graded `likely`.","operationId":"locateCountry","requestBody":{"content":{"application/json":{"example":{"point":{"latitude":6.5244,"longitude":3.3792}},"schema":{"$ref":"#/components/schemas/LocateCountryRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Which country, currency, clock and Plus Code apply here","tags":["Africa"]}},"/v1/map-match":{"post":{"description":"Send timestamps where you have them: with them the matcher can discard an implausible fix instead of routing a detour to reach it.","operationId":"mapMatch","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.5825,"timestamp":1757660000},{"latitude":0.3512,"longitude":32.5934,"timestamp":1757660130}]},"schema":{"$ref":"#/components/schemas/MapMatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Reconcile a GPS trace with the road network","tags":["Roads"]}},"/v1/matrix":{"post":{"description":"Computed in one engine traversal. **This endpoint never fails**: when the engine is unavailable or a pin cannot be snapped to a road, the answer is great-circle distance with `method` set to `great_circle` and `degraded` set to true.\n\nAsk about a whole basket in one call -- a four-vendor cart is four origins and one doorstep, which is cheaper for the engine than four separate requests.","operationId":"matrix","requestBody":{"content":{"application/json":{"example":{"costing":"motor_scooter","destinations":[{"latitude":0.0512,"longitude":32.4637}],"origins":[{"latitude":0.3476,"longitude":32.5825}]},"schema":{"$ref":"#/components/schemas/MatrixRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Road distance and time for every origin against every destination","tags":["Routing"]}},"/v1/nearest-roads":{"post":{"description":"`snapped_distance_metres` is how far the pin had to move. A pin 200 m from any road is a pin in a field, and that is worth telling a shopper before they order.","operationId":"nearestRoads","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.5825}]},"schema":{"$ref":"#/components/schemas/NearestRoadsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Snap coordinates to the nearest road","tags":["Roads"]}},"/v1/places/details":{"post":{"description":"Ids are this deployment's own and are not interchangeable with another platform's.","operationId":"placeDetails","requestBody":{"content":{"application/json":{"example":{"id":"nominatim:N240109189"},"schema":{"$ref":"#/components/schemas/PlaceDetailsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Full record for a place id","tags":["Places"]}},"/v1/places/nearby":{"post":{"description":"Needs an Overpass backend. A geocoder ranks by fame rather than distance and cannot answer this question well.","operationId":"nearbyPlaces","requestBody":{"content":{"application/json":{"example":{"categories":["pharmacy"],"near":{"latitude":0.3476,"longitude":32.5825},"radius_metres":1500},"schema":{"$ref":"#/components/schemas/PlaceSearchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Places of a category within a radius, nearest first","tags":["Places"]}},"/v1/places/search":{"post":{"description":"","operationId":"searchPlaces","requestBody":{"content":{"application/json":{"example":{"near":{"latitude":0.3476,"longitude":32.5825},"query":"pharmacy","radius_metres":2000},"schema":{"$ref":"#/components/schemas/PlaceSearchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Text search over places","tags":["Places"]}},"/v1/pluscode/decode":{"post":{"description":"Accepts a full code, a short code with `near`, or the compound form \"8HXM+22, Kampala\", whose locality is geocoded to recover it. A short code with no reference is **refused**: it names a different square in every 100 km cell on Earth.","operationId":"plusCodeDecode","requestBody":{"content":{"application/json":{"example":{"code":"8HXM+22, Kampala"},"schema":{"$ref":"#/components/schemas/PlusCodeDecodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Turn a Plus Code into coordinates","tags":["Places"]}},"/v1/pluscode/encode":{"post":{"description":"An Open Location Code names a SQUARE, not a point: ten digits is about fourteen metres and six is five and a half kilometres. `resolution_metres` says which, so that a coarse code is not mistaken for a doorstep.\n\nFor much of this continent this is the address. Kenya adopted Plus Codes nationally; The Gambia and Cabo Verde use them as the postal address. Set `short: true` to get the speakable form, \"MQPX+9G, Kampala\", which needs a geocoder to name the locality.","operationId":"plusCodeEncode","requestBody":{"content":{"application/json":{"example":{"point":{"latitude":0.3476,"longitude":32.5825},"short":true},"schema":{"$ref":"#/components/schemas/PlusCodeEncodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Turn a coordinate into a Plus Code","tags":["Places"]}},"/v1/reverse-geocode":{"post":{"description":"`granularity` picks how specific an answer to give, from `building` to `country`.","operationId":"reverseGeocode","requestBody":{"content":{"application/json":{"example":{"granularity":"building","point":{"latitude":0.3476,"longitude":32.5825}},"schema":{"$ref":"#/components/schemas/ReverseGeocodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Turn a coordinate into an address","tags":["Places"]}},"/v1/route":{"post":{"description":"Returns an encoded polyline at **precision 6** (`shape_precision` says so) and the turn-by-turn instructions. Unlike the matrix this can fail: there is no straight-line equivalent of a turn instruction.","operationId":"route","requestBody":{"content":{"application/json":{"example":{"locations":[{"latitude":0.3476,"longitude":32.5825},{"latitude":0.3556,"longitude":32.6136}]},"schema":{"$ref":"#/components/schemas/RouteRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"A drawn path through locations in the order given","tags":["Routing"]}},"/v1/speed-limits":{"post":{"description":"Read `edges_with_known_limit`: OpenStreetMap's `maxspeed` tag is absent on most roads in most countries, and a limit of zero means unknown rather than zero.","operationId":"speedLimits","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.5825},{"latitude":0.3512,"longitude":32.5934}]},"schema":{"$ref":"#/components/schemas/MapMatchRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Posted speed limits along a trace","tags":["Roads"]}},"/v1/staticmap":{"get":{"description":"Centre and zoom are optional: with markers or a path and no centre, the map frames itself. The coordinates are necessarily in the URL because this has to work in an `\u003cimg\u003e` tag; POST the same parameters as JSON where a browser does not have to fetch it.","operationId":"staticMap","parameters":[{"description":"`WIDTHxHEIGHT`, default 600x400, maximum 2048 each way","in":"query","name":"size","required":false,"schema":{"type":"string"}},{"description":"`lat,lng`","in":"query","name":"center","required":false,"schema":{"type":"string"}},{"description":"0 to 22","in":"query","name":"zoom","required":false,"schema":{"type":"integer"}},{"description":"1 or 2 (device pixel ratio)","in":"query","name":"scale","required":false,"schema":{"type":"integer"}},{"description":"`color:red|label:A|size:mid|lat,lng|lat,lng` -- repeatable","in":"query","name":"markers","required":false,"schema":{"type":"string"}},{"description":"`color:0x1967d2cc|weight:5|enc6:\u003cpolyline\u003e` or coordinates -- repeatable","in":"query","name":"path","required":false,"schema":{"type":"string"}},{"description":"png or jpg","in":"query","name":"format","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"image/png":{"schema":{"format":"binary","type":"string"}}},"description":"An image"}},"summary":"Render a map as a PNG","tags":["Maps"]}},"/v1/style.json":{"get":{"operationId":"style","responses":{"200":{"description":"A style document"}},"security":[],"summary":"A MapLibre GL style document pointing at this deployment","tags":["Maps"]}},"/v1/tariffs":{"get":{"description":"Read `operator_tariffs`. When it is false, every fare this deployment quotes comes from the built-in **reference** table, which is indicative, dated, and moves with the fuel price. Set `GEO_TARIFFS_FILE` before charging anybody.\n\nWith a country, each vehicle's tariff comes with example fares at two, five and twenty-five kilometres, so that a table of coefficients reads as prices.","operationId":"tariffs","parameters":[{"description":"ISO 3166-1 code; defaults to the deployment's own","in":"query","name":"country","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tariffs"}},"summary":"The price lists this deployment would use","tags":["Fares"]}},"/v1/tariffs/{country}":{"get":{"operationId":"tariffsForCountry","parameters":[{"description":"ISO 3166-1 code","in":"path","name":"country","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tariffs"}},"summary":"Every vehicle's price list for one country","tags":["Fares"]}},"/v1/tiles/{z}/{x}/{y}":{"get":{"description":"A missing tile answers **204**, not 404: holes in a basemap are normal and every map library treats a 404 as an error worth retrying.","operationId":"tile","parameters":[{"description":"Zoom level","in":"path","name":"z","required":true,"schema":{"type":"integer"}},{"description":"Tile column","in":"path","name":"x","required":true,"schema":{"type":"integer"}},{"description":"Tile row, with the format extension: `2044.png`","in":"path","name":"y","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Tile bytes"},"204":{"description":"No tile at this address -- draw background"}},"summary":"One basemap tile","tags":["Maps"]}},"/v1/timezone":{"post":{"description":"Standard and summer-time offsets are returned separately. Pass `at` for a future instant: a zone's offset is not a constant.","operationId":"timezone","requestBody":{"content":{"application/json":{"example":{"point":{"latitude":0.3476,"longitude":32.5825}},"schema":{"$ref":"#/components/schemas/TimezoneRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"The IANA time zone at a coordinate","tags":["Environment"]}},"/v1/timezone/convert":{"post":{"description":"Two coordinates, one call, and the subtraction done once rather than in every client. `same_day` catches the case that actually bites: a delivery window that crosses midnight at one end and not the other. Africa is five hours wide and a great deal of its commerce is regional.","operationId":"timezoneConvert","requestBody":{"content":{"application/json":{"example":{"from":{"latitude":6.5244,"longitude":3.3792},"to":{"latitude":-1.2921,"longitude":36.8219}},"schema":{"$ref":"#/components/schemas/TimezoneConvertRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"What time is it there, when it is this time here","tags":["Environment"]}},"/v1/tour":{"post":{"description":"Optimises **time**, not distance, and does not pin the last stop -- a rider leaves the shop full and comes back empty. Stop ids are yours and come back attached to the answer.","operationId":"tour","requestBody":{"content":{"application/json":{"example":{"origin":{"latitude":0.3476,"longitude":32.5825},"return_to_origin":true,"stops":[{"id":"SO-1043","point":{"latitude":0.3556,"longitude":32.6136}},{"id":"SO-1044","point":{"latitude":0.3006,"longitude":32.6019}}]},"schema":{"$ref":"#/components/schemas/TourRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Sequence a set of drops from a dispatch point","tags":["Routing"]}},"/v1/traffic/conditions":{"post":{"description":"Read `summary.coverage` before anything else. A delay factor of 1.05 on eight per cent coverage is not \"traffic is fine\", it is \"we have almost no data\" -- and a road nobody has driven comes back `unknown`, never `free`.","operationId":"trafficConditions","requestBody":{"content":{"application/json":{"example":{"points":[{"latitude":0.3476,"longitude":32.5825},{"latitude":0.3556,"longitude":32.6136}]},"schema":{"$ref":"#/components/schemas/TrafficConditionsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"How congested is this route, or this area","tags":["Traffic"]}},"/v1/traffic/flow":{"get":{"description":"Point a MapLibre or Leaflet source at this. Each feature carries a `level`, a `colour` to draw it in, the observed and free-flow speeds, and how many samples and how old.\n\nA road nobody has driven is **absent** rather than green.\n\n`bbox` is longitude-first, matching every mapping library's convention -- the one place in this API where it is, and it is named `bbox` to say so.","operationId":"trafficFlow","parameters":[{"description":"`min_lon,min_lat,max_lon,max_lat`. Omitted, the whole store","in":"query","name":"bbox","required":false,"schema":{"type":"string"}},{"description":"free, light, moderate (default), heavy, standstill","in":"query","name":"worse_than","required":false,"schema":{"type":"string"}},{"description":"RFC 3339 instant; defaults to now","in":"query","name":"at","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"A GeoJSON FeatureCollection of congested segments"}},"summary":"The jam layer, as GeoJSON","tags":["Traffic"]}},"/v1/traffic/report":{"post":{"description":"There is no third-party traffic feed behind this service and none is invented. The feed is **your own riders**: a platform with three hundred vehicles in one city has a better picture of that city than any global product, and until now there was nowhere to put it.\n\nSend `traces` (position fixes with timestamps, which are map-matched here) or `segments` (speeds you have already matched to OpenStreetMap way ids). Observations live in this process's memory, so send a fleet's traces to one instance.","operationId":"trafficReport","requestBody":{"content":{"application/json":{"example":{"traces":[{"points":[{"at":"2026-09-12T08:00:00Z","point":{"latitude":0.3476,"longitude":32.5825}},{"at":"2026-09-12T08:04:00Z","point":{"latitude":0.3512,"longitude":32.5934}}]}]},"schema":{"$ref":"#/components/schemas/TrafficReportRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Report where your vehicles have been","tags":["Traffic"]}},"/v1/usage":{"get":{"operationId":"usage","responses":{"200":{"description":"Usage report"}},"summary":"This key's consumption today","tags":["Platform"]}},"/v1/validate-address":{"post":{"description":"Returns a verdict rather than candidates. `usable_for_delivery` is the boolean most callers act on; the reasons and alternatives are the evidence behind it.","operationId":"validateAddress","requestBody":{"content":{"application/json":{"example":{"query":"Plot 12 Kampala Road, Kampala"},"schema":{"$ref":"#/components/schemas/ValidateAddressRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Success"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The API key is missing or not valid."},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request was understood and refused. Read `error` -- `unroutable` must not be retried."},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limit or daily quota exceeded. Honour `Retry-After`."},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"This deployment does not run the backend that service needs. See `GET /v1/capabilities`."},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The routing engine could not be reached. Retry shortly."}},"summary":"Decide whether an address may be dispatched to without confirmation","tags":["Places"]}}},"security":[{"bearerAuth":[]},{"apiKeyHeader":[]},{"apiKeyQuery":[]}],"servers":[{"url":"https://maps.duara.shop"}],"tags":[{"description":"Distances, routes, reachable areas and multi-drop sequencing.","name":"Routing"},{"description":"Map matching, road attributes and snapping.","name":"Roads"},{"description":"Geocoding, autocomplete, search and address validation.","name":"Places"},{"description":"Turning a distance into a price somebody can pay in cash.","name":"Fares"},{"description":"Geofences: delivery areas, restricted areas, surcharge zones, and what a rider crossed.","name":"Zones"},{"description":"Observed road speeds, reported by this deployment's own vehicles.","name":"Traffic"},{"description":"The country table: currency, clock, vocabulary, driving side, map extract.","name":"Africa"},{"description":"Elevation and time zones.","name":"Environment"},{"description":"Tiles, styles, static images and embeddable pages.","name":"Maps"},{"description":"Capability discovery, usage and health.","name":"Platform"}]}
