CorrConnectDocs
Early accessRequest access

API reference

Errors

Every error has a stable code you can branch on, a message written for people, and a request ID for support. Messages can change; codes don't.

Error shape

JSON
{
  "code": "rate_limit_exceeded",
  "message": "This app has used its request limit; retry after 412 seconds.",
  "request_id": "req_0123456789abcdef0123456789abcdef",
  "details": {
    "scope": "app",
    "per_15_minutes": 100,
    "per_day": 10000,
    "per_athlete_per_15_minutes": 30,
    "per_athlete_per_day": 2000
  }
}

Branch on code and the HTTP status. Show message to a developer, not to an athlete. details appears on a few codes: validation_failed lists fields, rate_limit_exceeded gives the limits and scope, app_limit_reached gives max_apps, invalid_scope gives supported_scopes.

Bearer failures also send a WWW-Authenticate header: error="invalid_token" for 401, and error="insufficient_scope", scope="..." for 403.

Callback errors

If the athlete declines, your callback receives error=access_denied and your state instead of a code. It isn't a failure of your app: show them what they'd get and a way to try again. Problems with the authorize link itself never reach your callback; CorrConnect shows the athlete the error instead, with one of the /oauth/authorize codes below.

API errors

Everything except the token endpoint answers with code, message, request_id and sometimes details. invalid_client appears in both shapes: as code on the authorize page and as error from the token endpoint.

CodeStatusWhereWhat to do
account_exists409POST /v1/account/registerSign in instead, or reset the password from the portal.
activity_not_found404GET /v1/activities/{activity_id}Check the ID came from this athlete. Deleted accounts take their activities with them.
app_limit_reached409POST /v1/developer/appsDelete an app you no longer use, or email support for more.
app_not_found404DELETE /v1/developer/apps/{app_id}
Every webhook subscription endpoint
Check the app ID and that you are signed in to the account that owns it.
app_registration_rate_limited429POST /v1/developer/appsWait for Retry-After seconds.
authentication_required401GET /v1/account/me
POST /v1/account/logout
Every app endpoint
Every webhook subscription endpoint
Sign in with POST /v1/account/login and send the session cookie.
csrf_rejected403POST /v1/account/logout
DELETE /v1/developer/apps/{app_id}
POST /v1/developer/apps
DELETE /v1/developer/apps/{app_id}/webhooks/{subscription_id}
POST /v1/developer/apps/{app_id}/webhooks
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/enable
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/test
Call GET /v1/account/me for a fresh csrf_token and send it as X-CSRF-Token.
email_verification_required403GET /v1/account/me
POST /v1/account/login
POST /v1/account/logout
Every app endpoint
Every webhook subscription endpoint
Open the verification link CorrConnect emailed to the account.
http_error405Any endpointAnother HTTP-level error, such as a method the route does not support; message has the detail.
insufficient_scope403Every athlete data endpointSend the athlete through /oauth/authorize again asking for the scope you need.
invalid_access_token401POST /oauth/revoke
POST /oauth/revoke-token
Every athlete data endpoint
Refresh the access token. If the refresh fails with invalid_grant, the athlete has to approve your app again.
invalid_authorization_request400GET /oauth/authorizeSend a state of 8 or more characters and a 43 to 128 character S256 challenge.
invalid_callback_url400POST /v1/developer/apps/{app_id}/webhooksUse a public https:// URL on port 443.
invalid_client400GET /oauth/authorize
POST /oauth/token
Check client_id is the app_... value from registration and the app still exists.
invalid_credentials401POST /v1/account/loginCheck the email and password.
invalid_cursor400GET /v1/activitiesStart the listing again without a cursor.
invalid_event_type400POST /v1/developer/apps/{app_id}/webhooksUse activity.created and grant.revoked only.
invalid_limit400GET /v1/activitiesUse a limit from 1 to 100.
invalid_redirect_uri422POST /v1/developer/appsUse exact https:// URLs with no fragment, user name, password or *.
invalid_scope400GET /oauth/authorizeAsk only for activities:read and profile:read, space-separated.
invalid_session401GET /v1/account/me
POST /v1/account/logout
Every app endpoint
Every webhook subscription endpoint
Sign in again.
invalid_source400GET /v1/activitiesUse one of the documented source values, or leave source out.
not_found404Any endpointThe path does not exist, or it is a portal route sent to the API host.
origin_moved404Any endpointA non-GET request reached an old CorrConnect origin; send it to the current one.
origin_rejected403POST /v1/account/login
POST /v1/account/logout
POST /v1/account/register
DELETE /v1/developer/apps/{app_id}
POST /v1/developer/apps
DELETE /v1/developer/apps/{app_id}/webhooks/{subscription_id}
POST /v1/developer/apps/{app_id}/webhooks
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/enable
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/test
Set Origin to exactly your CORRCONNECT_BASE address, with no path or trailing slash.
rate_limit_exceeded429POST /v1/account/login
POST /v1/account/register
Every athlete data endpoint
POST /v1/developer/apps/{app_id}/webhooks
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/enable
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/test
Wait for Retry-After seconds. details.scope says whether your app or one athlete hit the limit.
redirect_uri_mismatch400GET /oauth/authorizeRegister the exact callback, then send it unchanged (same scheme, host, path and query).
service_capacity_exceeded429Every athlete data endpointWait for Retry-After seconds and retry. It does not count against your app.
session_expired401GET /v1/account/me
POST /v1/account/logout
Every app endpoint
Every webhook subscription endpoint
Sign in again.
unsupported_authorization_request400GET /oauth/authorizeUse response_type=code and code_challenge_method=S256.
validation_failed422POST /v1/account/login
POST /v1/account/register
POST /v1/developer/apps
POST /oauth/revoke-token
GET /v1/activities
GET /v1/developer/apps/{app_id}/webhooks/{subscription_id}/deliveries
POST /v1/developer/apps/{app_id}/webhooks
Read details.fields; each entry names the field and the problem.
webhook_disabled409POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/testEnable the subscription, then send the test again.
webhook_limit_reached409POST /v1/developer/apps/{app_id}/webhooksDelete a subscription first; an app can have 3.
webhook_not_found404DELETE /v1/developer/apps/{app_id}/webhooks/{subscription_id}
GET /v1/developer/apps/{app_id}/webhooks/{subscription_id}/deliveries
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/enable
POST /v1/developer/apps/{app_id}/webhooks/{subscription_id}/test
List the app's subscriptions to find the current ID.

OAuth token errors

These come back from POST /oauth/token in the OAuth shape: error, error_description and request_id.

ErrorStatusWhereWhat to do
invalid_grant400POST /oauth/tokenDo not retry the same code or refresh token. Start a new authorization.
invalid_request400POST /oauth/tokenSend every required form field as application/x-www-form-urlencoded.
slow_down429POST /oauth/tokenWait for Retry-After seconds. Fix the failing requests first; failures are what count.
unsupported_grant_type400POST /oauth/tokenUse authorization_code or refresh_token.

CorrConnect's own pages

The portal, sign-in recovery and device-connection screens use these codes. Your app does not call those routes, but an athlete may mention them in a support request.

CodeStatusMeaning
confirmation_required400A device disconnect was sent without its confirmation.
invalid_provider_state400A device-connection callback did not match a sign-in CorrConnect started.
invalid_action_token400An email verification or password-reset link is invalid or expired.
invalid_workout_file400An uploaded GPX, TCX or FIT file could not be read; message says why.
grant_not_found404The athlete tried to revoke a grant that is not theirs or no longer exists.
provider_request_not_found404A device request to withdraw was not found.
provider_account_in_use409That device account is already linked to another CorrConnect account.
provider_already_connected409The device account is already connected.
provider_not_connected409There is no device connection to sync or remove.
provider_operation_active409Another connect, sync or disconnect for this device is running.
provider_revocation_pending409A previous disconnect is still being completed with the device maker.
stale_provider_operation409The connection changed while this request ran; try again.
file_too_large413An uploaded workout file is over 2 MB.
unsupported_media_type415An upload was not GPX, TCX or FIT.
password_reused422A password reset chose the current password.
provider_sync_failed502The device maker's API failed during a sync.
provider_persistence_failed502Records from a device could not be saved; nothing was half-written.
provider_unavailable503That device connection is not available yet.
mail_unavailable503CorrConnect could not send email right now.
launch_gates_incomplete503Operator readiness check: the service is not qualified for production.