Partner API
Integrate your application with Commit Swimming to access workout data on behalf of coaches and teams.
Overview
The Commit Swimming Partner API uses OAuth 2.0 Authorization Code flow. Your users authenticate with their Commit Swimming account, select which teams to share, and your app receives a Bearer token scoped to those teams.
To get started you need a client_id and client_secret issued by Commit Swimming.
Contact your Commit partnership representative to receive credentials.
https://utility.commitswimming.comUser Authorization URL:
https://team.commitswimming.com
OAuth Flow
-
Redirect the user to the Commit authorization page:
https://team.commitswimming.com/oauth/authorize ?client_id=YOUR_CLIENT_ID &response_type=code &scope=workouts:read &state=RANDOM_STATE_STRINGThe user will sign in (if needed), see which teams your app is requesting access to, and approve or deny.
-
User approves. Commit redirects back to the
redirect_uriyour app specified:https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE_STRINGVerify the
statematches what you sent to prevent CSRF attacks. - Exchange the code for tokens by calling the token endpoint from your server (see below).
Token Exchange
POST/oauth/token
Exchange an authorization code for an access token and refresh token.
Request body (application/json)
| Parameter | Type | Description |
|---|---|---|
grant_type | string | "authorization_code" |
code | string | The authorization code from the redirect |
client_id | string | Your client ID |
client_secret | string | Your client secret |
redirect_uri | string | The same redirect URI used in the authorize step |
Example request
curl -X POST https://utility.commitswimming.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "abc123...",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uri": "https://yourapp.com/callback"
}'
Response
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBh...",
"scope": "workouts:read",
"super_team_ids": ["teamId1", "teamId2"]
}
access_token expires in 1 hour.
refresh_token expires in 90 days.
Store both securely on your server—never expose them client-side.
Refreshing Tokens
POST/oauth/token
Use your refresh token to get a new access token before or after it expires.
Request body
{
"grant_type": "refresh_token",
"refresh_token": "dGhpcyBpcyBh...",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
The response is the same shape as the initial token exchange. The old token pair is automatically revoked.
Revoking Tokens
POST/oauth/revoke
Revoke an access token or refresh token when the user disconnects your app.
Request body
{
"token": "eyJhbGciOi...",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Always returns 200 OK regardless of whether the token was found (per RFC 7009).
GET /api/v2/partner/me
GET/api/v2/partner/me
Returns information about the authenticated user and which teams were authorized.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Response
{
"userId": "abc123",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"superTeams": [
{ "_id": "team1", "name": "Metro Marlins" },
{ "_id": "team2", "name": "City Sharks" }
],
"scope": "workouts:read"
}
GET /api/v2/partner/workouts
GET/api/v2/partner/workouts
Fetch workouts within a date range for the authorized teams. Returns paginated results with structured set data for pace clock integration.
Headers
Authorization: Bearer YOUR_ACCESS_TOKEN
Query parameters
| Parameter | Required | Description |
|---|---|---|
startDate | Yes | Unix timestamp (seconds). Start of date range. |
endDate | Yes | Unix timestamp (seconds). End of date range. Max 365 days from startDate. |
superTeamIds | No | Comma-separated team IDs to filter. Must be a subset of authorized teams. Defaults to all authorized teams. |
skip | No | Number of results to skip (default: 0). |
limit | No | Results per page, 1–50 (default: 20). |
Example request
curl "https://utility.commitswimming.com/api/v2/partner/workouts?startDate=1710201600&endDate=1710460800&limit=10" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response
{
"workouts": [
{
"_id": "wkt_abc123",
"name": "AM Practice",
"text": "3 rounds\n 2 x 50 Free @ 1:00\n 2 rounds\n 4 x 100 Back @ 2:00",
"dateTime": "2026-03-12T06:00:00.000Z",
"endDateTime": null,
"course": { "distance": 25, "metric": "Yards" },
"trainingGroup": {
"_id": "tg_1",
"name": "Senior",
"superTeamId": "team1"
},
"groups": [
{ "_id": "grp_1", "name": "Distance" },
{ "_id": "grp_2", "name": "Sprint" }
],
"sets": [
{
"description": "3 rounds",
"rounds": 3,
"distance": null,
"interval": null,
"rest": null,
"stroke": null,
"type": null,
"intensity": null,
"equipment": [],
"innerSets": [
{
"description": "2 x 50 Free @ 1:00",
"rounds": 2,
"distance": 50,
"interval": 60,
"rest": null,
"stroke": "Free",
"type": null,
"intensity": null,
"equipment": [],
"innerSets": []
},
{
"description": "2 rounds",
"rounds": 2,
"distance": null,
"interval": null,
"rest": null,
"stroke": null,
"type": null,
"intensity": null,
"equipment": [],
"innerSets": [
{
"description": "4 x 100 Back @ 2:00",
"rounds": 4,
"distance": 100,
"interval": 120,
"rest": null,
"stroke": "Back",
"type": null,
"intensity": null,
"equipment": [],
"innerSets": []
}
]
}
]
}
],
"createdAt": "2026-03-11T22:00:00.000Z",
"updatedAt": "2026-03-11T23:15:00.000Z"
}
],
"pagination": {
"total": 42,
"skip": 0,
"limit": 10,
"hasMore": true
}
}
Scopes
Request only the scopes your application needs. The user will see what you are requesting on the consent screen.
| Scope | Description |
|---|---|
| workouts:read | Read workout data (text, structured sets, groups, course info) |
Additional scopes will be added as the API expands.
Structured Sets
Each workout includes a sets array—a recursive tree representing the workout structure.
This is ideal for pace clock integration, where you need to know rounds, distances, and intervals.
Set object
| Field | Type | Description |
|---|---|---|
description | string | The original text of this set line |
rounds | number | Number of rounds/reps (default: 1) |
distance | number | null | Distance per rep in yards/meters. null for parent sets. |
interval | number | null | Interval in seconds (e.g., 90 for 1:30) |
rest | number | null | Rest in seconds between reps |
stroke | string | null | Stroke name: Free, Back, Fly, Breast, IM, Choice |
type | string | null | Activity type: Swim, Kick, Pull, Drill |
intensity | string | null | Intensity: Easy, Moderate, Fast, Sprint, Build, etc. |
equipment | array | Equipment names (currently always empty—coming soon) |
innerSets | array | Nested child sets. Empty array for leaf sets. |
Reading the tree
A set with innerSets is a parent set—its rounds tells you how many times to repeat
the children. A set with an empty innerSets and a distance is a leaf set—the actual swim piece.
// Pseudocode for a pace clock
function executeSet(set) {
for (let round = 0; round < set.rounds; round++) {
if (set.innerSets.length > 0) {
// Parent set: repeat children
for (const child of set.innerSets) {
executeSet(child)
}
} else {
// Leaf set: run the swim piece
startTimer(set.interval || set.rest)
display(set.distance, set.stroke)
}
}
}
Errors
All errors return a JSON object with an error code and an error_description.
| HTTP Status | Error Code | Meaning |
|---|---|---|
| 400 | invalid_request | Missing or invalid parameters |
| 400 | invalid_grant | Expired or already-used authorization code / refresh token |
| 400 | unsupported_grant_type | Use authorization_code or refresh_token |
| 401 | invalid_client | Bad client_id or client_secret |
| 401 | unauthorized | Missing or expired Bearer token |
| 403 | insufficient_scope | Token doesn't have the required scope |
| 403 | forbidden | Requested a superTeamId not authorized for this token |
| 405 | method_not_allowed | Wrong HTTP method |
Example error
{
"error": "invalid_request",
"error_description": "startDate and endDate are required (unix timestamps in seconds)"
}
Rate Limits
API requests are rate limited per access token. If you exceed the limit you will receive a
429 Too Many Requests response. Current limits:
| Window | Limit |
|---|---|
| Per second | 10 requests |
| Per hour | 500 requests |