openapi: 3.1.0
info:
title: Equipment Vendor API (for server-to-server cases)
description: "At EGYM, we believe that fitness is the soul of healthy living. Work together with partners to help us bringing\
\ new ways of working out to \nmillions of gym members, and new ways of prescribing exercises to hundred of thousands\
\ of gym trainers, all around the world.\n\nEGYM provides a Fitness Platform for the Connected Gym so you can help us\
\ with the mission to ***make the gym work for everyone***. \nEGYM provides a global account that is not scoped for a\
\ specific tenant, brand or gym. The account is called EGYM ID. The EGYM ID allows \nusers to use different services of\
\ the gym. The EGYM ID is used for the login to EGYM hardware and software products and conneceted equipment. \n\nEGYM\
\ works with the best of breed of the fitness industry. In this documentation, we provide tools and info needed to build\
\ a variety of workout \nexperiences that integrate directly with the EGYM Ecosystem. We are happy to welcome you as a\
\ partner of EGYM.\n\n# Introduction\n\nThe EGYM Equipment Vendor API Server is an open Web service specification that\
\ defines methods to connect and save measurements between the\nPartner's Cloud and the EGYM Cloud Platform.\n\nUse the\
\ resources below to create, test, and submit your integration to EGYM.\n\n---\n\n# \U0001F4E6 Changelog of Documentation\n\
\n| Version | Changes |\n|---------|---------|\n| **1.1.1** | \U0001F510 Introduced **NFC-based login** via Apple/Google\
\ Wallet — see [`POST /api/v1/oauth/token`](#tag/OAuth/operation/token) |\n| **1.1.0** | ✅ Update of Introduction|\n\n\
---\n\n# Client Authentication\n\nThe Equipment Vendor API Server client authentication is based on the API Key.\n\nCurrently,\
\ different methods for obtaining the Access Token are made available, one by specifying the EGYM User ID (encrypted or\
\ obfuscated) and another one by specifying \nthe user RFID.\n\n## \U0001F504 Typical Integration Flow\n\nTo integrate\
\ your equipment with the EGYM platform, follow this sequence:\n\n1. **Authenticate via OAuth** \n The standard and\
\ recommended method is using RFID-based login (`RFID`) or NFC wallet-based login (`NFC`).\n\n > Logins using `ENCRYPTED_USER_ID`,\
\ `OBFUSCATED_USER_ID`, or `REFRESH_TOKEN` are only for special use cases and require prior alignment with EGYM.\n\n2.\
\ **Retrieve User Details** \n Use the `/api/v1/gyms/{gymId}/users` endpoint with the token to get enriched user data.\n\
\n3. **Submit a Measurement** \n Post workout data (body, cardio, flexibility) to the `/api/v1/measurements/...` endpoint.\n\
\n> All subsequent requests after login must include the `Authorization: Bearer <accessToken>` header.\n\n```plaintext\n\
[Authenticate (POST /oauth/token)]\n ↓\n[Get User Details (GET /gyms/{gymId}/users)]\n ↓\n[Submit\
\ Measurements (POST /measurements/*)]\n\n```\n\n---\n\n## OAuth Login\n\nThe `/api/v1/oauth/token` endpoint supports\
\ multiple login methods via the `grantType` field. Select the appropriate grant method and use the corresponding request\
\ payload.\n\n### Grant Types and Payload Examples\n\n#### \U0001F3F7️ RFID Login (Standard)\n```json\n{\n \"grantType\"\
: \"RFID\",\n \"rfid\": \"AB1020CD\",\n \"rfidFormat\": \"MIFARE\",\n \"machineName\": \"scale\"\n}\n```\n\n#### \U0001F4F1\
\ NFC Login (Google Wallet)\n```json\n{\n \"grantType\": \"NFC\",\n \"machineName\": \"scale\",\n \"gymId\": 130,\n\
\ \"payload\": \"nfc-token-data-abc123\",\n \"transponderType\": \"GOOGLE\",\n \"issuerId\": {\n \"collector\":\
\ \"123456789\"\n }\n}\n```\n\n#### \U0001F4F1 NFC Login (Apple Wallet)\n```json\n{\n \"grantType\": \"NFC\",\n \"\
machineName\": \"scale\",\n \"gymId\": 130,\n \"payload\": \"nfc-token-data-abc123\",\n \"transponderType\": \"APPLE\"\
,\n \"issuerId\": {\n \"passTypeId\": \"example.apple.com\"\n }\n}\n```\n\n#### \U0001F501 NFC Login (with VTAP Configuration\
\ Index)\n```json\n{\n \"grantType\": \"NFC\",\n \"machineName\": \"scale\",\n \"gymId\": 130,\n \"payload\": \"nfc-token-data-abc123\"\
,\n \"transponderType\": \"GOOGLE\",\n \"configurationIndex\": {\n \"configurationIndex\": \"2\"\n }\n}\n```\n\n\
---\n\n#### \U0001F510 Encrypted User ID (Special Case - Requires Approval)\n```json\n{\n \"grantType\": \"ENCRYPTED_USER_ID\"\
,\n \"userId\": 4337273027797130000\n}\n```\n\n#### \U0001F9D1 Obfuscated User ID (Special Case - Requires Approval)\n\
```json\n{\n \"grantType\": \"OBFUSCATED_USER_ID\",\n \"obfuscatedUserId\": \"-1c2yqo37vn9m\"\n}\n```\n\n#### \U0001F504\
\ Refresh Token (Special Case - Requires Approval)\n```json\n{\n \"grantType\": \"REFRESH_TOKEN\",\n \"refreshToken\"\
: \"sample-refresh-token-123\"\n}\n```\n\n##\n---\n\n## \U0001F464 Get User Details\n\n\n\n### Endpoint: `/api/v1/gyms/{gymId}/users`\n\
Use the access token from login to fetch user profile data:\n\nReturns:\n```json\n{\n \"equipmentUser\": {\n \"userId\"\
: \"-1c2yqo37vn9m\",\n \"firstName\": \"Max\",\n \"lastName\": \"Mustermann\",\n \"gender\": \"MALE\",\n \"\
dateOfBirth\": \"1990-01-01\",\n \"height\": 180,\n \"weight\": 75,\n \"locale\": \"en_US\",\n \"trainingGoal\"\
: \"MUSCLE_GAIN\"\n }\n}\n```\n\n---\n\n\n\n## \U0001F3CB️ Submit Measurements\n\n\n\n### Endpoint: `/api/v1/measurements/body`\n\
\n```json\n{\n \"timestamp\": 1693474800000,\n \"machineName\": \"smartscale123\",\n \"gymId\": 130,\n \"uniqueMeasurementId\"\
: \"unique-id-xyz\",\n \"data\": {\n \"weight\": 72.5,\n \"bodyFat\": 15.3\n }\n}\n```\n\n- `timestamp`: Milliseconds\
\ since epoch\n- `uniqueMeasurementId`: Must be unique for deduplication\n- `data`: Key-value pairs per registered metric\n\
\nSimilar structure is used for `/measurements/cardio` and `/measurements/flexibility`.\n\n\n\n---\n\n\n\n## \U0001F512\
\ Security\n\nAll requests must include one of:\n\n- `x-api-key`: For login endpoint\n- `Authorization: Bearer <accessToken>`:\
\ For all other endpoints\n\n---\n\n\n\n## ❓ FAQ\n\nThis section addresses common technical integration questions specific\
\ to the **Server-to-Server** model.\n\n## 1. Authentication\n- Use the **API-Key** provided by EGYM for your integration\n\
- API-Key is issued **per gym location**\n- Store securely in your backend; do not expose to client devices\n\n## 2. User\
\ Identification\n- Your backend is responsible for mapping user IDs between your system and EGYM\n- Identification methods\
\ can include:\n - RFID/NFC data received from devices\n - Account linking between your user database and EGYM\n\n##\
\ 3. Data Flow\n- Devices send data to your backend\n- Backend sends authenticated API requests to EGYM Cloud\n- Supports\
\ both **real-time** and **batched** submissions\n\n## 4. Key Endpoints for Server-to-Server Integrations\n- Authentication:\
\ via API-Key in request headers\n- User details: `GET /users`\n- Measurements: `POST /measurements/*`\n\n## 5. Error\
\ Handling Best Practices\n- **401 Unauthorized**: Invalid/missing API-Key\n- **403 Forbidden**: Permission issue\n- **429\
\ Too Many Requests**: Rate limit exceeded; retry after delay\n- Implement retry logic with exponential backoff for transient\
\ errors\n\n## 6. Rate Limits\n- Standard API limits apply – see API documentation\n- Monitor usage to avoid hitting limits\
\ during batch processing\n\n## 7. Testing\n- Use the **EGYM Sandbox** environment for development\n- Test credentials\
\ are prodvided by EGYM\n- Test authentication, user mapping, data submission, and error handling\n\n## 8. What if I send\
\ the same measurement twice?** \nUse a different `uniqueMeasurementId` each time.\n\n## 9.When should I use `issuerId`\
\ vs `configurationIndex` in NFC?** \nThese are alternatives for identifying the wallet pass source. Provide one depending\
\ on the integration project with EGYM. Configuration Index is only supported when using the Dot Origin VTAP readers.\n\
---"
termsOfService: https://egym.com/us/terms/
contact:
name: EGYM GmbH
url: https://egym.com/
email: support@egym.com
version: 1.1.1
servers:
- url: https://b2b-partner-api.ext-1.test.co.egym.coffee
description: Generated server url
paths:
/api/v1/oauth/token:
post:
tags:
- OAuth
summary: Login
description: This endpoint logs in the user and returns the access token
operationId: createToken
requestBody:
content:
application/json:
schema:
description: Select the grant type
anyOf:
- $ref: '#/components/schemas/RfidTokenRequestDTO'
- $ref: '#/components/schemas/NfcTokenRequestDTO'
- $ref: '#/components/schemas/EncryptedUserIdTokenRequestDTO'
- $ref: '#/components/schemas/ObfuscatedUserIdTokenRequestDTO'
- $ref: '#/components/schemas/RefreshTokenRequestDTO'
examples:
EncryptedUserId:
summary: Encrypted User ID Login
value:
grantType: ENCRYPTED_USER_ID
userId: 4337273027797130000
ObfuscatedUserId:
summary: Obfuscated User ID Login
value:
grantType: OBFUSCATED_USER_ID
obfuscatedUserId: -1c2yqo37vn9m
RFID:
summary: RFID Login
value:
grantType: RFID
rfid: AB1020CD
rfidFormat: MIFARE
machineName: scale
NFC (Google):
summary: NFC Login Google
value:
grantType: NFC
machineName: scale
gymId: 130
payload: nfc-token-data-abc123
transponderType: GOOGLE
issuerId:
collector: '123456789'
NFC (Apple):
summary: NFC Login Apple
value:
grantType: NFC
machineName: scale
gymId: 130
payload: nfc-token-data-abc123
transponderType: APPLE
issuerId:
passTypeId: example.apple.com
NFC (DotOrigin VTAP ConfigurationIndex):
summary: NFC Login (VTAP)
value:
grantType: NFC
machineName: scale
gymId: 130
payload: nfc-token-data-abc123
transponderType: GOOGLE
configurationIndex:
configurationIndex: '2'
RefreshToken:
summary: Refresh Token Login
value:
grantType: REFRESH_TOKEN
refreshToken: sample-refresh-token-123
required: true
description: Select from the dropdown the Login method that should be used
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponseDTO'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/TokenResponseDTO'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
security:
- partner: []
/api/v1/oauth/.well-known/jwks.json:
get:
tags:
- OAuth
summary: well-known
description: This endpoint returns a JSON Web Key (JWK) set.
operationId: wellKnown
security: []
responses:
'200':
description: OK
content:
application/json:
schema:
type: string
example:
keys:
- kty: RSA
kid: '1'
alg: RS256
e: AQAB
n: jvb
/api/v1/gyms/{gymId}/users:
get:
tags:
- User Details
summary: Get user details
description: This endpoint retrieves user details in the defined gym location
operationId: getUserDetails
parameters:
- name: gymId
in: path
description: The EGYM Gym Location ID.
required: true
schema:
type: integer
format: int64
example: 130
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/UserDetailsDTO'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/UserDetailsDTO'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
security:
- user: []
/api/v1/measurements/body:
post:
tags:
- Body Measurement
summary: Create Body Measurement
description: This endpoint creates a body measurement for the user. See the Body Measurements document for the list
of available measurements that can be transferred to EGYM cloud.
operationId: createBodyMeasurement
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementDTO'
required: true
responses:
'204':
description: No Content
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'401':
description: Unauthorized
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
security:
- user: []
/api/v1/measurements/cardio:
post:
tags:
- Cardio Measurement
summary: Create Cardio Measurement
description: This endpoint creates a cardio measurement for the user. See the Cardio Measurements document for the list
of available measurements that can be transferred to EGYM cloud.
operationId: createCardioMeasurement
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementDTO'
required: true
responses:
'204':
description: No Content
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'401':
description: Unauthorized
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
security:
- user: []
/api/v1/measurements/flexibility:
post:
tags:
- Flexibility Measurement
summary: Create Flexibility Measurement
description: This endpoint creates a flexibility measurement for the user. See the Flexibility Measurements document
for the list of available measurements that can be transferred to EGYM cloud.
operationId: createFlexibilityMeasurement
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MeasurementDTO'
required: true
responses:
'204':
description: No Content
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'401':
description: Unauthorized
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorDTO'
security:
- user: []
components:
schemas:
MeasurementDTO:
required:
- data
- gymId
- machineName
- timestamp
- uniqueMeasurementId
type: object
properties:
timestamp:
type: integer
description: The timestamp of when the measurement took place in milliseconds since the epoch time.
format: int64
example: 1553074207520
machineName:
type: string
description: The machine name of the equipment that generates the measurement. Machine name needs to be registered
at EGYM.
example: device123
gymId:
type: integer
description: The EGYM Gym Location ID.
format: int64
example: 130
uniqueMeasurementId:
type: string
description: Unique identifier for this measurement. The machine should generate a unique id for each measurement
submitted.
example: f5c15c853c1e46de
data:
type: object
additionalProperties:
type: number
description: A map of properties that define each measurement for this equipment. Properties need to be registered
per machineName at EGYM.
description: A map of properties that define each measurement for this equipment. Properties need to be registered
per machineName at EGYM.
ErrorDTO:
type: object
properties:
timestamp:
type: string
description: The timestamp of the request.
format: date-time
path:
type: string
description: The path requested.
requestId:
type: string
description: The request ID.
example: de625cf1-1
status:
type: integer
description: The http status code.
format: int32
error:
type: string
description: The error.
message:
type: string
description: The message to describe the error.
fieldErrors:
type: array
description: The constraint violations.
items:
$ref: '#/components/schemas/FieldErrorDTO'
FieldErrorDTO:
type: object
properties:
name:
type: string
description: The field name.
example: age
message:
type: string
description: The error message.
example: Must be greater than 10.
rejectedValue:
type: object
description: The rejected value.
example: 3
description: The constraint violations.
EncryptedUserIdTokenRequestDTO:
allOf:
- $ref: '#/components/schemas/TokenRequestDTO'
- properties:
userId:
type: integer
description: EGYM User ID encrypted.
format: int64
title: Encrypted User ID
description: Encrypted User ID Login
type: object
ObfuscatedUserIdTokenRequestDTO:
required:
- grantType
- obfuscatedUserId
type: object
title: Obfuscated User ID
allOf:
- $ref: '#/components/schemas/TokenRequestDTO'
- type: object
properties:
obfuscatedUserId:
type: string
description: EGYM User ID obfuscated.
RefreshTokenRequestDTO:
required:
- grantType
- refreshToken
type: object
title: Refresh Token
allOf:
- $ref: '#/components/schemas/TokenRequestDTO'
- type: object
properties:
refreshToken:
type: string
description: The refresh token.
example: ab3a16b67...
RfidTokenRequestDTO:
required:
- grantType
- machineName
- rfid
- rfidFormat
type: object
title: RFID
allOf:
- $ref: '#/components/schemas/TokenRequestDTO'
- type: object
properties:
rfid:
type: string
description: RFID serial number.
rfidFormat:
type: string
description: RFID Format.
enum:
- MIFARE
- LEGIC
machineName:
type: string
description: The name of the machine.
NfcTokenRequestDTO:
required:
- grantType
- machineName
- payload
- transponderType
- gymId
title: NFC
allOf:
- $ref: '#/components/schemas/TokenRequestDTO'
- required:
- machineName
- gymId
- payload
properties:
machineName:
type: string
description: The name of the machine.
gymId:
type: integer
example: 130
description: The EGYM Gym Location ID. Mandatory if the grant_type is NFC.
payload:
type: string
description: The NFC payload. The NFC payload is a string that represents the NFC data contained in a wallet pass.
The string must be no more than 64 bytes long and may only contain ASCII characters. In the case of an Apple
Wallet pass, corresponds to the `message` property of the [`Pass.NFC`](https://developer.apple.com/documentation/walletpasses/pass/nfc#properties)
object. In the case of a Google Wallet pass, corresponds to the `smartTapRedemptionValue` property of the [`GenericObject`](https://developers.google.com/wallet/reference/rest/v1/genericobject#GenericObject.FIELDS.smart_tap_redemption_value)
resource. Mandatory if the grant_type is NFC.
transponderType:
type: string
description: The pass type of the NFC data. Can be Apple or Google.
enum:
- APPLE
- GOOGLE
issuerId:
$ref: '#/components/schemas/IssuerId'
configurationIndex:
$ref: '#/components/schemas/VtapKeySlot'
x-internal: false
type: object
IssuerId:
type: object
description: The issuer of the NFC data. Must be provided if vtap configurationIndex is not provided.
x-internal: false
properties:
passTypeId:
type: string
description: The Google collector ID of the pass issuer.
collectorId:
type: string
description: The Apple pass type ID of the pass issuer.
VtapKeySlot:
type: object
description: The VTAP configuration index read. Must be provided if issuerId is not provided.
x-internal: false
properties:
configurationIndex:
type: string
example: '2'
title: ''
TokenRequestDTO:
type: object
discriminator:
propertyName: grantType
mapping:
RFID: '#/components/schemas/RfidTokenRequestDTO'
ENCRYPTED_USER_ID: '#/components/schemas/EncryptedUserIdTokenRequestDTO'
OBFUSCATED_USER_ID: '#/components/schemas/ObfuscatedUserIdTokenRequestDTO'
REFRESH_TOKEN: '#/components/schemas/RefreshTokenRequestDTO'
NFC: '#/components/schemas/NfcTokenRequestDTO'
description: Grant Type
properties:
grantType:
type: string
description: The grant type.
enum:
- RFID
- ENCRYPTED_USER_ID
- REFRESH_TOKEN
- OBFUSCATED_USER_ID
- NFC
required:
- grantType
TokenResponseDTO:
type: object
properties:
accessToken:
type: string
description: The access token.
example: a9a160b77797f...
ab3a16b67...:
type: string
description: The refresh token.
expirationTime:
type: integer
description: The expiration time of the access token.
format: int64
example: 1553074207520
EquipmentUserDetailsDTO:
type: object
properties:
userId:
type: string
description: The userId assigned by egym, for example -1c2yqo37vn9m.
example: -1c2yqo37vn9m
email:
type: string
description: The email address of the user. Not available by default. Only available if needed for use case.
firstName:
type: string
description: The user's first name.
lastName:
type: string
description: The user's last name.
gender:
type: string
description: The gender of the user.
enum:
- MALE
- FEMALE
- NON_BINARY
dateOfBirth:
type: string
description: The date of birth in ISO 8601 format (yyyy-MM-dd).
format: date
height:
type: number
description: The body height of the user in centimeters.
format: double
example: 180
weight:
type: number
description: The body weight of the user in kilograms.
format: double
example: 75
locale:
type: string
description: 'The user locale is either in ISO 15897 standard format. i.e.: ''en_US'' or just language. i.e.: ''en'''
example: en_US
unitSystem:
type: string
description: The user Unit System.
enum:
- METRIC
- US_CUSTOMARY
- IMPERIAL
trainingGoal:
type: string
description: The user training goal.
enum:
- WEIGHT_LOSS
- FITNESS
- MUSCLE_GAIN
- BBP
- BBP_AND_WEIGHT_LOSS
- REHASPORT
termsAndConditionsAccepted:
type: boolean
description: Informs if the terms and conditions have been accepted.
UserDetailsDTO:
type: object
properties:
equipmentUser:
$ref: '#/components/schemas/EquipmentUserDetailsDTO'
securitySchemes:
user:
type: http
scheme: bearer
bearerFormat: JWT
partner:
type: apiKey
name: x-api-key
in: header
x-tagGroups:
- name: Authentication
tags:
- OAuth
- name: Measurements
tags:
- Body Measurement
- Cardio Measurement
- Flexibility Measurement
- name: User
tags:
- User Details
x-internal: false