> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyclemate.club/llms.txt
> Use this file to discover all available pages before exploring further.

# Update a route

> Edit a route you host: its details, its line, or its cover image

## Overview

Edits a route and answers it back. Every field is optional and an omitted field is unchanged; send `null` to clear `start_time`, `end_time`, `max_rsvps` or `theme`. Hosts only: the creator or a co-host.

### Authentication

A [Login with Cyclemate](https://docs.cyclemate.club/api-reference/oauth.md) access token, or a signed-in rider's session token. Every route belongs to a rider: your app acts for the rider who connected it, and the route is theirs. Your app reaches only the routes it created: a route the rider made in our apps, or through another app, answers `404`.

```
Authorization: Bearer ACCESS_TOKEN
```

An API key on its own is rejected, as are anonymous requests. To create routes as your organisation, sign up for a Cyclemate account and connect your app to it with Login with Cyclemate.

### Rate limits

Riders and apps acting for them may make **60 requests a minute**; a request that routes between `stops` counts once per hop. Every rider may create **10 routes in any 24 hours**, counted across our apps and every connected app: the eleventh answers `429` with `code: "daily_limit"` and the seconds until a slot frees.

## Path parameters

**id** `string` *required*

The route's id.

## Request body

> **Optional parameters**
>
> **name** `string`
>
> 1 to 120 characters.
>
> **description** `string`
>
> Up to 4,000 characters.
>
> **emoji** `string` (default: 🚴)
>
> One emoji.
>
> **alias** `string`
>
> The link slug: 3 to 60 lowercase letters, digits and hyphens. Route and product names are reserved. Generated from the name when omitted. A taken alias answers `409`.
>
> **privacy** `string` (default: public)
>
> `public`, `unlisted` (anyone with the link) or `private` (hosts only).
>
> **route\_mode** `string` (default: dynamic)
>
> `dynamic` or `fixed`. A dynamic route is re-planned between its stops as roads change, so it has no hidden shaping points; `geometry` and a hidden leg require `fixed`.
>
> **theme** `object`
>
> The page theme. Keys: `headerFont`, `headerFlair`, `headerColor` (`#rrggbb`), `headerLayout`, `backgroundColor`, `backgroundTexture`, `animation`, `stickers` (up to 12 of `{ id, position: { x, y } }`). Every value must be one the app offers; an unknown one answers `400` naming it.
>
> **categories** `string[]`
>
> Up to 10 category slugs, for example `coffee`, `park`, `live-music`.
>
> **start\_time** `string`
>
> ISO 8601. Setting it makes the route a group ride. `end_time` (after it; derived from the route's duration when omitted), `max_rsvps` (0 to 1000) and `rsvp_questions` are accepted only with a start time.
>
> **rsvp\_questions** `array`
>
> Up to 20 of `{ prompt, required, type?, options? }`; `type` is `text` or `choice`, and a choice question lists `options` as `{ label, limit? }`. Ids are generated when omitted and must be sent back unchanged on edit.
>
> **show\_elevation** `boolean` (default: true)
>
> With `show_safety_colors`, `show_why_this_route` and `show_street_by_street`: which sections the route's page shows.
>
> **stops** `array`
>
> 2 to 25 stops as `{ location: [longitude, latitude], name? }`, all in one city. We plan the safest cycling route between them, one leg per hop, and name each stop as given.
>
> **legs** `array`
>
> Your own line, 1 to 24 legs of `{ geometry, end_name?, hidden? }`. `geometry` is a GeoJSON LineString; each leg starts within 25 m of the previous leg's end. The whole route is at most 10,000 coordinates and 200 km and must start and end in the same city. `hidden: true` marks a shaping point that is not shown as a stop (fixed routes only). Pass `start_name` for the start.
>
> **geometry** `object`
>
> A single GeoJSON LineString, the same as one leg. Fixed routes only. `start_name` and `end_name` name its ends.
>
> **start\_name** `string`
>
> With `legs` or `geometry`.
>
> **end\_name** `string`
>
> With `geometry`.
>
> **cover\_image\_id** `string`
>
> The id of one of the route's images, to make it the cover.

## Response body

**id** `string`

The route's id, a UUID string. Pass it as `event_id` to [`POST /multi-modal/v2/`](https://docs.cyclemate.club/api-reference/routing/multimodal) to ride it.

**alias** `string`

The route's link slug, chosen or generated from the name.

**url** `string`

The route's page on cyclemate.club.

**name** `string`

**description** `string`

**emoji** `string`

**privacy** `string`

`public`, `unlisted` or `private`.

**route\_mode** `string`

`dynamic` (we re-plan the line between the stops as roads change) or `fixed` (the line is kept as given).

**theme** `object`

The page theme, when one is set. The same object you send.

**categories** `array`

Each `{ slug, name, emoji }`.

**start\_time** `string`

Present on a group ride, with `end_time`, and `max_rsvps` and `rsvp_questions` when set.

**show\_elevation** `boolean`

With `show_safety_colors`, `show_why_this_route` and `show_street_by_street`: which sections the page shows.

**city** `string`

The city the route is in: `london`, `new_york_city`, `los_angeles` or `bay_area`.

**host** `object`

The rider who created the route: `{ name, slug, image_url }`. `null` once that rider has deleted their account.

**route** `object`

`distance_m`, `duration_s`, `polyline` (precision 5), `geometry` (a GeoJSON LineString; omitted in lists) and `stops`: the start then every stop in order, each `{ name, location }`, with `hidden: true` on a shaping point that is not shown as a stop.

**route\_status** `string`

`pending` while we match the line to the road network (a fixed route) or plan it between the stops (a dynamic route), `ready` once turn-by-turn steps and the safety summary are stored, `unavailable` when the line could not be matched or no route exists between the stops. A pending route is already viewable and rideable; poll every few seconds.

**images** `array`

Each `{ id, url, status, cover }`. `status` is `pending` until our image check has run, then `approved`. Hosts see every image with its status; everyone else sees approved images only.

**created\_at** `string`

**updated\_at** `string`

```bash cURL theme={null}
curl -X PATCH 'https://api.cyclemate.club/custom-routes/78360cf7-0f4a-48f1-91ca-25e83db44aff/' \
  -H 'Authorization: Bearer ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{ "privacy": "unlisted", "theme": { "backgroundColor": "sky", "headerFont": "playfair" } }'
```

```json Response theme={null}
{
  "id": "78360cf7-0f4a-48f1-91ca-25e83db44aff",
  "alias": "culver-city-coffee-loop-1792536692813",
  "url": "https://cyclemate.club/explore/los-angeles/routes/culver-city-coffee-loop-1792536692813",
  "name": "Culver City coffee loop",
  "description": "An easy Sunday loop with a stop at the bakery.",
  "emoji": "☕",
  "privacy": "unlisted",
  "route_mode": "dynamic",
  "theme": { "backgroundColor": "sky", "headerFont": "playfair" },
  "categories": [{ "slug": "coffee", "name": "Coffee", "emoji": "☕" }],
  "show_elevation": true,
  "show_safety_colors": true,
  "show_why_this_route": true,
  "show_street_by_street": true,
  "city": "los_angeles",
  "host": { "name": "Tom", "slug": "tom", "image_url": null },
  "route": {
    "distance_m": 833.5,
    "duration_s": 200,
    "polyline": "ycunEbsoqUMu@EU??M?YH{Er…",
    "geometry": { "type": "LineString", "coordinates": [[-118.377621, 34.028292], [-118.37735, 34.028364], "…"] },
    "stops": [
      { "name": "Home", "location": [-118.377621, 34.028292] },
      { "name": "Bakery", "location": [-118.378755, 34.027277] },
      { "name": "Park", "location": [-118.37675, 34.02727] }
    ]
  },
  "route_status": "ready",
  "images": [],
  "created_at": "2026-10-20T22:51:32.816+00:00",
  "updated_at": "2026-10-20T22:51:32.816+00:00"
}
```

## Variants

### Replacing the line

Send `stops`, `legs` or `geometry` to replace the whole route. Its plan is dropped and rebuilt, so `route_status` returns to `pending`.

### Changing the mode

Switching a fixed route to `dynamic` keeps its stops but drops any hidden shaping points, since a dynamic route is planned through its stops only. Switching to `fixed` keeps the current line.

### Making a route a group ride

Send `start_time` to turn a route into a group ride; send `start_time: null` to turn a group ride back into a route, which also clears `max_rsvps` and `rsvp_questions`.

## Error responses

Every error body is `{ "error", "code" }`; `error` is a sentence, `code` is stable.

### 400 Bad request

Invalid body or query: the message names the field. `code` is `invalid_body`, or for a route input `outside_coverage` (the route starts outside the cities we cover), `cross_city` (its stops span two cities) or `no_route` (no cycling route exists between two stops).

```json theme={null}
{ "error": "unknown field: colour", "code": "invalid_body" }
```

### 401 Unauthorized

Missing or invalid credentials, or an anonymous caller.

```json theme={null}
{ "error": "Missing user credentials" }
```

### 404 Not found

No route with that id that you may see. A private route you do not host, a route you may read but not edit, and a route your app did not create all answer `404`, never `403`.

```json theme={null}
{ "error": "Custom route not found", "code": "not_found" }
```

### 405 Method not allowed

```json theme={null}
{ "error": "Method not allowed", "code": "method_not_allowed" }
```

### 409 Conflict

The alias you chose is already taken, by another route or by a rider's profile link.

```json theme={null}
{ "error": "That alias is already taken", "code": "alias_taken" }
```

### 429 Too many requests

The burst limit, with a `Retry-After` header; or the daily creation cap, which adds `code: "daily_limit"`.

```json theme={null}
{ "error": "Daily limit of 10 custom routes reached", "code": "daily_limit", "retry_after_seconds": 3600 }
```

### 500 Internal server error

```json theme={null}
{ "error": "Internal server error", "code": "internal_error" }
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.