---
title: "Plans a chain of errands against a hard arrival time."
method: POST
path: "/v1/errands/plan"
tags: ["routing"]
---

# Plans a chain of errands against a hard arrival time.

`POST /v1/errands/plan`

## Request

- Base URL: `https://api.mapmap.ai`
- URL: `https://api.mapmap.ai/v1/errands/plan`
- Auth: one of:
  - HTTP bearer
  - API key in query parameter `api_key`

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.

## Request body

- ErrandPlanRequest: `POST /v1/errands/plan` request body.
  - `arrive_by` string, required: The hard arrival time, RFC 3339 with an offset.
  - `costing_options` unknown
  - `depart_at` string, nullable: When they set off, RFC 3339. Defaults to now, in the offset `arrive_by` carries.
  - `destination` Destination, required: The destination, with the deadline attached to it.
    - `lat` number, double, required: Latitude, WGS84.
    - `lon` number, double, required: Longitude, WGS84.
    - `name` string, nullable: What to call it in the answer. "The school" is a place the driver knows and the map may not, so it is a coordinate plus a label, never a name we go looking for.
  - `errands` ErrandInput[], required: The errands, in any order: the answer decides the order.
    - `category` string, nullable: The kind of place wanted: an index category token, or a colloquial phrase the shared normaliser understands.
    - `dwell_minutes` number, double, nullable: Time out of the car here, minutes. Default [`DEFAULT_DWELL_MINUTES`].
    - `id` string, nullable: What the caller calls this errand, echoed throughout the answer. Defaults to the category, or to `stop N`.
    - `place` Destination: The destination, with the deadline attached to it.
      - `lat` number, double, required: Latitude, WGS84.
      - `lon` number, double, required: Longitude, WGS84.
      - `name` string, nullable: What to call it in the answer. "The school" is a place the driver knows and the map may not, so it is a coordinate plus a label, never a name we go looking for.
  - `max_detour_minutes` number, double, nullable: How far off the direct route a candidate may sit, as a detour in minutes. Default [`DEFAULT_MAX_DETOUR_MINUTES`].
  - `origin` Point, required: A point in the request.
    - `lat` number, double, required: Latitude, WGS84.
    - `lon` number, double, required: Longitude, WGS84.

## Response `200`

The plan, feasible or honestly not: `{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

- object

## Other responses

- `400`: Invalid request, including an instant more than 366 days from now and an `arrive_by` more than 18 hours after departure
- `401`: Missing or invalid API key
- `422`: The chain is too large or too spread out for the engine's matrix limits
- `429`: Quota or rate limit exceeded
- `501`: A `category` errand on a deployment with no first-party place index (SN_GEOCODE_DIR unset)
- `502`: Upstream routing engine failed

---

[API](https://skmtc.dev/mapmap/apis/sn-gateway.md) · [All operations](https://skmtc.dev/mapmap/apis/sn-gateway/llms.txt) · [OpenAPI document](https://skmtc.dev/mapmap/apis/sn-gateway/revisions/2293be8b4c38?raw)
