# Ativiti API Document #### Base URL - https://dev.ativiti.ch/ #### Postman Collection - [Ativiti PROD.postman_collection.json](https://drive.google.com/file/d/1irhBeZikuKjFbzUeWahke4kMOnOJqonz/view?usp=sharing) # Login - [x] DONE ## Correct credentials Request: /api/v1/login_check Method: GET|POST Body: ~~~ json { "username": "user@email.com", "password": "password" } ~~~ Response: ~~~ json { "statusCode": 200, //this field can be obtained for response.status.. why to make more data flow ? "data": { "token": "eyJ0eXAiOiJKV1QiLCJhbG....." } } ~~~ ## Bad credentials Request: /api/v1/login_check Method: GET|POST Body: ~~~ json { "username": "user@email.com", "password": "WRONGpassword" } ~~~ Response: ~~~ json { "statusCode": 401, "data": { "error": "Bad credentials" } } ~~~ ## Facebook Login - [ ] DONE Expected Facebook Login Flow: Request: /api/v1/login_check/facebook Method: POST Body ~~~json { "access_token":"............................" } ~~~ Access token is obtained once user signup with facebook popup and that contain information like firstname, lastname, email, profile_picture... This access token should be submitted to facebook graph api to extract information Response For success: ~~~json { "statusCode": 200, "data": { "token": "..." } } ~~~ For Facebook Graph not return any information ~~~json { "statusCode": 400, "data": { "error": "Invalid access token" } } ~~~ For user not registered ~~~json { "statusCode": 400, "data": { "error": "Information not found in database, please signup" } } ~~~ --- # Sign up ## Sending signup form - [x] DONE ### Ex1. Email Signup #### Step 1: Request: /api/v1/signup/email method: POST Body: ~~~ json { "email": "test@email.com", "is_adult": true, "subscribe_newsletter": true, "first_name": "Alice", "last_name": "Bob", "password": "******", "area": "region.geneva|region.lausanne" } ~~~ Response: ~~~ json { "statusCode": 201, "data": { "message": "Check email for 4 digit code" } } ~~~ #### Step 2: Request: /api/v1/signup/validate method: POST Body: ~~~ json { "email": "smth@gmail.com", "four_digit_code": 1234 } ~~~ Response: For Correct Code ~~~ json { "statusCode": 200, //this field can be obtained for response.status.. "data": { "message": "Code verified. User activated and logged in.", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ...." } } ~~~ For Invalid Code ~~~ json { "statusCode": 400, "data": { "message": "Invalid code.", "attempts_left": 2 } } ~~~ ~~~ json { "statusCode": 400, "data": { "message": "You entered the code incorrectly 3 times. The new code has been sent to the email" } } ~~~ For an already active user ~~~ json { "statusCode": 404, "data": { "error": "User with provided email already activated" } } ~~~ ### Ex2. Facebook Signup - [ ] DONE Expected Facebook Signup Flow: Request: /api/v1/signup/facebook Method: POST Body ~~~json { "is_adult": true, "subscribe_newsletter": true, "access_token":"..........................." } ~~~ Access token is obtained once user signup with facebook popup and that contain information like firstname, lastname, email, profile_picture... This access token should be submitted to facebook graph api to extract information Response For success: ~~~json { "statusCode": 200, "data": { "message": "Signup successful" } } ~~~ For Facebook Graph not return any information ~~~json { "statusCode": 400, "data": { "error": "Invalid access token" } } ~~~ For user already registered ~~~json { "statusCode": 400, "data": { "error": "Already registered, please login" } } ~~~ --- # Change Password ## Change password from settings page - [x] DONE Request: /api/v1/security/update_password method: POST ### Headers: **Authorization**: `Bearer eyJ0eXAiOiJKV1QiLC...` Body: ~~~ json { "current_password":"44zHHN8!!TSXhU^HDwHV", "new_password":"%@*2&ZxeFMFwC&A2Pf^8" } ~~~ Response: ### Ex1. Successfully changed ~~~ json { "statusCode": 200, "data": { "message": "Password updated." } } ~~~ ### Ex2. Error in changing password ~~~ json { "statusCode": 403, "data": { "error": "Error message" } } ~~~ # Reset Password ## Sending reset code - [x] DONE ### Ex1. Correct email #### Step 1: Request: /api/v1/security/reset method: POST Body: ~~~ json { "email": "test@email.com" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "message": "Check email for 4 digit code" } } ~~~ #### Step 2: Request: /api/v1/security/validate method: POST Body: ~~~ json { "four_digit_code": "1234" } ~~~ ***Correct Format*** ~~~ json { "email": "smth@gmail.com", "four_digit_code": 1234 } ~~~ Response: For Correct Code ~~~ json { "statusCode": 200, "data": { "message": "Code verified.", "change_password_token": "sometoken", "token_ttl_seconds": "900" } } ~~~ For Invalid Code ~~~ json { "statusCode": 400, "data": { "message": "Invalid code." "attempts_left": 10 } } ~~~ ~~~ json { "statusCode": 400, "data": { "message": "You entered the code incorrectly 10 times. The code has been canceled. Resend the code." } } ~~~ ~~~ json { "statusCode": 404, "data": { "error": "User with provided email not request the token" } } ~~~ ~~~ json { "statusCode": 400, "data": { "message": "The request code is expired. Please request a new one.", "token_ttl_seconds": 900 } } ~~~ #### Step 3: Request: /api/v1/security/recover_password method: POST Body: ~~~ json { "token": "sometoken", "new_password": "******" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "message": "Password updated." } } ~~~ For Invalid Code ~~~ json { "statusCode": 400, "data": { "message": "The change password token is expired.", "token_ttl_seconds": 900 } } { "statusCode": 400, "data": { "message": "Password is too short" } } ~~~ ### Ex2. Repeated request when the delay time has not expired: Request: ~~~ json { "email": "test@email.com" } ~~~ Response: ~~~ json { "statusCode": 400, "data": { "message": "The password reset link was sent to the email earlier", "seconds_left": 7089 } } ~~~ ### Ex3. Not valid email: Request: ~~~ json { "email": "not_valid_email" } ~~~ Response: ~~~ json { "statusCode": 400, "data": { "error": "Parameter \"email\" of value \"not_valid_email\" violated a constraint \"Cette valeur n'est pas une adresse email valide.\"" } } ~~~ ### Ex4. User not found: Request: ~~~ json { "email": "some_other@email.com" } ~~~ Response ~~~ json { "statusCode": 404, "data": { "error": "User with provided email not found" } } ~~~ --- # Home ## Home View - [x] DONE ### Default data loaded with homepage **all_categories** section in the response shall include all the available categories and sub-categories in the backend for use in the search filter. Request: /api/v1/home method: GET Body: ~~~ json { } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "has_upcoming_events": null, "category": [ { "id": 1, "title": "Sport", "count": 9, "icon": "https://dev.ativiti.ch/uploads/category/sport.jpg" }, { "id": 6, "title": "Dancing", "count": 9, "icon": "https://dev.ativiti.ch/uploads/category/dancing.jpg" }, { "id": 11, "title": "Game", "count": 21, "icon": "https://dev.ativiti.ch/uploads/category/game.jpg" }, { "id": 14, "title": "Business", "count": 9, "icon": "https://dev.ativiti.ch/uploads/category/business.jpg" } ], "all_categories": { "Sport": [ "Chessy" ], "Dancing": [ "Pasodoble" ], "Game": [ "Laser tag" ], "Business": [ "Seminar", "Conference", "Workshop", "Chessy", "Paintball", "Samba", "Zumba", "Tango", "Kicker (Table football)", "Darts", "Swimming", "Judo" ] }, "discover": [ { "id": 14, "title": "Dance", "venue": "1400, Yverdon-les-Bains", "address": "Route de Lausanne, Pâquis-Nations, Швейцария, 1400, Yverdon-les-Bains, Швейцария", "image": "https://provideradmin-dev.ativiti.ch/uploads/gallery/5f11aa2e52400.png" } ], "keepfit": [ { "id": 14, "title": "Dance", "venue": "1400, Yverdon-les-Bains", "address": "Route de Lausanne, Pâquis-Nations, Швейцария, 1400, Yverdon-les-Bains, Швейцария", "image": "https://provideradmin-dev.ativiti.ch/uploads/gallery/5f11aa2e52400.png" } ], "recreational": [ { "id": 14, "title": "Dance", "venue": "1400, Yverdon-les-Bains", "address": "Route de Lausanne, Pâquis-Nations, Швейцария, 1400, Yverdon-les-Bains, Швейцария", "image": "https://provideradmin-dev.ativiti.ch/uploads/gallery/5f11aa2e52400.png" } ], "inspiring": [ { "id": 14, "title": "Dance", "venue": "1400, Yverdon-les-Bains", "address": "Route de Lausanne, Pâquis-Nations, Швейцария, 1400, Yverdon-les-Bains, Швейцария", "image": "https://provideradmin-dev.ativiti.ch/uploads/gallery/5f11aa2e52400.png" } ] } } ~~~ ___ ## Last minute activities (fig no: 8.16) ### Ex1. Get event sessions for listing based on user location for all categories - [x] DONE Here the user is geolocated so the search is performed on a radius of 2km from the current position. Request: /api/v1/event_list method: POST Body: ~~~ json { //"area": "", "distance_limit": "2", "date": "", //"start_time": "", //"stop_time": "", "current_lat": "27.717245", "current_long": "85.323959", "type": "sessions", "categories": [], "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "sessions", "sorting": "time", "active_booking_count": 3, "event_count": 15, "events": [ { "id": 1, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "waiting list", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ] } } ~~~ ### Ex2. Get free entries for listing based on user location and time - [x] DONE Here we added values to the date and time parameters. This will limit the search to specific date and search for events that begin and within the time range selected by user. Request: /api/v1/event_list method: POST Body: ~~~ json { "area": "", "distance_limit": "2", "date": "12-06-2019", "start_time": "12:00:00 UTC + 5:45", "stop_time": "16:00:00 UTC + 5:45", "current_lat": "27.717245", "current_long": "85.323959", "type": "free_entries", "categories": [], "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "free_entries", "sorting": "time", "active_booking_count": 3, "event_count": 15, "events": [ { "id": 1, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booking request", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "booked", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ] } } ~~~ ### Ex3. No events found based on user location and time - [ ] DONE Here distance limit is also set to 10 km. So the event venue should be within 10km from the current location of the user. Request: /api/v1/event_list method: POST Body: ~~~ json { "area": "", "distance_limit": 400.0, "date": "", "start_time": "", "stop_time": "", "current_lat": "8.539865", "current_long": "47.329282", "type": "free_entries", "categories": [], "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "2020-09-02", "type": "free_entries", "sorting": "time", "active_user_booking_count": 0, "event_count": 1, "events": [ { "id": 186, "event_date": "02-09-2020", "start_time": "19:00:00", "stop_time": "21:00:00", "title": "Tomfoolery", "venue": "Route de Lausanne, Pâquis-Nations, Швейцария, 1400, Yverdon-les-Bains, Швейцария", "address": "1400, Yverdon-les-Bains", "is_favourite": false, "booking_status": "", "distance_from_location": "136.4 km", "event_lat": "6.6468802", "event_long": "46.7632507" } ] } } ~~~ ___ ## Explore activities (fig no: 8.15) ### Ex1. Get event sessions for listing based on user location - [x] DONE Here the sorting is set to distance instead of time. Request: /api/v1/event_list method: POST Body: ~~~ json { "area": "", "distance_limit": "2", "date": "", "start_time": "", "stop_time": "", "current_lat": "27.717245", "current_long": "85.323959", "type": "sessions", "categories": [], "sorting": "distance" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "sessions", "sorting": "distance", "active_booking_count": 3, "event_count": 15, "events": [ { "id": 1, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "booked", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ] } } ~~~ * P.S. Also has "type"="free_entries" (Ref: Ex2. of previous section) ___ ## Category filter and Search ### Ex1. Get event sessions for listing based on user location, time and category selection (fig no: 8.18 - 8.22) - [x] DONE Uses the same request pattern as above sections with categories selected. In this example all events in **sports** category that fall under the location and time filters will be selected. Also we consider that the user is **not geo-located** in this session and search by **area**. Request: /api/v1/event_list method: POST Body: ~~~ json { "area": "geneva", "distance_limit": "", "date": "", "start_time": "", "stop_time": "", "current_lat": "", "current_long": "", "type": "sessions", "categories": [{ "sports": [] }], "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "sessions", "sorting": "time", "active_booking_count": 3, "event_count": 15, "events": [ { "id": 1, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "declined", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ] } } ~~~ ### Ex2. Get event sessions based on search filter (fig no: 8.07) - [x] DONE In this example all events in **culture** category and **cardio sculpt** or **yoga** events from **sports** category, that fall under the location and time filters will be selected. Here we consider that the user is geo-located and hence search by the distance_limit from current location. Request: /api/v1/event_list method: POST Body: ~~~ json { "area": "geneva", "distance_limit": "10", "date": "", "start_time": "", "stop_time": "", "current_lat": "27.717245", "current_long": "85.323959", "type": "sessions", "categories": [{ "sports": [ "cardio sculpt", "yoga" ], "culture": [] }], "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "sessions", "sorting": "time", "active_booking_count": 1, "event_count": 15, "events": [ { "id": 1, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "cancelled", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ] } } ~~~ ___ # Agenda ## Upcoming Events (fig no: 11.01) - [x] DONE ![](https://i.imgur.com/1EpOK0V.png) ### Ex1. Get event details for listing based on user booking status Request: /api/v1/agenda method: POST Body: ~~~ json { "type": "upcoming", "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "upcoming", "sorting": "time", "active_booking_count": 3, "event_count": 15, "events": [ { "id": 1, "event_date": "21-06-2019" "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "event_date": "21-06-2019" "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "waiting list", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ], "notifications": [] } } ~~~ ### Ex2. Notification from provider (fig no: 11.01) - [x] DONE ![](https://i.imgur.com/aNbdDVT.png) In this case, the new event date does not conflict with any other confirmed bookings of the user. The user can either **accept** or **decline** the change in schedule. Request: /api/v1/agenda method: POST Body: ~~~ json { "type": "upcoming", "sorting": "time" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "upcoming", "sorting": "time", "active_booking_count": 3, "event_count": 15, "events": [], "notifications": [{ "events": [{ "id": 1, "old_event_date": "21-06-2019" "old_start_time": "12:00:00", "old_stop_time": "12:45:00", "new_event_date": "23-06-2019" "new_start_time": "12:00:00", "new_stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }] }] } } ~~~ #### Case 1: - [x] DONE **User accepts the new time for the event** Request: /api/v1/agenda/sessionTimeUpdate method: POST Body: ~~~ json { "event_id": 1, "booking": "accept", "new_event_date": "02-09-2020", "new_start_time": "12:00:00" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "upcoming", "sorting": "time", "event_count": 15, "events": [{ "id": 1, "event_date": "21-06-2019" "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }], } } ~~~ Error: ~~~ json { "statusCode": 400, "data": { "error": "The requested event does not exist" } } { "statusCode": 400, "data": { "error": "The requested bookingRequest does not exist" } } { "statusCode": 200, "data": { "error": "changes are out of date, refresh page" } } ~~~ #### Case 2: - [x] DONE **User declines the new time for the event** Request: /api/v1/agenda/sessionTimeUpdate method: POST Body: ~~~ json { "event_id": 1, "booking": "decline", "new_event_date": "23-06-2019", "new_start_time": "12:00:00" } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "message": "Confirm to remove session" } } ~~~ **On user confirmation** Request: /api/v1/agenda/sessionTimeUpdate method: POST Body: ~~~ json { "event_id": 1, "booking": "decline", "confirmation": true } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "message": "Your session has been cancelled successfully." } } ~~~ ### Ex3. Cancelling session by client (fig no: 11.02,11.03) - [x] DONE ![](http://i.imgur.com/NTY1sSH.png) When a client wants to cancel a request and confirms, the system itself will determine which status to assign and whether cancellation is possible and return type of cancellation in “reason” field (“сanceled” with a regular cancellation and “late” with a late cancellation). Request: /api/v1/agenda/cancel_session method: POST Body: ~~~ json { "requestId": 1 } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "reason": "canceled" } } ~~~ Error: ~~~ json { "statusCode": 400, "data": { "message": "This request cannot be cancelled" } } ~~~ ### Ex4. History - [x] DONE ![](https://i.imgur.com/k3ePGZZ.png) Request: /api/v1/agenda/cancel_reasons method: POST Body: ~~~ json { "event_id": 1 } ~~~ Response: ~~~ json If provider cancels the event { "statusCode": 200, "data": { "cancellation_type": "cancelled_by_provider", "message": "Your session was cancelled by provider", "message_from_provider": "Here is message from provider" } } If Ativiti was forced to cancel the event { "statusCode": 200, "data": { "cancellation_type": "force_cancellation", "message": "Your session was cancelled by Ativiti", } } If event date changed and conflicts with existing booking { "statusCode": 200, "data": { "cancellation_type": "new_schedule_conflict", "message": "New session time conflicts with existing booking", } } ~~~ ## History Events (fig no: 11.13) ### Get event details for listing based on user history - [x] DONE Request: /api/v1/agenda method: POST Body: ~~~ json { "type": "history", "sorting": "time" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "date": "17-06-2019", "type": "history", "sorting": "time", "event_count": 15, "events": [ { "id": 1, "event_date": "21-06-2019" "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "completed", "distance_from_location": "0.3 km", "event_lat": "27.717245", "event_long": "85.323959" }, { "id": 2, "event_date": "21-06-2019" "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Bowling", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": true, "booking_status": "completed", "distance_from_location": "0.2 km", "event_lat": "27.717245", "event_long": "85.323959" } ] } } ~~~ ### Remove history event (fir. 11.13) - [x] DONE DONE Request: /api/v1/agenda/remove_history_event method: POST Body: ~~~ json { "eventId": 100, } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "succeed": true, "message": "The event has been hidden", "errors": [reference link] } } ~~~ Bad Response: ~~~ json { "statusCode": 200, "data": { "succeed": false, "message": "", "errors": [ "An error occurred during hiding the event" ] } } ~~~ # ID Register ## User ID registration required for booking events. (fig no: 6.03, 7.01) - [x] DONE ### User Detail Submission Here the the user adds name as in their ID and selects a picture fit for ID. The image data will be **base64 encoded** and the payload will be **json multipart/form-data**. After successful registration the user will get a **referral code** that can be shared with friends. Along with referral code the **number of times the referral has been redeemed** is also returned. Request: /api/v1/id_register method: POST Body: ~~~ json { "id_first_name": "upcoming", "id_last_name": "time", "acknowledge_is_true": true, "id_image": "c2Zhc2ZzYWRmc2RmYWFzZGY=" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "message": "Your ID information has been successfully saved.", "referral_code": "HG345JADE", "redeem_count": 0 } } ~~~ > Bad Response: ~~~ json { "statusCode": 403, "error": "Error You did not give consent" } ~~~ ### State Get Request: - [x] DONE Request: /api/v1/id_register/get_state method: POST Body: ~~~ json { } ~~~ Response: ~~~ json { "statusCode": 200, "data": { "id_first_name": "OL1", "id_last_name": "SH2", "id_image": "c2Zhc2ZzYWRmc2RmYWFzZGY=", "acknowledge_is_true": true } } ~~~ # Activity Page (fig no: 12.04) ## Get activity detail and user booking status with all information - [x] DONE Request: /api/v1/activity_detail method: POST ### Headers: **Authorization**: `Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ...` Body: ~~~ json { "activity_id":10, //Primary Key of the event_id } ~~~ > Response: ### Ex1. Successfully fetched activity detail and booking status ~~~ json { "statusCode": 200, "data": { "activity_id": 2, "event_date": "02-09-2020", "start_time": "08:00:00", "stop_time": "09:00:00", "title": "wfdsgdfg", "image_urls": [ "/uploads/gallery/5f1a89ba6b38a.png" ], "venue": "Route de Lausanne, Pâquis-Nations, Швейцария, 1400, Yverdon-les-Bains, Швейцария", "address": "1400, Yverdon-les-Bains", "is_favourite": false, "booking_status": "reservation_period_expired", "is_free_entry": false, "distance_from_location": "0.0 km", "event_lat": "6.6468802", "event_long": "46.7632507", "social_share_link": "http://this-is-event-link.com", "description": "dfgdsfgsac aSCSac aC", "attendees_count": 0, "cancellation_deadline": "time expired", "difficulty_level": "For everyone", "conditions": "Min 0 people coming", "other_info": "", "other_sessions": [ { "id": 18, "event_date": "02-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 3, "event_date": "03-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 4, "event_date": "04-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 19, "event_date": "04-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 5, "event_date": "05-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 6, "event_date": "06-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 7, "event_date": "07-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 20, "event_date": "07-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 8, "event_date": "08-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 9, "event_date": "09-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 21, "event_date": "09-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 10, "event_date": "11-09-2020", "start_time": "08:00:00", "booking_status": "available" }, { "id": 22, "event_date": "11-09-2020", "start_time": "08:00:00", "booking_status": "available" } ], "amenities": [] } } ~~~ ### Ex2. Error in fetching activity detail and user booking status ~~~ json { "statusCode": 403, "data": { "error": "Error message" } } ~~~ # Book Activity ## Book an activity when status is available - [x] DONE Request: /api/v1/activity_book method: POST ### Headers: **Authorization**: `Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ...` Body: ~~~ json { "activity_id":10, //Primary Key of the activity "number_of_friends":4 } ~~~ > Response: ### Ex1. Successfully booked activity. Returns attendees_list and all hidden data if any ~~~ json { "statusCode": 200, "data": { "id": 10, "event_date": "21-06-2019" "start_time": "12:00:00", "stop_time": "12:45:00", "title": "Cardio Sculpt", "venue": "Bowland Lausanne-Flon", "address": "1003, Lausanne", "is_favourite": false, "booking_status": "booked", "is_free_entry": true "distance_from_location": "0.3 km", "event_lat": 27.717245, "event_long": 85.323959, "social_share_link": "http://this-is-event-link.com", "description": "This is description", "attendees_count": 32, "attendees_list": [ { "profile_name": "Tom H.", // Firstname L. ; lastname initial only "preview_img": "http://link-to-user-thumb.com" }, { ... } ], "cancellation_deadline": "34 minutes", //Prepared string in backend "difficulty_level": "Intermediate", "conditions": "Min 3 people coming", "other_info": "This is something additional", "other_sessions": [ //list of 10 days including today { "id": 1, "event_date": "21-06-2019" "start_time": "12:00:00", "booking_status": "available", }, { "id": 2, "event_date": "21-07-2019" "start_time": "12:00:00", "booking_status": "waiting list", } ], "amenities": [ { "name": "Shower", "is_paid": true }, { "name": "Changing room", "is_paid": false }, { "name": "Coffee", "is_paid": true }, ] } } ~~~ ### Ex2. Booking failed due to user subscription status or session conflict ~~~ json { "statusCode": 403, "data": { "failure_code": "session_coflict" // subscription_paused / no_pass / subscription_canceled } } ~~~ # Membership - [x] Done ## list of existing membership plans - [x] Done **Description**: Returns common information about membership actions * list of allowed actions; * active subscription of current user (if exist); * list of existing membership plans. All actions require authorization header - see Login block ### Headers: **Authorization**: `Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ...` **Request**: /api/v1/membership **method**: GET **Body**: none **Response**: ~~~ json { "statusCode": 200, "data": { "succeed": true, "allowedActions": [ "pause", "unsubscribe", "unpause", "re-subscribe" ], "activeSubscription": { "planId": 1, "active": true, "expireDate": { "date": "2020-05-26 14:18:51.000000", "timezone_type": 3, "timezone": "UTC" }, "isPaused": false, "isCanceled": false }, "planList": [ { "planId": 1, "name": "Trial", "price_per_month": 7900, "currency": "chf" }, { "planId": 3, "name": "Adventure", "price_per_month": 19900, "currency": "chf" } ] } } ~~~ **Bad Response**: none ## Subscription Plan Info ### Headers: **Authorization**: `Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJ...` **Request**: /api/v1/membership/plan_info **method**: POST Params: 1. subscriptionPlan required: required type: integer description: Membership plan ID to subscribe. Check Membership section to see available plans 2. couponId required: optional type: string default: null description: Discount coupon that has to apply on subscription. Use this param to get actual amount of subscription plan **Body**: example with existing card: ~~~ json { "subscriptionPlan": 3 } ~~~ ## Subscribe (fig. 5.07, 5.18) - [x] Done Description: Subscribe user to provided membership plan Request: api/v1/membership/subscribe method: POST Params: 1. subscriptionPlan required: required type: integer description: Membership plan ID to subscribe. Check Membership section to see available plans 2. stripeToken required: optional type: string default: null description: Provide this token if you need to add card during susbcribing 3. cardId required: optional type: integer default: null description: Use this parameter if you need to pass existing card as payment tool. See card list action to get card IDs 4. couponId required: optional type: string default: null description: Discount coupon that has to apply on subscription 5. agree required: optional type: boolean values: true or false. default: false description: This flag means that an user accept with T&C rules on subcription page **Body**: ~~~ json Body, example with existing card: { "subscriptionPlan": 1, "cardId": 12 } Body, example with stripe card token: { "subscriptionPlan": 1, "stripeToken": "asdasfdajassda" } Success Response: { "statusCode": 200, "data": { "succeed": true, "errors": [], "message": "Subscribed" } } Response without agree flag: { "statusCode": 200, "data": { "succeed": false, "errors": [ "You did not accept the agreement" ], "message": "You did not accept the agreement" } } Response without cardID or Stripe token: { "statusCode": 403, "data": { "succeed": false, "message": "Payment info is required - provide stripe token of card ID" "errors": [ "Payment info is required - provide stripe token of card ID" ] } } Response with incorrect membership plan ID: { "statusCode": 403, "data": { "succeed": false, "message": "Plan does not exist" "errors": [ "Plan does not exist" ] } } ~~~ ## Pause (fig. 18.73) - [x] Done Description: Sets the user’s active membership plan on pause. **Request**: api/v1/membership/pause method: POST **Params**: 1. confirm required: optional type: boolean values: true or false. default: false description: Uses to get some data from the backend to display it on confirm popup (see. Response for confirm block). Skip this parameter to pause membership **Body**: ~~~ json Body, example with “confirm” param: { "confirm": "true" } Body, to pause membership empty Response for confirm: this example shows how to get an amount of penalty fee { "statusCode": 200, "data": { "succeed": true, "errors": [], "confirm": { "penaltyFee": 15 }, "message": "Info for confirm dialog" } } Response: { "statusCode": 200, "data": { "succeed": true, "errors": [], "message": "Membership has been paused" } } Bad Response: { "statusCode": 200, "data": { "succeed": false, "errors": [ "An error occurred during activating membership" ], "message": "An error occurred during pausing membership" } } Bad Response 2: { "statusCode": 403, "data": { "succeed": false, "errors": [ "User does not exist or security token is not valid", "User does have active subscription" ] } } ~~~ ## UnPause (fig. 18.33) - [x] Done **Description**: Takes off the user’s membership plan from the pause. **Request**: /api/v1/membership/unpause **method**: POST **Params**: 1. confirm required: optional type: boolean values: true or false. default: false description: Uses to get some data from the backend to display it on confirm popup (see. Response for confirm block). Skip this parameter to unpause membership **Body**: ~~~ json example with “confirm” param: { "confirm": "true" } Body, to unpause membership empty Response for confirm: there is no additional data for confirmation popup. Response: { "statusCode": 200, "data": { "succeed": true, "errors": [], "message": "Membership has been activated" } } Bad Response: { "statusCode": 200, "data": { "succeed": false, "errors": [ "An error occurred during activating membership" ], "message": "An error occurred during activating membership" } } Bad Response 2: { "statusCode": 403, "data": { "succeed": false, "errors": [ "User does not exist or security token is not valid", "User does have active subscription" ] } } ~~~ ## UnSubscribe (fig. 18.34, 18.35) - [x] Done **Description**: Cancel active membership. **Request**: /api/v1/membership/unsubscribe **method**: POST **Params**: 1. confirm required: optional type: boolean values: true or false. default: false description: Uses to get some data from the backend to display it on confirmation popup (see. Response for confirm block). Skip this parameter to cancel membership 2. cancel_reason required: optional type: string description: Reason of cancel subscription. 3. cancel_message required: optional type: string description: Message of subscription canceling. ~~~ json Body, example with “confirm” param: { "confirm": "true" } Body, to cancel membership empty Response for confirm: this example shows how to get the end date of current subscription { "statusCode": 200, "data": { "succeed": true, "errors": [], "confirm": { "membershipExpireDate": "May 8, 2020" }, "message": "Info for confirm dialog" } } Response: { "statusCode": 200, "data": { "succeed": true, "errors": [], "message": "Membership has been canceled" } } Response 3 (we cannot cancel the “Trial” plan): { "statusCode": 403, "data": { "succeed": false, "errors": [ "This plan can not be canceled" ], "message": "This plan can not be canceled" } } Bad Response: { "statusCode": 200, "data": { "succeed": false, "errors": [ "An error occurred during canceling membership" ], "message": "An error occurred during canceling membership" } } Bad Response 2: { "statusCode": 403, "data": { "succeed": false, "errors": [ "User does not exist or security token is not valid", "User does have active subscription" ] } } ~~~ ## ReSubscribe (fig. 18.37 - [x] Done **Description**: Re-activate canceled membership. **Request**: /api/v1/membership/resubscribe **method**: POST **Params**: 1. confirm required: optional type: boolean values: true or false. default: false description: Uses to get some data from the backend to display it on confirmation popup (see. Response for confirm block). Skip this parameter to re-activate membership ~~~ json Body, example with “confirm” param: { "confirm": "true", } Body, to cancel membership empty Response for confirm: there is no additional data for confirmation popup. Response: { "statusCode": 200, "data": { "succeed": true, "errors": [], "message": "Membership has been activated" } } Response 2 (the current date is more than the expiration date of subscription - an user needs to buy a new subscription): { "statusCode": 200, "data": { "succeed": false, "errors": [ "Your membership is expired and you need to subscribe it again" ], "message": "Your membership is expired and you need to subscribe it again" } } Response 3 (we cannot cancel the “Trial” plan): { "statusCode": 403, "data": { "succeed": false, "errors": [ "This plan can not be re-activated" ], "message": "This plan can not be re-activated" } } Bad Response: { "statusCode": 200, "data": { "succeed": false, "errors": [ "An error occurred during activating membership" ], "message": "An error occurred during activating membership" } } Bad Response 2: { "statusCode": 403, "data": { "succeed": false, "errors": [ "User does not exist or security token is not valid", "User does have active subscription" ] } } ~~~ # Coupons - [x] Done **Description**: Displays information about referral coupon of user. All actions require authorization header - see Login block **Request**: /api/v1/coupon **method**: GET **Params**: none **Body**: empty ~~~ json Success Response: { "statusCode": 200, "data": { "succeed": true, "couponId": "ASDASDA", "redeemed": 4, "status": true // TRUE if coupon is redeemed less than 6 times else FALSE } } Bad Response: none ~~~ ## Coupon check - [x] Done Description: Displays list of user cards Request: /api/v1/coupon/check method: POST Params: 1. couponId required: required type: string description: coupon 2. subscriptionPlan required: required type: integer description: Membership plan ID to apply coupon. Check Membership section to see available plans **Body**: ~~~ json { "couponId": "7eoT1W31", "subscriptionPlan": 2 } ~~~ ~~~ json Success Response: { "statusCode": 200, "data": { "valid": true, "coupon": { "id": "BMAjTqmD", "current_amount": 19900, "new_amount": 15920, "currency": "chf" } } } Bad Response: { "statusCode": 200, "data": { "valid": false, "coupon": [] } } Bad Response 2: { "statusCode": 400, "data": { "succeed": false, "errors": [ "Coupon ID or subscription plan ID is not correct" ] } } ~~~ # Cards - [x] Done **Description**: Displays common information about payment card actions: allowed card actions, default card of user All actions require authorization header - see Login block **Request**: /api/v1/card **method**: GET **Params**: none **Body**: empty Success Response: ~~~ json { "statusCode": 200, "data": { "succeed": true, "allowedActions": [ "card/list", "card/add", "card/delete", "card/set_default" ], "defaultCard": { "cardId": 12, "cardBand": "Visa", "lastFour": "4242" } } } Bad Response: none ~~~ ## Card List (fig. 18.17) - [x] Done **Description**: Displays list of user cards **Request**: /api/v1/card/list **method**: GET **Params**: none **Body**: empty **Success Response**: ~~~ json { "statusCode": 200, "data": { "succeed": true, "cards": [ { "cardId": 11, "cardBand": "Visa", "lastFour": "4242", "default": false }, { "cardId": 12, "cardBand": "Visa", "lastFour": "4242", "default": true } ], "errors": [reference link] } } Bad Response: none ~~~ ## Card Add (fig. 18.13, 18.20) - [x] Done **Description**: Add new card in Stripe and the database **Request**: /api/v1/card/add **method**: POST **Params**: 1. stripe_token required: required type: string description: Stripe card token that comes from Stripe side 2. set_card_default required: optional type: boolean default: true description: Makes a new card as default payment card **Body**: ~~~ json { "stripe_token": "adsdasdasd", "set_card_default": true } ~~~ **Success Response**: ~~~ json { "statusCode": 200, "data": { "succeed": true, "message": "The card has been added", "card": [ "cardId": 11, "cardBand": "Visa", "lastFour": "4242", "default": true ], "errors": [reference link] } } ~~~ **Bad Response**: ~~~ json { "statusCode": 200, "data": { "succeed": false, "message": "An error occurred during adding the card", "errors": [ "An error occurred during adding the card" ] } } ~~~ ## Card Delete (fig. 18.26) - [x] Done Description: Delete card from Stripe and the database Request: /api/v1/card/delete method: POST Params: 1. cardId required: required type: integer description: Card ID in the database - see card list action Body: ~~~ json { "cardId": 11 } Success Response: { "statusCode": 200, "data": { "succeed": true, "message": "The card has been deleted", "errors": [reference link] } } Bad Response: { "statusCode": 200, "data": { "succeed": false, "message": "An error occurred on card delete", "errors": [ "An error occurred on card delete" ] } } Bad Response 2: { "statusCode": 200, "data": { "succeed": false, "message": "The card was not found", "errors": [ "The card was not found" ] } } Bad Response 3: { "statusCode": 400, "data": { "succeed": false, "message": "Card ID is not valid", "errors": [ "Card ID is not valid" ] } } ~~~ ## Set default (fig. 18.23) - [x] Done Description: Makes a card as default payment tool. Request: /api/v1/card/set_default method: POST Params: 1. cardId required: required type: integer description: Card ID in the database - see card list action ~~~ json Body: { "cardId": 11 } Success Response: { "statusCode": 200, "data": { "succeed": true, "message": "The card has been updated", "errors": [reference link] } } Bad Response: { "statusCode": 200, "data": { "succeed": false, "message": "An error occurred on card update", "errors": [ "An error occurred on card update" ] } } Bad Response 2: { "statusCode": 200, "data": { "succeed": false, "message": "The card was not found", "errors": [ "The card was not found" ] } } Bad Response 3: { "statusCode": 400, "data": { "succeed": false, "message": "Card ID is not valid", "errors": [ "Card ID is not valid" ] } } ~~~ # BUYING A PASS - [x] DONE diagram Customer App wireframes_CLEAN VERSION v11 (fig no: 5.00) ![](http://i.imgur.com/1Og4eDN.png) ## Apply Coupon (fig no: 5.02) - [x] DONE Request: /api/v1/subscribe/check_coupon method: POST Body: ~~~ json { "couponId": "BMAjTqmD", "subscription_plan": "1" //PLAN_FREE = 0; PLAN_TRIAL = 1; PLAN_ADVENTURE = 3; PLAN_PENALTY = 99; } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "valid": true, "coupon": { "id": "BMAjTqmD", "current_amount": 19900, "new_amount": 15920, "currency": "chf" } } } ~~~ > Bad Response: ~~~ json { "statusCode": 200, "data": { "valid": false, "coupon": [] } } ~~~ ## Edit my profiles (fig no: 5.04) - [x] DONE Request: /api/v1/subscribe/edit_my_profile method: POST Body: ~~~ json { "first_name": "Marco", "last_name": "Tamburrino", "email": "marco.t@gmail.com", "sex": "1",//1 = Male, 0 = female "date_of_birth": "1980-12-02", "address": "Dents de morci", "street": "Tambarrino", "city": "Munich", "zip_code": "007889", "country": "CH" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "message": "Profiler updated successfully" } } ~~~ > Bad Response: ~~~ json { "statusCode": 400, "data": { "error": "Data is not valid" } } ~~~ ## Current subscription method (fig no: 5.18) - [x] DONE Request: /api/v1/subscribe/current_subscription_method method: POST ### Show default card Body: ~~~ json { "couponId": "BMAjTqmD", "subscription_plan": "1" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "card": { "card_last_four": "4444", "card_band": "MasterCard", "card_expire_month": 4, "card_expire_year": 2022, "card_reference": "card_1GL3ofIyGRXowT46fF2EGJEV", "is_default_card": true }, "subscription_start_date": "10/03/2020", "cancellation_period": "10 Days", "price_per_month": 19900, "discounted_price_per_month": 15920, "currency": "chf" } } ~~~ > Bad Response: ~~~ json { "statusCode": 403, "data": { "message": "Default card is absent" } } ~~~ ### Selected plan Body: ~~~ json { "couponId": "BMAjTqmD", "subscription_plan": "1", "agree": "true" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "message": "Subscribed" } } ~~~ > Bad Response: ~~~ json { "statusCode": 400, "data": { "error": "The problem with the selected plan or with the stripe token" } } ~~~ ## Add subscription card (fig no: 5.07) - [x] DONE Request: /api/v1/subscribe/add_subscription_card method: POST ### The strip token is not transferred, and the coupon is optional Body: ~~~ json { "couponId": "BMAjTqmD", "subscription_plan": "1" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "subscription_start_date": "10/03/2020", "cancellation_period": "10 Days", "price_per_month": 7900, "discounted_price_per_month": 6320, "currency": "chf" } } ~~~ > Bad Response: ~~~ json { "statusCode": 400, "data": { "error": "Notice: Undefined index" } } ~~~ ### The strip token, agree and subscription plan are transferred, and the coupon is optional Body: ~~~ json { "couponId": "BMAjTqmD", "subscription_plan": "1", "stripe_token": "tok_1GLBlBIyGRXowT46niPwn6L6", "agree": "true" } ~~~ > Response: ~~~ json { "statusCode": 200, "data": { "message": "Subscribed", } } ~~~ > Bad Response 1: ~~~ json { "statusCode": 400, "data": { "error": "The problem with the selected plan or with the stripe token" } } ~~~ > Bad Response 2: ~~~ json { "statusCode": 403, "data": { "error": "The problem with the selected plan or with the stripe token" } } ~~~ # QR Code & Access Validation - [x] Done Request: /api/v1/qr_code/access_validation Method: POST Headers: Authorization: Bearer eyJ0eXAiOiJKV1QiLC... ~~~ json Body: { "provider_id": "CH-59676" } Response: { "statusCode": 200, "data": { "booking_id": 2, "ev_id": 370, "activity_name": "Activity 1 P1", "start_date": { "date": "2020-03-25 19:00:00.000000", "timezone_type": 3, "timezone": "Europe/Helsinki" }, "start_time": { "date": "1970-01-01 19:00:00.000000", "timezone_type": 3, "timezone": "Europe/Helsinki" }, "end_time": { "date": "1970-01-01 20:00:00.000000", "timezone_type": 3, "timezone": "Europe/Helsinki" }, "venue_address": "Venue 1, 4, Sity, Country region", "attendance_validation": "2020-03-25 18:18:34", "userId": 2, "first_name": "Marco", "last_name": "Tamburrino", "avatar": null, "provider_id": "CH-59676" } } ~~~ # Favorites (diagram 9.0) - [x] Done ## Favoritees Add - [x] Done Request: /api/v1/favourite/add Method: POST Headers: Authorization: Bearer eyJ0eXAiOiJKV1QiLC... ~~~ json Body: { "favorite_id":432 } Response: { "statusCode": 200, "message": "Event - 432 added successfully." } Bad Response: { "statusCode": 400, "data": { "error": "An exception occurred while" } } ~~~ ## Favoritees Remove - [x] Done Request: /api/v1/favourite/remove Method: POST Headers: Authorization: Bearer eyJ0eXAiOiJKV1QiLC... ~~~ json Body: { "favorite_id":432 } Response: { "statusCode": 200, "message": "Event - 432 removed successfully." } Bad Response: { "statusCode": 400, "data": { "error": "The requested favorite not found" } } ~~~ ## Favoritees List - [x] Done Request: /api/v1/favourite/list Method: POST Headers: Authorization: Bearer eyJ0eXAiOiJKV1QiLC... ~~~ json Body: { } Response: { "statusCode": 200, "data": [ { "id": 420, "title": "Activity 1 P1", "venue": "Delphinstrasse, 12, 8008,Zürich, Switzerland", "address": "8008, Zürich", "image": [ "https://website.ativiti.loc/uploads/gallery/5e4bd15133c32.jpg" ], "status": "active", "event_date": "28-05-2020", "start_time": "08:00:00", "stop_time": "09:00:00" } ] } Bad Response: { "statusCode": 200, "message" => "Look like you don\'t have any favorite activities yet" } ~~~ # Attendees (diagram 14.0) - [x] Done Request: /api/v1/attendance_list Method: POST Headers: Authorization: Bearer eyJ0eXAiOiJKV1QiLC... ~~~ json Body: { "event_id": 426 } Response: { "statusCode": 200, "data": { "attendees": [ { "avatar": "https:// website.ativiti.loc/uploads/avatars/ paintball_5f292b2d807a9.jpg", "full_name": "FirstShkurik L." }, { "avatar": "https:// website.ativiti.loc/uploads/avatars/ samba_5f292bc8a2568.jpg", "full_name": "test a." } ], "attendeesCount": 2 } } Response without subscription: { "statusCode": 200, "data": { "message": [ "Ativiti Members will join this session.", "Only subscribers can see attendees profiles" ], "attendeesCount": 2 } } Response without bookings: { "statusCode": 200, "data": { "message": [ "Hu ho .. No attendees so far", "Be the first to join this session" ] } } ~~~