# update_route

Edit a route.

`PATCH /v1/routes/{routeId}` · MCP tool `update_route` · scope `write` · stable · since 2026-09-17

Only sent fields change. Strings clear on empty string, numeric stats and objects clear on null, arrays are full replacements, track null removes the line.

## Path parameters

- `routeId` (string, required): Id of the route (from list_routes).

## Body parameters

- `activity` (string, optional): hike, walk, bike, drive, ski or paddle. A route without one is a hike; empty string clears.
- `ascentMin` (number or null, optional): Minutes up, when the source gives up and down separately. null clears.
- `descentM` (number or null, optional): Descent in meters, 0 or more. Never derived from gainM: give it when the source does. null clears.
- `descentMin` (number or null, optional): Minutes down. null clears.
- `description` (string, optional): The route in the creator's words, shown on the route card. Empty string clears.
- `distanceKm` (number or null, optional): Distance in kilometers, 0 or more. null clears.
- `durationBasis` (string or null, optional): What durationMin counts: round_trip (the default), one_way or ascent. null clears. One of `round_trip`, `one_way`, `ascent`.
- `durationMin` (number or null, optional): Duration in minutes, 0 or more; durationBasis says what it counts (round trip unless set). null clears.
- `effortLabel` (string, optional): Easy, Moderate, Hard (free text). Empty string clears.
- `equipment` (array of strings or null, optional): Gear, full replacement, at most 50 items; [] means explicitly no special gear (shown as None), null clears back to unknown.
- `extraProps` (array of ExtraProp, optional): Free label and value rows shown on the card, full replacement ([] clears), at most 24: [{ label (at most 60 characters), value (at most 500) }]. A row with both sides empty is dropped, a half-empty one refused.
  Fields of ExtraProp: https://developers.sceniq.earth/fields/extra-prop.md
- `factsCheckedOn` (string, optional): The day the facts were last checked against their sources (YYYY-MM-DD). Empty string clears.
- `figureSources` (array of FigureSource, optional): Where each number comes from, full replacement: [{ field: durationMin|distanceKm|gainM|descentM|sacGrade, kind: sourced|computed, url?, note? }].
  Fields of FigureSource: https://developers.sceniq.earth/fields/figure-source.md
- `gainM` (number or null, optional): Elevation gain in meters, 0 or more. null clears.
- `gradeScale` (string, optional): sac, via_ferrata, cai, yds, mtb, whitewater or other. A route without one uses sac; empty string clears.
- `name` (string, optional): Route name, e.g. Fairy Falls trail or Oeschinensee loop. Without one the apps show Hike from <start> (Drive from, Bike ride from ... by activity), or Route N when there is no start either. Empty string clears.
- `reviewBy` (string, optional): The day the facts need a new check (YYYY-MM-DD), e.g. when a season, fare or timetable runs out. get_guide_readiness and get_publish_status warn once it has passed. Empty string clears.
- `reviewNotes` (string, optional): Notes for the creator's review (why a value was chosen, what could not be verified). Never shown to buyers; get_guide_readiness lists them. Empty string clears.
- `sacGrade` (string, optional): The grade as written in gradeScale, e.g. T2 (SAC), EE (CAI), Class 3 (US), S2 (mountain bike). Empty string clears.
- `season` (RouteSeason or null, optional): When the route can be walked, null clears: { months? [1-12], closureNote? (stairs chained November to April), statusUrl? }. Buyers opening it out of season see the closure.
  Fields of RouteSeason: https://developers.sceniq.earth/fields/route-season.md
- `segments` (array of RouteSegment, optional): Sections with their own access (free to Scout Lookout, permit for the chains): [{ name, km?, accessNote? }].
  Fields of RouteSegment: https://developers.sceniq.earth/fields/route-segment.md
- `shape` (string or null, optional): loop, out_and_back or one_way. null clears. One of `loop`, `out_and_back`, `one_way`.
- `sourceGrade` (string, optional): The source's own grade (Easy on the park page) next to your effortLabel. Empty string clears.
- `sources` (array of strings, optional): Where the facts come from, full replacement ([] clears), at most 25 entries: links to official trail pages or plain text (the creator's GPX). Buyers see them under Sources, links by their host.
- `stages` (array of RouteStage, optional): Days or legs of a trek, full replacement: [{ name, km?, min?, gainM?, note? }].
  Fields of RouteStage: https://developers.sceniq.earth/fields/route-stage.md
- `start` (string, optional): Trailhead or starting point in words. Empty string clears.
- `startMapsUrl` (string, optional): Map link to the trailhead: https on Google Maps, Apple Maps, Swisstopo, OpenStreetMap or SchweizMobil, or a geo: URI. Empty string clears.
- `status` (StatusNotice or null, optional): Closure or works notice, null clears: { state: open|partly_closed|closed|reopening, note?, since?, until? (the day the state is expected to end; buyers stop seeing the notice after it), sourceUrl?, checkedOn? }. reopening with until means reopens on that day.
  Fields of StatusNotice: https://developers.sceniq.earth/fields/status-notice.md
- `track` (RouteTrack or null, optional): The route line (see create_route), replaced whole; null removes it.
  Fields of RouteTrack: https://developers.sceniq.earth/fields/route-track.md
- `transit` (array of TransitNote, optional): Transit notes shown under Transit on the route card, full replacement ([] clears), at most 20: [{ mode: train|bus|cable_car|car|boat|ferry|plane, note }]. Rows with an empty note are dropped.
  Fields of TransitNote: https://developers.sceniq.earth/fields/transit-note.md
- `variants` (array of RouteVariant, optional): Alternatives, full replacement: [{ name, start?, extraKm?, extraMin?, purpose? }].
  Fields of RouteVariant: https://developers.sceniq.earth/fields/route-variant.md

## Returns

200.

- `ok` (boolean, required): Always true: the write went through. Always `true`.
- `routeId` (string, required): The route's id.
- `warnings` (array of strings, optional): Notes about things the write accepted but you should look at (a plain http link). Present only when there are any.

## Examples

### Post a closure notice with the day the spur reopens

```bash
curl -X PATCH "https://sceniq.earth/api/v1/routes/kx7f2h8j4k0k6z2x8c4v0b6n2m8q4w0e" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": {
    "state": "reopening",
    "note": "The overlook spur and platform are closed for repairs. The trail to Fairy Falls stays open.",
    "since": "2026-09-28",
    "until": "2026-10-16",
    "sourceUrl": "https://www.nps.gov/yell/planyourvisit/conditions.htm",
    "checkedOn": "2026-09-29"
  },
  "reviewBy": "2026-10-17"
}'
```

MCP `tools/call`:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "update_route",
    "arguments": {
      "routeId": "kx7f2h8j4k0k6z2x8c4v0b6n2m8q4w0e",
      "status": {
        "state": "reopening",
        "note": "The overlook spur and platform are closed for repairs. The trail to Fairy Falls stays open.",
        "since": "2026-09-28",
        "until": "2026-10-16",
        "sourceUrl": "https://www.nps.gov/yell/planyourvisit/conditions.htm",
        "checkedOn": "2026-09-29"
      },
      "reviewBy": "2026-10-17"
    }
  }
}
```

Response 200:

```json
{
  "ok": true,
  "routeId": "kx7f2h8j4k0k6z2x8c4v0b6n2m8q4w0e"
}
```

### Remove the line and the figures computed from it (null clears)

```bash
curl -X PATCH "https://sceniq.earth/api/v1/routes/kx7f2h8j4k0k6z2x8c4v0b6n2m8q4w0e" \
  -H "Authorization: Bearer $SCENIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "track": null,
  "gainM": null,
  "descentM": null,
  "figureSources": [
    {
      "field": "distanceKm",
      "kind": "sourced",
      "url": "https://www.nps.gov/places/000/grand-prismatic-spring.htm"
    }
  ],
  "reviewNotes": "The GPX was from another trail; gain removed until a new recording."
}'
```

MCP `tools/call`:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "update_route",
    "arguments": {
      "routeId": "kx7f2h8j4k0k6z2x8c4v0b6n2m8q4w0e",
      "track": null,
      "gainM": null,
      "descentM": null,
      "figureSources": [
        {
          "field": "distanceKm",
          "kind": "sourced",
          "url": "https://www.nps.gov/places/000/grand-prismatic-spring.htm"
        }
      ],
      "reviewNotes": "The GPX was from another trail; gain removed until a new recording."
    }
  }
}
```

Response 200:

```json
{
  "ok": true,
  "routeId": "kx7f2h8j4k0k6z2x8c4v0b6n2m8q4w0e"
}
```

## Errors

- `invalid_argument` (400): A required field is missing, a field is unknown, or a value does not match its type (the Convex validator's path and value are in the message).
- `invalid_request` (400): A writer refused a value: a map link on a host that is not allowed, a title that is too long, a date that does not exist, too many items in a list. The message is the writer's own sentence.
- `unauthenticated` (401): The request carries no key. Send it as Authorization: Bearer sk_sceniq_... (or X-Api-Key).
- `invalid_api_key` (401): The key is unknown, revoked or expired.
- `api_scope_required` (403): A read-only key called a write operation, or a key without the publish option called publish_changes.
- `forbidden` (403): The key belongs to a creator account that is no longer active.
- `not_found` (404): The id does not exist or belongs to another creator. The two answers are identical on purpose, so ids never reveal what exists.
- `payload_too_large` (413): A JSON body over 2 MB, an image over 6 MB, a batch upload over 19 MB, or an MCP message over 2 MB.
- `rate_limited` (429): Over a limit: requests per key per minute, per creator per hour, uploads per key per minute, or check_links per guide per hour.
- `internal_error` (500): An unexpected failure. The message is hidden on purpose.

Reference: https://developers.sceniq.earth/reference/update_route
