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.

API Base URL: https://utility.commitswimming.com
User Authorization URL: https://team.commitswimming.com

OAuth Flow

  1. 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_STRING

    The user will sign in (if needed), see which teams your app is requesting access to, and approve or deny.

  2. User approves. Commit redirects back to the redirect_uri your app specified:
    https://yourapp.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE_STRING

    Verify the state matches what you sent to prevent CSRF attacks.

  3. 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)

ParameterTypeDescription
grant_typestring"authorization_code"
codestringThe authorization code from the redirect
client_idstringYour client ID
client_secretstringYour client secret
redirect_uristringThe 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

ParameterRequiredDescription
startDateYesUnix timestamp (seconds). Start of date range.
endDateYesUnix timestamp (seconds). End of date range. Max 365 days from startDate.
superTeamIdsNoComma-separated team IDs to filter. Must be a subset of authorized teams. Defaults to all authorized teams.
skipNoNumber of results to skip (default: 0).
limitNoResults 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.

ScopeDescription
workouts:readRead 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

FieldTypeDescription
descriptionstringThe original text of this set line
roundsnumberNumber of rounds/reps (default: 1)
distancenumber | nullDistance per rep in yards/meters. null for parent sets.
intervalnumber | nullInterval in seconds (e.g., 90 for 1:30)
restnumber | nullRest in seconds between reps
strokestring | nullStroke name: Free, Back, Fly, Breast, IM, Choice
typestring | nullActivity type: Swim, Kick, Pull, Drill
intensitystring | nullIntensity: Easy, Moderate, Fast, Sprint, Build, etc.
equipmentarrayEquipment names (currently always empty—coming soon)
innerSetsarrayNested 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 StatusError CodeMeaning
400invalid_requestMissing or invalid parameters
400invalid_grantExpired or already-used authorization code / refresh token
400unsupported_grant_typeUse authorization_code or refresh_token
401invalid_clientBad client_id or client_secret
401unauthorizedMissing or expired Bearer token
403insufficient_scopeToken doesn't have the required scope
403forbiddenRequested a superTeamId not authorized for this token
405method_not_allowedWrong 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:

WindowLimit
Per second10 requests
Per hour500 requests
Need help? Contact your Commit Swimming partnership representative for support or to request additional scopes.