mcpField
T
terra-routes

terra-routes

github.com/tryterraOther078 installsUpdated 2d ago
Repository

About terra-routes

Terra API Routes is a skill for creating reusable GPS route templates and pushing them to users’ connected Garmin, COROS, or Wahoo devices. It guides the two-step API workflow and highlights provider-specific limitations.

Key features

  1. 01Create and update route templates
  2. 02Push routes to connected devices
  3. 03Manage pushed routes
  4. 04Handle GPS waypoints and sport types
  5. 05Account setup via CLI or MCP admin tools

Use cases

  1. 01Send a planned course to a user's watch
  2. 02Update a route template and push it again
  3. 03Check whether a user has a supported device connected

MCP support

Terra API admin MCP tools can manage enabled providers and connected users. Route endpoint calls are available through the CLI, not MCP.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/tryterra/agent-skills --skill terra-routes

After installing, type/terra-routesin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

metadata.json
README.md
references/
provider-details.md

Details

From the project README

Terra API Routes

Terra API Routes is a write-to-device product (pre-release): define a GPS route once with waypoints, and Terra API pushes it to your users' connected devices for on-device navigation. The route appears on the watch or bike computer after the user's next device sync, so your app never has to speak each provider's native route format.

Only Garmin, COROS, and Wahoo are supported. Feature coverage differs sharply between them (see the provider matrix below), so design routes for the lowest common denominator unless you know every user is on Garmin.

Account tools: CLI or MCP

Routes is pre-release, so it has no account commands and its endpoints are not in the API description the CLI bundles: terra admin-api list --data-api will not show them. terra data-api sends them anyway, because the server, not the CLI, validates the path:

terra data-api /routes -X POST --body-file route.json
terra data-api /pushedRoutes -q user_id=<uuid>

Add -i for the status line, --jq to filter the response; a DELETE asks for confirmation unless you pass --yes.

The account configuration around Routes does have commands. terra unified-api sources list --env <dev-id> says which of the devices below are enabled, and terra users list --env <dev-id> --provider GARMIN says whether a user is connected to push a course to.

The route calls themselves are terra data-api, which only the CLI has; over MCP, the admin tools cover only enabled providers and connected users.

In a coding agent (Claude Code, Codex, Cursor and the like, on the user's machine or in the cloud), use the CLI, and offer to install it if it is missing: curl -fsSL https://cli.tryterra.co/install.sh | sh on macOS and Linux, irm "https://cli.tryterra.co/install.ps1" | iex in Windows PowerShell, or Homebrew or npm where the user already uses them. In a chat assistant (claude.ai, Claude Desktop, ChatGPT), use the Terra API admin MCP server, even when it can run code: its sandbox cannot install the CLI or sign in. Each admin command is a tool whose name and method spell it (terra users list is users_read with method: "list"). Only the CLI has terra data-api, terra admin-api and terra examples, and --select and --jq have no MCP equivalent. The terra-cli and terra-mcp skills carry the guardrails (confirming changes, keeping credentials out of transcripts), the errors, and a playbook per task. They administer the integration; they do not replace the API calls this skill describes.

Two-Phase Workflow

Routes always take two steps: create a reusable template, then push that template to one or more users.

Phase 1  POST /routes                          -> route_id           (create template once)
Phase 2  POST /routes/{route_id}/push?user_id=X -> pushed_route_id    (push to a user's device)
                                                    provider_route_id

The route reaches the device on the user's next sync. Base URL is https://access.tryterra.co/api/v2. Every request needs two headers: dev-id and x-api-key.

curl -X POST "https://access.tryterra.co/api/v2/routes" \
  -H "Content-Type: application/json" \
  -H "dev-id: YOUR_DEV_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "name": "Morning Run Loop",
    "sport": "running",
    "waypoints": [
      { "latitude": 51.5074, "longitude": -0.1278 },
      { "latitude": 51.5150, "longitude": -0.1200 }
    ]
  }'
# -> { "status": "success", "route_id": "295581149349019648" }

curl -X POST "https://access.tryterra.co/api/v2/routes/295581149349019648/push?user_id=USER_ID" \
  -H "dev-id: YOUR_DEV_ID" \
  -H "x-api-key: YOUR_API_KEY"
# -> { "status": "success", "pushed_route_id": "...", "provider_route_id": "..." }

Endpoints

MethodEndpointDescription
POST/routesCreate a route template
GET/routesList all templates
GET/routes/{id}Get one template
PUT/routes/{id}Update a template, partial update (200)
PUT/routes/{id}?cascadeUpdate and re-push to all devices, async (202)
DELETE/routes/{id}Delete a template
POST/routes/{id}/push?user_id=XPush route to a user's device
GET/pushedRoutes?user_id=XList a user's pushed routes
GET/pushedRoutes/{id}?user_id=XGet one pushed route
DELETE/pushedRoutes/{id}?user_id=XRemove a pushed route

Update semantics. PUT /routes/{id} behaves like a patch and returns 200: every field is optional and only the fields you provide are updated (waypoints, speed_meters_per_second, and elevation as well as name, description, and sport). It does not touch already-pushed devices. Add ?cascade to also re-push the updated route to every device it was sent to; this runs asynchronously and returns 202 Accepted. Cascade is fire-and-forget: re-push failures are only logged server-side, never surfaced in the response or in GET /pushedRoutes, so a partial failure is invisible to the API. To confirm a device actually received the update, re-push to that user explicitly or verify out of band on the device.

Data Model

RouteTemplate

FieldRequiredNotes
nameYesRoute name shown on device, non-empty
sportYesOne of the sport types below
waypointsYesArray of GPS points, minimum 2
descriptionNoGarmin only
elevation_gain_metersNoTotal gain in meters
elevation_loss_metersNoTotal loss in meters
speed_meters_per_secondNoAverage course speed target, Garmin only

Waypoint

FieldRequiredNotes
latitudeYesDecimal degrees, -90 to 90
longitudeYesDecimal degrees, -180 to 180
elevation_metersNoMetres above sea level
course_pointNoPOI marker at this waypoint, Garmin only

course_point is { "type": ..., "name": ... }. The API schema contains 15 types: generic, summit, valley, water, food, danger, first_aid, sprint, segment_start, segment_end, left, right, straight, left_fork, right_fork. Garmin support is documented for the first 10 (the POI types) only; do not rely on the five turn-direction types unless verified against current provider behavior (see Gotchas).

Sports (8): running, trail_running, hiking, cycling, road_biking, mountain_biking, gravel_cycling, other. cycling is distinct from road_biking but maps to ROAD_CYCLING on Garmin.

Validation (hard 400 on create and update): name non-empty, max 255 chars; description max 2,000 chars; sport valid; 2 to 10,000 waypoints; latitude in -90..90; longitude in -180..180; elevation_meters in -500..9000; speed_meters_per_second > 0.

Provider Matrix

CapabilityGarminCOROSWahoo
Wire formatJSONGPXbase64 FIT
PushYesYesYes
Re-syncYesYes (re-POST new GPX)Yes
DeleteYesNoYes
Course points (POIs)YesNoNo
Speed targetYesNoNo
DescriptionYesNoNo

All three support re-sync. COROS has no in-place update, so a re-sync re-POSTs a fresh GPX file while the provider_route_id stays stable. Delete works on Garmin and Wahoo only.

Gotchas

  • Garmin-only fields fail silently elsewhere. course_point, speed_meters_per_second, and description are honored on Garmin but silently ignored by COROS and Wahoo. Do not depend on them unless every target user is on Garmin.
  • Garmin elevation is all-or-nothing per route. If some waypoints carry elevation_meters and others do not, Terra API drops all elevation values and lets Garmin's own elevation model fill them in. Supply elevation on every waypoint or none.
  • Course-point failures happen at different stages. An invalid type string fails at POST /routes with 400 (creation-time enum validation). The five turn-direction types (left, right, straight, left_fork, right_fork) pass creation-time validation but are not among the 10 documented Garmin course-point types – how the push handles them is not guaranteed (it may error or drop them), so stick to the 10 Garmin-supported POI types unless you have verified current behavior.
  • COROS is the most limited provider. No delete (a delete only removes the record from Terra API's database; the route stays on the device), no retrieve, and it collapses all sports into two internal types (run and cycle), so the device display may not distinguish, say, gravel cycling from road biking.
  • Pushing the same template repeatedly creates duplicate pushed routes. Each push is a new pushed route; there is no dedup. Track pushed_route_id values yourself if you need to avoid duplicates on a device.
  • Elevation is auto-computed when absent, and the exact handling is provider-dependent.
  • Unsupported operations still return success. A delete against COROS, for example, returns a success response even though the route remains on the device.

References

  • references/provider-details.md – per-provider behavior: Garmin PUT-then-POST re-sync and elevation normalization, Wahoo stable terra-{pushed_route_id} external ID and granular sport mapping, COROS unique-GPX-per-push and provider_route_id meaning. Read when a route behaves differently across devices or you are debugging a re-sync or delete.

For full payload examples beyond the one above (trail running with course points, road biking with a speed target, the minimal 2-waypoint route), fetch the live page: https://docs.tryterra.co/routes-api-pre-release/sport-specific-examples.md

Full docs: Routes API overview, introduction, core concepts, provider compatibility. Ask terra docs ask (or docs_ask over MCP) before fetching a page: it answers from the docs and cites its sources. Append .md to any docs URL for markdown.

Note: the Routes docs space is pre-release and may not be published yet. If a page above returns "Page Not Found", rely on this skill's bundled references and verify against the live API.

Comments

Markdown supported