Overview
Trek Point acts as an OAuth 2.0 authorization server. Send the user
to the authorize URL, receive an authorization code on your
redirect_uri, then exchange that code for an access token
and, in some cases, a refresh token. Use the access token as a
Bearer token when calling APIs for the granted scopes.
Register an application
- Sign in to Trek Point.
- Open the Developer portal and create an application.
- Add every redirect URI your app will use. Redirect URIs must match exactly. Use HTTPS in production.
- Choose public for native or browser-based apps with PKCE, or confidential for server-side apps with a client secret.
- Copy the
client_idand, for confidential clients, theclient_secret. Secrets are only shown once.
Endpoints
- Authorization
-
GET
/oauth/authorize - Token
-
POST
/oauth/token - Revocation
-
POST
/oauth/revoke - Example resource (
profilescope) -
GET
/oauth/profile— sendAuthorization: Bearer …
Authorization code flow
- Generate a PKCE
code_verifierandcode_challenge. This is recommended for all clients and expected for public clients. - Redirect the user’s browser to
/oauth/authorizewith the query parameters below. - After consent, we redirect to your
redirect_uriwithcodeandstate. POSTto/oauth/tokenwithgrant_type=authorization_code, thecode, the sameredirect_uri, and either client authentication or the PKCEcode_verifier.- Store tokens securely and call APIs with
Authorization: Bearer <access_token>.
Token request
POST /oauth/token with
Content-Type: application/x-www-form-urlencoded.
Exchange authorization code
grant_type=authorization_codecode— the value returned to your redirect URIredirect_uri— the same URI used in the authorize step- Confidential clients: authenticate with HTTP Basic (
client_id:client_secret) or includeclient_idandclient_secretin the body. - Public clients: include
client_idandcode_verifier.
A successful response includes JSON with access_token, token_type
(typically Bearer), expires_in,
and may include refresh_token.
Refresh token
POST /oauth/token with
grant_type=refresh_token, refresh_token, and
scope if you want to narrow the request. The requested scope must not
exceed the original grant. Confidential clients authenticate the same way as above.
A successful refresh returns a new access token. If the response includes a new
refresh_token, store it and use it for the next refresh.
Revocation
POST /oauth/revoke to invalidate a token when a user signs out
of your app or disconnects it. Send the token value and, where supported,
token_type_hint. Confidential clients authenticate the same way as at
the token endpoint.
Profile API
GET /oauth/profile returns JSON when you send
Authorization: Bearer <access_token> and the token includes the
profile scope:
{
"id": <user id>,
"email": "<email>",
"username": "<username>"
}
Public API
APIs use the prefix /api/v1
and require an OAuth access token with the right scopes. Read endpoints are
owner-scoped and only return resources owned by the token’s user.
- My activities
-
GET
/api/v1/activities— scopeactivities_read - Activity detail (includes geometry)
-
GET
/api/v1/activities/<id>— scopeactivities_read - Create activity
-
POST
/api/v1/activities— scopeactivities_write - Update activity (owner only)
-
PATCH
/api/v1/activities/<id>— scopeactivities_write - Delete activity (owner only)
-
DELETE
/api/v1/activities/<id>— scopeactivities_write - My routes
-
GET
/api/v1/routes— scoperoutes_read - Route detail (includes geometry)
-
GET
/api/v1/routes/<id>— scoperoutes_read - Create route
-
POST
/api/v1/routes— scoperoutes_write - Update route (owner only)
-
PATCH
/api/v1/routes/<id>— scoperoutes_write - Delete route (owner only)
-
DELETE
/api/v1/routes/<id>— scoperoutes_write
Read and write operations are tied to the OAuth token owner. Apps can only access and modify activities and routes created by that user account.
File format exports
You can export routes and activities by passing a format query parameter
to the export endpoints.
These export endpoints are served from the Maps API host, not the OAuth
/oauth prefix:
GET /api/routes/<id>/export?format=... and
GET /api/activities/<id>/export?format=....
Route export formats
format=gpx— GPX export.format=geojson— GeoJSON export.format=fit— FIT export.
GET /api/routes/455/export?format=gpx
GET /api/routes/455/export?format=geojson
GET /api/routes/455/export?format=fit
Activity export formats
format=gpx— generated GPX export.format=fit— generated FIT export.format=original— original uploaded track file (owner only).
GET /api/activities/1201/export?format=gpx
GET /api/activities/1201/export?format=fit
GET /api/activities/1201/export?format=original
For shared or public items, include the share key k when required:
/api/routes/<id>/export?format=gpx&k=<share_key>.
Invalid values return {"error":"invalid_format"}.
Response objects
JSON responses from OAuth and public API endpoints use the objects below.
Arrays are shown with []. Optional fields may be omitted or returned
as null depending on the data available.
OAuth token response
{
"access_token": "<opaque token>",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "<opaque token>"
}
access_token— credential sent asAuthorization: Bearer <token>.token_type— currentlyBearer.expires_in— access token lifetime in seconds.refresh_token— token used to obtain new access tokens.
Profile object
{
"id": 42,
"email": "[email protected]",
"username": "trailrunner"
}
id— internal user ID.email— user email address.username— public username.
Activity summary object
{
"id": 1201,
"name": "Sunday long run",
"activity_type": "walking",
"status": "ready",
"activity_started_at": "2026-03-29T06:30:00+00:00",
"created_on": "2026-03-29T07:02:12+00:00",
"distance_m": 12984.3,
"duration_s": 4210.0,
"tags": ["long", "easy"]
}
id,name— activity ID and title.activity_type— activity type label.status— processing state such asready.activity_started_at,created_on— ISO 8601 timestamps when available.distance_m,duration_s— distance in meters and duration in seconds.tags— string labels attached to the activity.
GET /api/v1/activities returns:
{
"activities": [ActivitySummary, ...]
}
Activity detail object
{
"name": "Sunday long run",
"route_line": {
"type": "LineString",
"coordinates": [[18.4241, -33.9249], [18.4250, -33.9258]]
},
"elevation": {
"range_height": [[0, 14.2], [500, 24.1]]
},
"way_types": {
"total_m": 1200,
"segments": [{"start_m": 0, "end_m": 400, "key": "footway", "label": "Footway"}]
}
}
route_line.coordinates— coordinate pairs in[lng, lat]order.elevation.range_height—[distance_m, elevation_m]points.way_types.segments— per-surface or path segments by distance range.
Route summary object
{
"id": 455,
"name": "Table Mountain Loop",
"costing_mode": "hiking",
"created_on": "2026-03-27T10:12:03+00:00",
"tags": ["weekend", "trail"],
"start_place": {
"city": "Cape Town",
"region": "WC",
"country": "ZA"
}
}
costing_mode— route mode used for routing.start_place— reverse-geocoded start context, when available.
GET /api/v1/routes returns:
{
"routes": [RouteSummary, ...]
}
Route detail object
{
"id": 455,
"name": "Table Mountain Loop",
"costing_mode": "hiking",
"tags": ["weekend", "trail"],
"description": "Steady climb with ocean views.",
"start_place": {"city": "Cape Town", "region": "WC", "country": "ZA"},
"waypoints": [[18.4241, -33.9249], [18.4320, -33.9320]],
"route_line": {"type": "LineString", "coordinates": [[18.4241, -33.9249], [18.4320, -33.9320]]},
"elevation": {"range_height": [[0, 10.1], [1500, 320.7]]},
"way_types": {"total_m": 1500, "segments": [{"start_m": 0, "end_m": 500, "key": "path", "label": "Path"}]},
"is_public": true
}
waypoints— user-defined route control points in[lng, lat].is_public— whether the route is publicly visible.
Error object
{
"error": "not_found",
"detail": {...}
}
error— machine-readable key such asmissing_authorization,not_found, oractivity_not_ready.detail— optional endpoint-specific context.
Scopes
The server currently advertises the following scopes. Your client registration may limit which of them it can request:
- profile
- activities_read
- activities_write
- routes_read
- routes_write
Client types & PKCE
Confidential clients have a client secret and run on your server. Never embed the secret in mobile or browser apps.
Public clients have no secret. Use PKCE on the authorization and token
steps so intercepted authorization codes cannot be exchanged without the original
code_verifier.