# Canonical GroupX Classes API This specification defines *Canonical API* for GroupX Classes Service, which provides operations for working with Group Exercise Classes, their schedules and attendees. **WARNING:** some sections of this document are marked for Professional Services only or for ClubMS Provider only. Before sharing with respective parties, the irrelevant sections must be removed. ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) - sections marked with such icon are intended only for Professional Services Teams (with their all subsections) ## Introduction to ClubMS and Canonical APIs **Club Management Software (ClubMS)** is a suite of software for managing fitness clubs: billing system, members management, group calendars, payments processing, personal trainers, etc. ClubMS is also know as Membership Management Software (MMS). There are a lot of different ClubMS in the world, and they all have different domain models. Generally speaking, for a given fitness location, different services might be provided by different external ClubMS. **Canonical API** hides this difference by mapping to Netpulse Canonical Model. Implementation of Canonical API for a certain ClubMS service makes it compatible with Netpulse Platform. Some of the contracts in this Canonical API definition depend on multiple meta options. These meta options are not reflected by API parameters directly, but define conditional API contracts. Meta option values reflect ClubMS specifics and are provided by *Implementer of Canonical API*. Netpulse Platform is then configured with these option values to enable integration with the given canonized service. **For example:** Different ClubMS use different types of credentials (security schemas) for their operations: client secret, chain secret, or location secret. Galaxy will expect corresponding configuration and pass appropriate parameters depending on corresponding meta option value. The meta options are defined by a json schema that accompanies the yaml file (they should be sent together). All conditional contracts that depend on specific meta option values are described in this Canonical API with references like `metaOption::someOptionName`. Implementer of Canonical API might want to remove all these conditional contracts and replace them by concrete contracts based on ClubMS specific meta option values (that should be known to the Implementer of Canonical API). String "metaOption::" can be used for quick finding of all such conditional contracts. ## General API Contracts ### Date Format All dates in the API are strings in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format and in **UTC time zone**: ``` yyyy-MM-dd'T'HH:mm:ss'Z' ``` Valid example: ``` 2010-08-21T22:31:20Z ``` Invalid examples: ``` 2008-05-26T07:23: (missing seconds) 2008-05-26T07:23:01.500Z (nanoseconds value is not supported) 2008-05-26 07:23:01Z ('T' in the middle is not specified) 2008-05-23T07:23:01 (trailing 'Z' is missing) ``` ### Additional Data HTTP Headers There are several HTTP Headers that must be used to provide additional data to every canonical operation. These headers presence based on *`metaOption::includeClientData`*, *`metaOption::includeChainData`*, *`metaOption::includeLocationData`*, *`metaOption::includeLocationGroupData`*, *`metaOption::includeUserData`* options. These options should be switched to `true` if any of the following applicable: * security access of certain level required; * extra configuration data is expected on some level; #### X-Client-ID This header contains Partner Client ID. It is a credential to access canonical API in scope of specific Partner implementation. It is required for every canonical operation which supports API access restriction on Partner level. In this case *`metaOption::includeClientData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### X-Client-Secret This header contains Partner Client Secret. It is a credential to access canonical API in scope of specific Partner implementation. It is required for every canonical operation which supports API access restriction on Partner level. In this case *`metaOption::includeClientData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png)X-NP-Client-Configuration This header provides Netpulse configuration associated with a Partner Client mentioned in `X-Client-ID` header. should be used if extra client application configuration data needed to call operaiton on ClubMS side. In this case *`metaOption::includeClientData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * badConfig => 400 (when configuration contents is invalid or there is external error because of wrong configuration parameters: for example something not found)   #### X-Chain-ID This header provides ID of a Chain which we access in canonical operation. Required for every canonical operaiton based on Chain information (security, configuration etc.) In this case *`metaOption::includeChainData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### X-Chain-Secret This header provides chain-level secret correspondent to Chain ID defined in `X-Chain-ID` header. Required if ClubMS expects access tokens or secret keys on accessing to Chain level information via API. In this case *`metaOption::includeChainData`* must be switched on for that operation. **_Additional Contracts:_** Secret can be any string of up to 1024 characters allowed for HTTP header values (see RFC2616). Possible reasons of validation errors: * missing => 400 * invalid => 401   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png)X-NP-Chain-Configuration This header provides Netpulse configuration associated with a chain mentioned in `X-Chain-ID` header. If extra chain level configuration data needed to call operaiton on ClubMS side. In this case *`metaOption::includeChainData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * badConfig => 400 (when configuration contents is invalid or there is external error because of wrong configuration parameters: for example something not found)   #### X-Location-Group-ID This header provides ID of a Location Group which we access in canonical operation. Required for every canonical operation based on Location Group information (security, configuration etc.) In this case *`metaOption::includeLocationGroupData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### X-Location-Group-Secret This header provides location group level secret correspondent to Location Group ID defined in `X-Location-Group-ID` header. Required if ClubMS expects access tokens or secret keys on accessing to Location Group level information via API. In this case *`metaOption::includeLocationGroupData`* must be switched on for that operation. **_Additional Contracts:_** Access data can be any string of up to 1024 characters allowed for HTTP header values (see RFC2616). Possible reasons of validation errors: * missing => 400 * invalid => 401   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png)X-NP-Location-Group-Configuration This header provides Netpulse configuration associated with a location group mentioned in `X-Location-Group-ID` header. Required if extra location group level configuration data needed to call operaiton on ClubMS side. In this case *`metaOption::includeLocationGroupData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * badConfig => 400 (when configuration contents is invalid or there is external error because of wrong configuration parameters: for example something not found)   #### X-Location-ID This header provides ID of a Location which we access in canonical operation. Required for every canonical operaiton based on Location information (security, configuration etc.) In this case *`metaOption::includeLocationData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### X-Location-Secret This header provides location-level secret correspondent to Location ID defined in `X-Location-ID` header. Required if ClubMS expects access tokens or secret keys on accessing to Location level information via API. In this case *`metaOption::includeLocationData`* must be switched on for that operation. **_Additional Contracts:_** Access data can be any string of up to 1024 characters allowed for HTTP header values (see RFC2616). Possible reasons of validation errors: * missing => 400 * invalid => 401   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png)X-NP-Location-Timezone This header provides location timezone correspondent to Location ID defined in `X-Location-ID` header. It is required if *`metaOption::includeLocationData`* switched on for the operation. Possible reasons of validation errors: * missing => 400 * timezoneUnknown => 400   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png)X-NP-Location-Configuration This header provides Netpulse configuration associated with a location mentioned in `X-Location-ID` header. If extra location level configuration data needed to call operaiton on ClubMS side. In this case *`metaOption::includeLocationData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * badConfig => 400 (when configuration contents is invalid or there is external error because of wrong configuration parameters: for example something not found)   #### X-User-ID This header contains User ID in Partner system. Required for every canonical operaiton which supports API access restriction on User level. In this case *`metaOption::includeUserData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### X-User-Secret This header contains User Secret in Partner system. It is required for every canonical operation which supports API access restriction on User level. In this case *`metaOption::includeUserData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   #### X-User-Locale This header contains Locale which was choosed on users phone. Required for every canonical operaiton which supports needs localization. In this case *`metaOption::includeLocalizationData`* must be switched on for that operation. Possible reasons of validation errors: * missing => 400 * invalid => 401   ### Errors Conventional HTTP response codes are used to indicate the success or failure of an API request. General Requirements: * The contracts of this section apply to every operation of Canonical APIs. * Furthermore, every operation specifies its own specific contracts which extend these ones. * Every error response has field `message`. * Every error response has field `cause`. * There are standard causes associated with corresponding HTTP status codes (4xx, 5xx). * And there might be also custom causes always defined on operation level and associated with 422 status code. #### 400: Bad Request Cause is `badRequest`. This cause is used for all parameter-level validation errors. This is often due to missing a required parameter or wrong parameter format. This can be also due to violation of some contract defined in the given specification for a certain parameter. Requirements: * Error message can simply contain "Bad Request" * Additional field `errors` must be present, which is a map of pairs: `"": ""`. See example. * Parameter names are defined in the documentation of respective canonical operations. * Possible reasons are defined in the documentation of parameters of respective canonical operations. **NOTE:** ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) This code must never be used to translate unexpected 4xx error occured in the third-party external system. Use 500 instead (with `cause=externalSystemUnexpectedResponse`). Example: ``` { "message": "Bad Request", "cause": "badRequest", "errors": { "locationId": "missing", "startDateTime": "greaterThanEndDateTime", "endDateTime": "lessThanStartDateTime" } } ```   #### 401: Unauthorized Access Cause is `unauthorizedAccess`. Bad, unexpected or expired access data (see 'Additional Data HTTP Headers': `X-Client-ID`/`X-Client-Secret`, `X-Chain-ID`/`X-Chain-Secret`, `X-Location-ID`/`X-Location-Secret`, `X-Location-Group-ID`/`X-Location-Group-Secret`, `X-User-ID`/`X-User-Secret`). Requirements: * Error message must clearly indicate the reason of the failure and what is expected to fix it. * Additional field `accessLevel` must indicate the level of access requested that caused this error (i.e. level of access backed by correspondent access headers): `client`, `chain`, or `location`. Suggested action: Fix access data and then retry. Example: ``` // 1st example { "message": "X-Client-Id is invalid", "cause": "unauthorizedAccess", "accessLevel": "client" } // 2nd example { "message": "X-Location-Secret is invalid", "cause": "unauthorizedAccess", "accessLevel": "location" } ```   #### 404: Entity Not Found Cause is `entityNotFound`. Any referenced entity or resource is not found. Requirements: * Error message must clearly indicate the reason of the failure. * Additional field `parameter` must be present. Parameter indicates which parameter of the request has wrong value that was not found. This parameter is then indicated in the documentation of respective canonical operation in section "Possible reasons of validation errors". Example: ``` { "message": "Location 'ESD3432' is not found", "cause": "entityNotFound", "parameter": "locationId" } ```   #### 404: Resource Not Found Cause is `resourceNotFound`. The requested endpoint does not exist or is not supported. Example: ``` { "message": "Requested endpoint is not supported", "cause": "resourceNotFound" } ```   #### 405: Method Not Allowed Used HTTP method is not supported for the given endpoint. For example, POST, PUT, DELETE, etc.   #### 422: Custom Cause The request was well-formed but could not be completed due to failed server-side validation checks, which can't be performed by client without server calls. This error can be used only if it is specified on operation level. Requirements: * Error message must clearly indicate the reason of the failure. * Additional field `cause` must indicate what exactly has caused this error. * Additional field `reason` must indicate the reason of the failure and is logically bound to the `cause` by extending it. * The values for `cause` and `reason` must be strictly defined at operation level. The values that are not defined at operation level must never be used. Examples: ``` // 1st example { "message": "Cannot add an Exerciser id='E67' to a Class 'Stretching' Waitlist since the Exerciser is already in this Waitlist", "cause": "addToWaitlistFailed", "reason": "userAlreadyInWaitlist" } // 2nd example { "message": "Cannot add an Exerciser id='E67' to a Class 'Stretching' Waitlist since the Exerciser is the Class Attendee already", "cause": "addToWaitlistFailed", "reason": "userAlreadyAttendee" } // 3nd example { "message": "Class 'Yoga 24/7' canot be booked by Exerciser with id='E3471' due to lack of Product to pay for the Class", "cause": "bookingFailed", "reason": "lackOfProduct" } ```   #### 429: Too Many Requests Cause is `rateLimitsExceeded`. Too many requests hit the API too quickly (Rate limit exceeded). **NOTE:** ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) If rate limits exeeded while calling an external ClubMS system, then error message must contain response returned from the external system. Example: ``` { "message": "Rate limit exceeded. External system response: 'Rate limit exceeded'", "cause": "rateLimitsExceeded" } ```   #### 500: Internal Server Error Cause is `internalUnexpectedError`. An unexpected error occurred while processing the request. Suggested action: Use exponential backoff. Detailed reason of internal server error is usually not returned in response to the client. Detailed reason (error stack trace) must be logged in internal logging system. Example: ``` { "message": "Internal Server Error: NullPointerException", "cause": "internalUnexpectedError" } ```   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) 500: External System Unexpected Response Cause is `externalSystemUnexpectedResponse`. External ClubMS system returned an unexpected response: 2xx, 3xx, 4xx, or 5xx. An unexpected response might be due to response parsing error, or due to missing mandatory field in response, or due to an unexpected error, particularly due to 400 Bad Request error, etc. But generally it might be any response: 2xx, 3xx, 4xx, or 5xx, - that is unexpected, other words expectation was that this should never happen. For example, 400 Bad Request error from external third-party system is not expected for canonical operation, because canonical operation already does all input validation and must not expect corresponding failure in external system. If this happens, then it means that some contracts are not considered by implementation of canonical operation and there a change must be performed by Professional Services Team. The reason might be an unexpected change on third-party side, or simply missing some edge case contract. On the other hand, 404 Not Found error from external third-party system is expected error in case when canonical operation knows which parameter caused this error and then it is translated into 404. But when canonical operation does not know what caused 404, then it is 500. **NOTE:** Unexpected response from an external system is always a permanent failure that requires a fix on side of implementation of canonical operation. Example: ``` { "message": "Unexpected response from External System XYZ: 404 Not Found: no such endpoint", "cause": "externalSystemUnexpectedResponse" } ```   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) 502: External System Internal Error Cause is `externalSystemInternalError`. Third-party external system error (5xx): external system returned a 500 error indicating an internal unexpected error on their side. Basically for the canonical operation this is expected outcome, which means that this is not a permanent failure. Suggested action: Use exponential backoff. Requirement: Error message sums up the cause and provides details about response from external system. This details must include the status code as returned by external system and its standard description, e.g. "500 Internal Server Error", and some other details from actual external response. Example: ``` { "message": "External System internal error. Response: 500 Internal Server Error: Nullpointer exception", "cause": "externalSystemInternalError" } ```   #### ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) 502: External System Unavailable Cause is `externalSystemUnavailable`. Third-party external system is unavailable: external system returned 503 or just refused connection (I/O error) or indicated in some other way that it is unavailable because of maintenance. Requirement: Error message sums up the cause and provides details about response from external system. This details must include the status code as returned by external system and its standard description, e.g. "503 Service Unavailable", and some other details from actual external response. In case if connection has been refused by peer, then message must contain: "Connection refused by peer". Example: ``` // 1st example { "message": "External System was unavailable. Response: Connection refused by peer", "cause": "externalSystemUnavailable" } // 2nd example { "message": "External System was unavailable. Response: 503 Service Unavailable: The server is down for maintenance until 2am PST.", "cause": "externalSystemUnavailable" } ```   #### 503: System Unavailable Cause is `systemUnavailable`. The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state. **NOTE:** ![Professional Services only](http://icons.iconarchive.com/icons/graphicloads/flat-finance/32/lock-icon.png) This status must not be used to report any issue from an external third-party system, use 502 instead. Example: ``` { "message": "Server is down for maintenance", "cause": "systemUnavailable" } ```   Version: 1.0 ## Servers ``` https://example.org/%3Cpartner%3E/group-classes/v1.0 ``` ## Download OpenAPI description [Canonical GroupX Classes API](https://developer.egym.com/_bundle/mms-blueprints/canonical-classes.yaml) ## L2 Operations related to L2 level of integration ### Search Classes - [GET /classes](https://developer.egym.com/mms-blueprints/canonical-classes/l2/searchclasses.md): This operation searches GroupX Classes by date range. It returns only brief information about GroupX Classes. #### Additional contracts N/A ### Get Class Details - [GET /classes/{classId}](https://developer.egym.com/mms-blueprints/canonical-classes/l2/getclassdetails.md): This operation returns all detailed information about GroupX Class. NOTE: !Professional Services only Implementation of this operation is mandatory. In that case if Partner API does not implement it then this implementation should be introduced on Anti-corruption layer. It could be based on cached data of getClasses call as a possible solution. #### Additional Contracts 1. Parameter exerciserId is supported only if metaOption::bookingEnabled = true. If booking is disabled, then 400 status code must be returned when exerciserId is specified. 2. If metaOption::bookingEnabled = true, parameter exerciserId is optional. If it is specified, then GroupXClassAttendeeBookingDetails is provided in response. If exerciserId is not specified, then response has no attendee booking details. ### Get Class Details - [GET /classes/{classId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/getclassdetails.md): This operation returns all detailed information about GroupX Class. NOTE: !Professional Services only Implementation of this operation is mandatory. In that case if Partner API does not implement it then this implementation should be introduced on Anti-corruption layer. It could be based on cached data of getClasses call as a possible solution. #### Additional Contracts 1. Parameter exerciserId is supported only if metaOption::bookingEnabled = true. If booking is disabled, then 400 status code must be returned when exerciserId is specified. 2. If metaOption::bookingEnabled = true, parameter exerciserId is optional. If it is specified, then GroupXClassAttendeeBookingDetails is provided in response. If exerciserId is not specified, then response has no attendee booking details. ## L3 Operations related to L3 level of integration ### Get Class Details - [GET /classes/{classId}](https://developer.egym.com/mms-blueprints/canonical-classes/l2/getclassdetails.md): This operation returns all detailed information about GroupX Class. NOTE: !Professional Services only Implementation of this operation is mandatory. In that case if Partner API does not implement it then this implementation should be introduced on Anti-corruption layer. It could be based on cached data of getClasses call as a possible solution. #### Additional Contracts 1. Parameter exerciserId is supported only if metaOption::bookingEnabled = true. If booking is disabled, then 400 status code must be returned when exerciserId is specified. 2. If metaOption::bookingEnabled = true, parameter exerciserId is optional. If it is specified, then GroupXClassAttendeeBookingDetails is provided in response. If exerciserId is not specified, then response has no attendee booking details. ### Get Class Details - [GET /classes/{classId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/getclassdetails.md): This operation returns all detailed information about GroupX Class. NOTE: !Professional Services only Implementation of this operation is mandatory. In that case if Partner API does not implement it then this implementation should be introduced on Anti-corruption layer. It could be based on cached data of getClasses call as a possible solution. #### Additional Contracts 1. Parameter exerciserId is supported only if metaOption::bookingEnabled = true. If booking is disabled, then 400 status code must be returned when exerciserId is specified. 2. If metaOption::bookingEnabled = true, parameter exerciserId is optional. If it is specified, then GroupXClassAttendeeBookingDetails is provided in response. If exerciserId is not specified, then response has no attendee booking details. ### Add Attendee to Class - [POST /classes/{classId}/attendees/{exerciserId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/addattendeetoclass.md): This operation books GroupX Class for the Attendee (Exerciser). NOTE: This operation is available only if metaOption::bookingEnabled = true (use status code 404 endpoint not supported). #### Additional Contracts N/A ### Update class booking - [PUT /classes/{classId}/attendees/{exerciserId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/updatebooking.md): This operation changes booking details of GroupX Class for the Attendee (Exerciser). NOTE: This operation is available only if metaOption::spotBookingEnabled = true (use status code 404 endpoint not supported). #### Additional Contracts N/A ### Remove Attendee from Class - [DELETE /classes/{classId}/attendees/{exerciserId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/removeattendeefromclass.md): This operation cancels GroupX Class for the Attendee (Exerciser). NOTE: This operation is available only if metaOption::bookingEnabled is true and (use status code 404 endpoint not supported). #### Additional Contracts N/A ### Add Attendee to Waitlist - [POST /classes/{classId}/waitlist/attendees/{exerciserId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/addattendeetowaitlist.md): This operation books GroupX Class Waitlist for the Attendee (Exerciser). Should be implemented in case if . NOTE: This operation is available only if metaOption::bookingEnabled is true. Use status code 404 if endpoint not supported. #### Additional Contracts N/A ### Remove Attendee from Waitlist - [DELETE /classes/{classId}/waitlist/attendees/{exerciserId}](https://developer.egym.com/mms-blueprints/canonical-classes/l3/removeattendeefromwaitlist.md): This operation cancels GroupX Class Waitlist for the Attendee (Exerciser). NOTE: This operation is available only if metaOption::bookingEnabled is true (use status code 404 endpoint not supported). #### Additional Contracts N/A ### Get Exerciser Class Schedule - [GET /exercisers/{exerciserId}/schedule](https://developer.egym.com/mms-blueprints/canonical-classes/l3/getexerciserclassschedule.md): This operation retrieves Attendee (Exerciser) Class schedule. NOTE: This operation is available only if metaOption::bookingEnabled is true (use status code 404 endpoint not supported).