Documentation menu
POST/v1/errands/plan
Access and price
| Auth | An API key, as Authorization: Bearer snk_… or ?api_key=snk_…. Metered. Needs a full key; a browser (maps) key is refused. |
|---|---|
| Price | A flat 10 calls. Class: standard, or premium for truck and ADR work. Published as unit_rules.errand_plan in /pricing.json. |
| Premium | When the body asks for truck routing: "costing": "truck", a top-level adr object or "hazmat": true |
What it does
Returns 200 whether or not the chain fits: an infeasible chain is an
answer, with the errand that costs the most named and the largest set
that does fit planned out in full. The precedent is POST /v1/ev/plan,
which answers feasible: false with a named cause rather than
inventing a journey, and the reasoning is identical. A driver who
cannot do all three things needs to be told which two they can.
Call it
curl -fsS -X POST "https://api.mapmap.ai/v1/errands/plan" \
-H "Authorization: Bearer $MAPMAP_KEY" \
-H "Content-Type: application/json" \
-d '{"arrive_by":"string","destination":{"lat":0,"lon":0},"errands":[{"category":"string","dwell_minutes":0,"id":"string"}],"origin":{"lat":0,"lon":0}}'Request body
application/json · ErrandPlanRequest
| Field | Type | Required | Description |
|---|---|---|---|
| arrive_by | string | yes | The hard arrival time, RFC 3339 with an offset. |
| costing_options | not stated | no | Valhalla costing options, passed to every engine call. The costing
itself is always auto. |
| depart_at | string | no | When they set off, RFC 3339. Defaults to now, in the offset
arrive_by carries. |
| destination | Destination | yes | Where they have to be, and what to call it. |
| errands | ErrandInput[] | yes | The errands, in any order: the answer decides the order. |
| max_detour_minutes | number | no | How far off the direct route a candidate may sit, as a detour in
minutes. Default [DEFAULT_MAX_DETOUR_MINUTES]. |
| origin | Point | yes | Where the driver sets off from. |
Example: A request assembled from this schema's own field examples, enums, defaults and types; not a recorded call
{
"arrive_by": "string",
"destination": {
"lat": 0,
"lon": 0
},
"errands": [
{
"category": "string",
"dwell_minutes": 0,
"id": "string"
}
],
"origin": {
"lat": 0,
"lon": 0
}
}Responses
{feasible, departure, arrive_by, arrival, slack_s, plan: {stops[]: {errand, kind, category, place: {name, label, lat, lon, id}, arrive, depart, dwell_s, drive_s, distance_m, hours: {verdict, tag, source, message}}, final_leg, total_drive_s, total_dwell_s, total_duration_s, total_distance_m}, errands[], search, opening_hours, geometry_polyline6, usage, verification, attribution}. geometry_note replaces geometry_polyline6 when the follow-up route through the stops failed: the plan stands, only the drawn line is missing. hours_note appears when the window crosses the hour a clock change can fall on and a stop was reached after it: those stops read unknown whatever their tag says. When feasible is false the answer carries reason {code, message, over_by_s, blocking: {errand, adds_s}} and, where anything at all fits, achievable: the largest set of errands that does fit, planned in full, with dropped naming what had to go| Field | Type | Required | Description |
|---|---|---|---|
| arrival | string | no | Planned arrival, ISO 8601. |
| arrive_by | string | no | The deadline as sent. |
| attribution | string | no | Place data attribution. |
| costing | string | no | The costing used. |
| departure | string | no | Planned departure, ISO 8601. |
| errands | object[] | no | Each errand: its category, the place chosen, the dwell and how many candidates were costed. |
| eta_calibration | object | no | The typical-delay calibration applied to the drive times. |
| feasible | boolean | no | Whether every errand fits before arrive_by. |
| geometry_polyline6 | string | no | The whole trip shape, precision-6 encoded polyline. |
| opening_hours | object | no | How opening hours were applied to candidates, and how many carried them. |
| plan | object | no | The chosen stops in order (place, arrive, depart,
dwell_s, drive_s), the final_leg and the totals. |
| search | object | no | The candidate search: radius, corridor and counts. |
| slack_s | number | no | Seconds to spare before the deadline. |
| usage | string | no | When the plan is meant to be made (before setting off). |
| verification | string | no | What every stop is: a place in this deployment's index, or a coordinate the caller supplied. |
Example: Recorded from the hosted gateway on 2026-11-05 with a test key: POST /v1/errands/plan, a pharmacy and a petrol station on a Brighton school run; lists trimmed to their first items
{
"arrival": "2026-11-05T21:14:53Z",
"arrive_by": "2026-11-06T15:15:00Z",
"attribution": "Place data © OpenStreetMap contributors, ODbL.",
"costing": "auto",
"departure": "2026-11-05T20:42:13Z",
"errands": [
{
"candidates_costed": 6,
"candidates_in_corridor": 25,
"category": "pharmacy",
"dwell_s": 360,
"excluded_closed": 1,
"id": "prescription",
"kind": "category",
"matched_categories": [
"pharmacy"
]
},
{
"candidates_costed": 6,
"candidates_in_corridor": 20,
"category": "petrol station",
"dwell_s": 300,
"excluded_closed": 0,
"id": "petrol",
"kind": "category",
"matched_categories": [
"fuel"
]
}
],
"eta_calibration": {
"applied": true,
"cells_calibrated": 179,
"cells_not_walked": 0,
"cells_unchanged": 17,
"method": "gb_typical_delay_v1",
"note": "Local roads in Great Britain carry no live traffic data, so the time on them includes the junction and congestion delay a typical car meets at this time of day, from Department for Transport statistics, on top of the routing engine's free-flowing time: the same delay POST /route adds, applied to the path each cell was costed on. It is a typical figure, not live traffic. Motorways, roads covered by National Highways traffic data and roads outside Great Britain keep the engine's own time.",
"period": "all_day",
"sources": [
"Department for Transport, tables CGN0503 and CGN0504: average speed and delay on local 'A' roads in England, urban and rural, by time of day, rolling year to June 2026"
]
},
"feasible": true,
"geometry_polyline6": "}ij}_BtxqGhBTvEh@vFx@cAfSlFzBr@ZbM~Dh@lFj@YnIxBtPjGfPvFjDlApAj@",
"opening_hours": {
"candidates_costed": 12,
"candidates_with_a_tag": 4,
"clock_change_possible_from": "2026-11-06T01:00:00Z",
"coverage_note": "Opening hours come from OpenStreetMap's `opening_hours` tag, which most places do not carry: on a British sample of the production index taken on 21 September 2026, 41 % of places carried one at all. A stop with no usable tag is planned and flagged, never quietly treated as open.",
"evaluated_in_offset": "+00:00",
"policy": "A stop the map's opening-hours tag proves shut at the time you would arrive is dropped and the chain re-solved. No stop is ever reported as known-open: `open_on_the_tag` means the tag covers your arrival time, which is evidence and not a promise.",
"stops_closed_on_the_tag": 0,
"stops_excluded_as_closed": 1,
"stops_open_on_the_tag": 0,
"stops_with_no_usable_tag": 2
},
"plan": {
"final_leg": {
"distance_m": 4114,
"drive_s": 443.6
},
"stops": [
{
"arrive": "2026-11-05T20:55:47Z",
"category": "petrol station",
"depart": "2026-11-05T21:00:47Z",
"distance_m": 5927,
"drive_s": 814.2,
"dwell_s": 300,
"errand": "petrol",
"hours": {
"message": "The map carries no usable opening hours for this place, so the plan does not know whether it is open when you would arrive.",
"source": "OpenStreetMap opening_hours tag, as held in this deployment's place index",
"tag": null,
"verdict": "unknown"
},
"kind": "category",
"place": {
"id": "osm:w608512509:poi",
"label": "Valero, Falmer Road, Brighton, Knepp Wildland, United Kingdom",
"lat": 50.83624867030552,
"lon": -0.07496869883366994,
"name": "Valero",
"off_route_m": 70,
"source": "index",
"type": "fuel"
}
},
{
"arrive": "2026-11-05T21:01:29Z",
"category": "pharmacy",
"depart": "2026-11-05T21:07:29Z",
"distance_m": 268,
"drive_s": 41.5,
"dwell_s": 360,
"errand": "prescription",
"hours": {
"message": "The map carries no usable opening hours for this place, so the plan does not know whether it is open when you would arrive.",
"source": "OpenStreetMap opening_hours tag, as held in this deployment's place index",
"tag": null,
"verdict": "unknown"
},
"kind": "category",
"place": {
"id": "osm:w1387527988:poi",
"label": "Well Pharmacy, Warren Way 13, BN2 6PH, Brighton, Knepp Wildland, United Kingdom",
"lat": 50.837354278564455,
"lon": -0.07435394078493118,
"name": "Well Pharmacy",
"off_route_m": 63.8,
"source": "index",
"type": "pharmacy"
}
}
],
"total_distance_m": 10309,
"total_drive_s": 1299.3,
"total_duration_s": 1959.3,
"total_dwell_s": 660
},
"search": {
"candidates_costed": 12,
"category_radius_m": 15000,
"corridor_max_detour_minutes": 12,
"note": "Candidates come from the corridor of the direct route. A place further off it than the corridor allows was never considered, so an empty or infeasible answer is a statement about this corridor and this index, never about what exists on the ground.",
"per_errand_cap": 6,
"sample_points": 9,
"total_cap": 24
},
"slack_s": 64806.9,
"usage": "Plan before setting off, or let a passenger do it. Stating a request like this is not something a driver can do at the wheel: handheld phone use while driving has carried six points in Great Britain since March 2022, and the offence covers operating an app.",
"verification": "Every stop is either a place held in this deployment's map index, with its id, or a coordinate you supplied. No place in this response was generated, and every time came from the routing engine."
}arrive_by more than 18 hours after departureapplication/problem+json · Problem
unauthorized); the body carries signup, docs and a one-call hint for a free keyapplication/problem+json · Problem
X-PAYMENT). Not problem+jsonapplication/json · X402PaymentRequired
scope-insufficient), a request from an origin the key does not allow (origin-not-allowed), or an account restriction (forbidden)application/problem+json · Problem
text/plain
application/problem+json · Problem
rate-limited, with Retry-After), or monthly quota exhausted with no payment path (quota-exceeded)application/problem+json · Problem
category errand on a deployment with no first-party place index (SN_GEOCODE_DIR unset)application/problem+json · Problem
application/problem+json · Problem
request-timeout)application/problem+json · Problem
Error bodies follow the shared problem model documented on API conventions.
Also under Routing
Generated from https://api.mapmap.ai/openapi.json on 2026-11-06 · operationId post_errand_plan