Skip to content
Developers

Walk15 Connect V1

API reference

Every partner-facing Walk15 Connect endpoint with parameters, examples and errors.

All paths are rooted at the issuer origin Walk15 gives you — https://api.walk15.lt in production. Examples use $WALK15_ISSUER for it. Curl's -u puts the client secret in your shell history, so use it only for local experiments.

Conventions

TransportHTTPS only
Browser endpointGET /oauth/authorize is opened in the user's browser. Never call it from your backend.
Backend endpointsEverything else is server-to-server.
Client authenticationHTTP Basic with client ID and secret — token, revoke and disconnect endpoints
User-delegated authenticationAuthorization: Bearer <access_token> — activity endpoints
Request bodiesapplication/x-www-form-urlencoded for OAuth endpoints; activity and disconnect requests have no body
DatesCalendar dates YYYY-MM-DD; instants are UTC ISO 8601 with milliseconds and Z
CachingEvery backend response is Cache-Control: no-store, Pragma: no-cache, Referrer-Policy: no-referrer
CorrelationResponses carry a server-generated X-Request-ID; include it when contacting Walk15 support

Two error shapes exist. OAuth endpoints (token, revoke, disconnect) return:

JSON
{ "error": "invalid_grant", "error_description": "The authorization grant is invalid." }

Activity endpoints return:

JSON
{
  "error": { "code": "invalid_date_range", "message": "The date range is invalid." },
  "request_id": "44444444-4444-4444-8444-444444444444"
}

All codes are listed in Errors and limits.

Discovery

GET/.well-known/oauth-authorization-serverBackend only

Returns RFC 8414 authorization server metadata. No authentication.

Shell
curl "$WALK15_ISSUER/.well-known/oauth-authorization-server"

Response 200 — see the full example in Discovery. Fields: issuer, authorization_endpoint, token_endpoint, revocation_endpoint, token_endpoint_auth_methods_supported, revocation_endpoint_auth_methods_supported, code_challenge_methods_supported, scopes_supported, response_types_supported, grant_types_supported.

Start authorization

GET/oauth/authorizeBrowser navigation

Starts the Walk15 sign-in and consent flow. Redirect the user's browser here (HTTP 303 from your backend); do not fetch it server-side. Authentication: none — your client is identified by client_id and validated against your registration.

Query parameterRequiredDescription
client_idYesYour client UUID
redirect_uriYesExact registered HTTPS callback, max 2048 bytes
response_typeYescode
scopeYesSpace-separated unique scopes from your allowance, max 512 characters
stateYes22–2048 visible ASCII characters; returned unchanged
code_challengeYes43-character unpadded base64url SHA-256 of your PKCE verifier
code_challenge_methodYesS256

Every parameter must appear exactly once; unknown parameters are rejected. The whole URL may not exceed 16 KB.

HTTP
GET /oauth/authorize?client_id=11111111-1111-4111-8111-111111111111
  &redirect_uri=https%3A%2F%2Fpartner.example%2Foauth%2Fwalk15%2Fcallback
  &response_type=code
  &scope=daily_steps%3Aread+cycling_daily%3Aread
  &state=EjzQZI751F_PEmxjmkfXDfqM8cL--2DKrXcjVV0jO3w
  &code_challenge=v8yANszyUApYb9P0q-VnGZQ5F5PTNrC06lSrGHkfJGw
  &code_challenge_method=S256
Host: api.walk15.lt

Result. Walk15 responds 303 to its own sign-in pages. After the user decides, the browser is redirected to your callback:

HTTP
HTTP/1.1 303 See Other
Location: https://partner.example/oauth/walk15/callback?state=EjzQZI751F_PEmxjmkfXDfqM8cL--2DKrXcjVV0jO3w&code=w15c_<authorization-code>

or, when the user cancels:

HTTP
HTTP/1.1 303 See Other
Location: https://partner.example/oauth/walk15/callback?state=EjzQZI751F_PEmxjmkfXDfqM8cL--2DKrXcjVV0jO3w&error=access_denied

Errors. An invalid request (invalid_request, unauthorized_client, unsupported_response_type, invalid_scope) or an expired request renders a generic Walk15 error page with status 400. It is never redirected to your callback.

Caveats. The flow must finish within 10 minutes. For a user who is already connected, see How do we reconnect a user?

Exchange code

POST/oauth/tokenBackend only

Redeems a single-use authorization code (valid for at most 120 seconds) for tokens. Authentication: HTTP Basic.

HeaderValue
AuthorizationBasic base64(urlencode(client_id) ":" urlencode(client_secret)) — exactly one
Content-Typeapplication/x-www-form-urlencoded (optionally ; charset=utf-8)
Form fieldRequiredDescription
grant_typeYesauthorization_code
codeYesThe code from your callback
redirect_uriYesThe same registered callback
code_verifierYesYour PKCE verifier, 43–128 characters of [A-Za-z0-9._~-]

No other fields are allowed. The body may not exceed 8 KB, and the request may not have a query string.

Shell
curl -X POST "$WALK15_ISSUER/oauth/token" \
  -u "$WALK15_CLIENT_ID:$WALK15_CLIENT_SECRET" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=$AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=https://partner.example/oauth/walk15/callback" \
  --data-urlencode "code_verifier=$CODE_VERIFIER"

Response 200

JSON
{
  "grant_id": "22222222-2222-4222-8222-222222222222",
  "history_from": "2026-10-10",
  "connection_version": "1",
  "access_token": "w15a_<access-token>",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_token": "w15r2_<refresh-token>",
  "scope": "daily_steps:read cycling_daily:read"
}
FieldDescription
grant_idPairwise UUID for this user and your company. Use it in activity paths. Stable across refreshes and reconnects.
history_fromEarliest date you may request: the user's authorization calendar day
connection_versionDecimal string; increases with every new authorization and every disconnect (revoking tokens alone does not change it)
access_tokenOpaque Bearer token
token_typeBearer
expires_inSeconds until the access token expires (at most 300)
refresh_tokenOpaque, single-use refresh token
scopeSpace-separated scopes approved by the user

Errors: 400 invalid_request (malformed body, unknown or duplicate fields), 400 unsupported_grant_type, 400 invalid_grant (unknown, expired or used code; wrong redirect_uri or verifier; connection changed), 401 invalid_client, 500 server_error.

Refresh tokens

POST/oauth/tokenBackend only

Rotates a refresh token. Authentication: HTTP Basic. Same headers and body rules as the code exchange.

Form fieldRequiredDescription
grant_typeYesrefresh_token
refresh_tokenYesThe current refresh token for the connection
Shell
curl -X POST "$WALK15_ISSUER/oauth/token" \
  -u "$WALK15_CLIENT_ID:$WALK15_CLIENT_SECRET" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=$WALK15_REFRESH_TOKEN"

Response 200 — the same fields as the code exchange, with a new access_token and a new refresh_token. grant_id, scope, history_from and connection_version are unchanged.

Errors: 400 invalid_request, 400 unsupported_grant_type, 400 invalid_grant (expired by inactivity, revoked, already used, user deleted, scope no longer allowed), 401 invalid_client, 500 server_error.

Revoke token

POST/oauth/revokeBackend only

RFC 7009 token revocation. Authentication: HTTP Basic.

Form fieldRequiredDescription
tokenYesAn access token or refresh token issued to you
token_type_hintNoaccess_token or refresh_token. Walk15 determines the real type itself.
Shell
curl -X POST "$WALK15_ISSUER/oauth/revoke" \
  -u "$WALK15_CLIENT_ID:$WALK15_CLIENT_SECRET" \
  --data-urlencode "token=$WALK15_REFRESH_TOKEN" \
  --data-urlencode "token_type_hint=refresh_token"

Response 200 with an empty body — also for unknown, foreign or already-revoked tokens, so the response never reveals whether a token existed.

  • An access token revokes only that token.
  • A refresh token revokes every token of that connection version and sends a connection.revoked webhook. The consent itself remains: re-authorizing with the same scopes keeps history_from.

To end a user's connection completely, use Disconnect grant instead.

Errors: 400 invalid_request, 400 unsupported_token_type (unknown hint), 401 invalid_client, 500 server_error.

Daily steps

GET/connect-walk15/v1/grants/{grant_id}/daily-activityBackend only

Returns adjusted Walk15 daily step totals for an inclusive date range. Authentication: Bearer access token. Scope: daily_steps:read.

ParameterInRequiredDescription
grant_idpathYesThe grant_id returned with the access token
fromqueryYesFirst date, YYYY-MM-DD, not before history_from (or the user's Walk15 registration day)
toqueryYesLast date, inclusive. At most 30 days after from (31 dates) and not later than tomorrow in UTC
Shell
curl "$WALK15_ISSUER/connect-walk15/v1/grants/22222222-2222-4222-8222-222222222222/daily-activity?from=2026-10-10&to=2026-10-12" \
  -H "Authorization: Bearer $WALK15_ACCESS_TOKEN"

Response 200

JSON
{
  "data": [
    { "date": "2026-10-10", "steps": 8412, "updated_at": null },
    { "date": "2026-10-12", "steps": 0, "updated_at": null }
  ],
  "meta": {
    "from": "2026-10-10",
    "to": "2026-10-12",
    "date_basis": "walk15_recorded_date",
    "source": "walk15",
    "snapshot_at": "2026-10-12T18:04:11.920Z"
  }
}
FieldDescription
data[].dateWalk15 recorded calendar date; ascending, at most one row per date
data[].stepsNon-negative integer: the recorded total plus any Walk15 support correction, never below zero
data[].updated_atAlways null in V1 — no reliable source-update time is exposed
meta.from, meta.toThe requested range
meta.date_basiswalk15_recorded_date
meta.sourcewalk15
meta.snapshot_atWhen Walk15 produced this response (UTC)

In the example, 11 October is absent: Walk15 has no step record for that date. Absence is not zero.

Errors: 400 invalid_request, 400 invalid_date_range, 401 invalid_access_token, 403 insufficient_scope, 404 not_found, 429 rate_limited, 503 data_unavailable.

Daily cycling

GET/connect-walk15/v1/grants/{grant_id}/cycling-dailyBackend only

Returns daily cycling distance per recorded start date. Authentication: Bearer access token. Scope: cycling_daily:read.

ParameterInRequiredDescription
grant_idpathYesThe grant_id returned with the access token
fromqueryYesFirst date, not before history_from
toqueryYesLast date, inclusive; at most 31 dates; not later than tomorrow in UTC
Shell
curl "$WALK15_ISSUER/connect-walk15/v1/grants/22222222-2222-4222-8222-222222222222/cycling-daily?from=2026-10-10&to=2026-10-16" \
  -H "Authorization: Bearer $WALK15_ACCESS_TOKEN"

Response 200

JSON
{
  "data": [
    { "date": "2026-10-11", "distance_meters": 18240, "source": "walk15", "updated_at": null },
    { "date": "2026-10-14", "distance_meters": 6105, "source": "walk15", "updated_at": null }
  ],
  "meta": {
    "from": "2026-10-10",
    "to": "2026-10-16",
    "date_basis": "walk15_recorded_date",
    "snapshot_at": "2026-10-16T08:30:00.000Z"
  }
}
FieldDescription
data[].dateStart date of the cycling activities that make up the total
data[].distance_metersNon-negative integer metres (not kilometres), including Walk15 support adjustments for that date. An adjustment alone never creates a date.
data[].sourcewalk15
data[].updated_atAlways null in V1
metafrom, to, date_basis (walk15_recorded_date), snapshot_at

Errors: 400 invalid_request, 400 invalid_date_range, 401 invalid_access_token, 403 insufficient_scope, 404 not_found, 429 rate_limited, 503 data_unavailable.

Cycling activities

GET/connect-walk15/v1/grants/{grant_id}/cycling-activitiesBackend only

Returns individual cycling activities whose recorded start date is in the range, oldest first, with cursor pagination. Authentication: Bearer access token. Scope: cycling_activities:read.

ParameterInRequiredDescription
grant_idpathYesThe grant_id returned with the access token
fromqueryYesFirst date, not before history_from
toqueryYesLast date, inclusive; at most 31 dates; not later than tomorrow in UTC
limitqueryNoPage size 1–500, plain decimal without leading zeros. Default 100
cursorqueryNometa.next_cursor from the previous page, unchanged
Shell
curl "$WALK15_ISSUER/connect-walk15/v1/grants/22222222-2222-4222-8222-222222222222/cycling-activities?from=2026-10-10&to=2026-10-16&limit=100" \
  -H "Authorization: Bearer $WALK15_ACCESS_TOKEN"

Response 200

JSON
{
  "data": [
    {
      "id": "ca_Q2xKw9mN3pR7sT1vX5yZ8aB4cD6eF0gH2iJ4kL6mN8o",
      "date": "2026-10-11",
      "distance_meters": 18240,
      "recorded_start_time": "2026-10-11T07:42:10.000000",
      "recorded_end_time": "2026-10-11T08:31:55.000000",
      "timezone": null,
      "manual": null,
      "granularity": "unknown",
      "source": "walk15",
      "updated_at": null
    }
  ],
  "meta": {
    "from": "2026-10-10",
    "to": "2026-10-16",
    "date_basis": "walk15_recorded_date",
    "time_basis": "walk15_recorded_clock",
    "snapshot_at": "2026-10-16T08:30:00.000Z",
    "next_cursor": "cc1_<opaque-cursor>"
  }
}
FieldDescription
idOpaque activity ID (ca_…), stable for this activity, user and your company
dateRecorded start date
distance_metersNon-negative integer metres for this activity (support adjustments are not allocated to activities)
recorded_start_timeUnzoned recorded clock YYYY-MM-DDTHH:mm:ss.ffffff — not UTC; never append Z
recorded_end_timeSame format, or null when unknown or invalid
timezone, manual, updated_atAlways null in V1
granularityAlways unknown
sourcewalk15
meta.time_basiswalk15_recorded_clock
meta.next_cursorCursor for the next page, or null on the last page

Pagination. Request the next page with the same from, to and limit plus cursor. Omitting limit and sending limit=100 are equivalent. A traversal expires 15 minutes after its first page, and a cursor stops working when the connection version changes; restart from the first page on 400 invalid_cursor. Results reflect live data, so rows changed during a traversal can be missed or repeated — upsert by id. See Following cursors.

Errors: 400 invalid_request (including a bad limit), 400 invalid_date_range, 400 invalid_cursor, 401 invalid_access_token, 403 insufficient_scope, 404 not_found, 429 rate_limited, 503 data_unavailable.

Disconnect grant

DELETE/connect-walk15/v1/customer/grants/{grant_id}Backend only

Ends a user's connection to your company: all codes and tokens of the connection stop working for new requests, a connection.revoked webhook is sent, and any future connection needs fresh consent and starts a new access window. Authentication: HTTP Basic. No query string and no body.

Shell
curl -X DELETE "$WALK15_ISSUER/connect-walk15/v1/customer/grants/22222222-2222-4222-8222-222222222222" \
  -u "$WALK15_CLIENT_ID:$WALK15_CLIENT_SECRET"

Response 204 with no body — for owned, already-revoked, unknown and other customers' grants alike, so it is safe to retry and reveals nothing about other companies.

Errors: 400 invalid_request (not a UUID, query string or body present), 401 invalid_client, 500 server_error.

Remove the grant from your system only after a 204. If the outcome is uncertain, stop using the connection's tokens and retry the request.

OpenAPI document

A machine-readable OpenAPI 3.1 document describes the backend HTTP endpoints above (discovery, token, revoke, activity reads and disconnect). The browser authorization flow is described on this page and in OAuth and PKCE rather than as an API operation you call.

Not part of the partner API

The Walk15-hosted sign-in, session and consent pages that the browser passes through after /oauth/authorize, and the endpoints the Walk15 app uses to list and disconnect a user's connected companies, are Walk15-owned surfaces. Do not call or automate them from your backend; follow the browser redirects instead.