# Terra API Documentation Terra offers a suite of APIs and SDKs designed to unify access to health, fitness, and sensor data from a vast array of sources. It acts as a single integration point, abstracting the complexities of individual provider APIs (like Garmin, Fitbit, Apple Health, Strava, etc.). - Eliminates the need for developers to build and maintain separate integrations for each data provider. - Provides standardized data formats across different sources. - Offers real-time data streaming capabilities. - Manages authentication flows with various providers. Health & Fitness API (Web API): Primarily for server-to-server interactions. Used for fetching historical data, receiving data updates via webhooks, and writing data back to certain providers. Mobile SDKs: Native libraries for iOS (Swift), Android (Kotlin), and wrappers for React Native and Flutter. Required for accessing data sources that *lack* a web API (Apple Health, Samsung Health, Google Fit via Health Connect). Also used as the "Producer" component in the Streaming API. Streaming API: Enables real-time data transmission (e.g., heart rate per second) using WebSockets. - Producer: Usually a mobile app using a Terra Real-Time (RT) SDK, connected to a wearable. - Broker: Terra's central WebSocket server. - Consumer: Your backend service listening to the WebSocket stream from the broker. 2).> Provider: The origin of the health/fitness data (e.g., Garmin, Fitbit, Apple Health, Strava). Integration: Connecting Terra to a specific Provider. Enabled/disabled in the Dashboard. Data Source: A specific Provider integration that you enable. Destination: The URL (typically your backend webhook endpoint) where Terra sends data updates and events. Configured in the Dashboard. User: An end-user whose data you are accessing via Terra. Represented by a unique `user_id` generated by Terra. Developer ID (`dev_id`): Your unique Terra account identifier. Required in API calls/SDK initialization. API Key (`x-api-key`): Your secret key for authenticating server-side API calls. CRITICAL: Never expose this on the client-side. Scope: Permissions requested from the end-user during the authentication flow. - `reference_id`: An *optional* identifier you can pass during user authentication setup. Terra associates this with the generated `user_id`. Use this to map the Terra `user_id` back to *your* internal user ID system. Event: A JSON payload sent by Terra to your Destination webhook. Types include `auth` (authentication results) and data types (`activity`, `daily`, `sleep`, etc.). Widget: Terra's pre-built UI component for handling user authentication and provider selection. SDK: Libraries for mobile platforms to interact with Terra. Webhook: An HTTP callback mechanism. Terra sends POST requests with event data to your registered Destination URL. --- ## Health & Fitness API (Web API) ### Connect a User (Authentication Flow) - Goal: Securely link an end-user's account for a specific provider to your Terra developer account and obtain a unique Terra `user_id`. - Prerequisites: 1. Obtain your API Key (`x-api-key`) and Developer ID (`dev-id`) from the Terra Dashboard. 2. Configure a Destination webhook URL. 3. Enable the desired Data Sources in the Dashboard. 4. Review the expected authentication events. #### Methods for Authentication ##### Method 1: Terra Widget (Recommended) - Backend Step: Your server calls `POST /auth/generateWidgetSession`. - Headers: `dev-id`, `x-api-key`. - Body (JSON): ```json { "reference_id": "your-internal-user-id", "providers": "GARMIN,FITBIT", // Optional, overrides dashboard selection "language": "en", // Optional, defaults to browser language "auth_success_redirect_url": "https://yourapp.com/success", "auth_failure_redirect_url": "https://yourapp.com/failure" } ``` - Response (Success 200): ```json { "status": "success", "url": "https://widget.tryterra.co/session/...", "session_id": "unique-session-identifier", "expires_in": 900 } ``` - Client-Side Step: Open the `url` received from the backend. Use an in-app browser or a new browser tab. NEVER use a WebView or iframe. - Widget Flow: The user selects a provider, logs into their provider account, and grants permissions. - Redirection: User is redirected to your `auth_success_redirect_url` or `auth_failure_redirect_url`. - Webhook Event: An `auth` event is sent to your Destination. ##### Method 2: Custom UI - Backend Step 1 (Optional): Fetch metadata for enabled integrations using `GET /integrations/detailed`. - Client-Side Step 1: User clicks a button for a specific provider. - Backend Step 2: Your server calls `POST /auth/authenticateUser`. - Headers: `dev-id`, `x-api-key`. - Body (JSON): ```json { "reference_id": "your-internal-user-id", "provider": "FITBIT", "auth_success_redirect_url": "https://yourapp.com/success", "auth_failure_redirect_url": "https://yourapp.com/failure" } ``` - Response (Success 200): ```json { "status": "success", "user_id": "terra-generated-user-id", "auth_url": "https://www.provider-url.domain/oauth2/authorize?..." } ``` - Client-Side Step 2: Open the `auth_url`. Use an in-app browser or new browser tab. NEVER use a WebView or iframe. - Provider Flow: User logs into the provider site and grants permissions. - Redirection: User is redirected to your success or failure URL. - Webhook Event: An `auth` event is sent to your Destination. 2.4. Receive Data Updates (Webhooks) - Mechanism: Terra sends HTTP POST requests to your Destination URL when new data is available or when authentication events occur. - Payload Structure (General): ```json { "type": "event_type", "user": { "user_id": "terra-user-id", "provider": "PROVIDER_NAME", "reference_id": "your-internal-user-id", "scopes": "Granted permissions string", "last_webhook_update": "timestamp or null" }, "status": "status_string", "data": [ /* Array of data objects for data events */ ] } ``` #### Event Types - `auth`: Sent after an authentication attempt. - `user_reauth`: Sent when a user connects the same provider account again. - Data Events (`activity`, `daily`, `body`, `sleep`, etc.): Sent when new data is synced from the provider cloud. #### Data Handling Strategy - Terra data payloads are generally supersets of previous data for the same logical entity. - Always overwrite data stored on your end with the most up-to-date version. - Unique Identifiers for Overwriting (per User): - Activity: `metadata.summary_id` - Daily: `metadata.start_time` (Use date part only) - Body: `metadata.start_time` (Use date part only) - Sleep: `metadata.summary_id` - Menstruation: `metadata.start_time` (Use date part only) - Planned Workout: `metadata.summary_id` - Testing Webhooks: Use the Payload Simulator in the Terra Dashboard to send test events to your Destination URL. - Error Handling: If your destination returns an error (e.g., 5xx HTTP status), Terra will retry sending the webhook with exponential backoff for a little over 24 hours. Ensure your endpoint is robust and returns 2xx on successful receipt. 2.5. Request Historical Data (REST API) - Purpose: Fetch data points from a time range *before* the user connected via Terra, or for debugging, or implementing pull-to-refresh. - Prerequisites: Valid `user_id` obtained from successful authentication. Required Data Sources and permissions enabled. - Endpoints: Use HTTP GET requests. Common endpoints: - `/activity` - `/daily` - `/sleep` - `/body` - `/athlete` (Fetches user profile info, including `provider_user_id`) - `/menstruation` (If applicable) - `/v2/plannedWorkout` (To retrieve workouts written previously) - Common Parameters: - `user_id` (query parameter, required): The Terra user ID. - `start_date` (query parameter, required, format `YYYY-MM-DD`): Start of the date range. - `end_date` (query parameter, optional, format `YYYY-MM-DD`): End of the date range. Defaults to current date if omitted. - `to_webhook` (query parameter, boolean, optional): Controls sync/async behavior. - `false` (Default for ranges <= 28 days): Synchronous. The API call waits (up to ~30s) and returns the data directly in the HTTP response body. Suitable for small, time-sensitive requests. - `true` (Default for ranges > 28 days): Asynchronous. The API call returns immediately with a success message and a `terra-reference` header. Terra fetches the data in the background and sends it to your Destination webhook later in chunks. Required for long time ranges. - `with_samples` (query parameter, boolean, optional): Include sample-level data (e.g., second-by-second HR) where available. Defaults to `false`. Can significantly increase payload size. - Asynchronous Flow (`to_webhook=true`): 1. Make GET request with `to_webhook=true`. 2. Store the `terra-reference` value returned in the response headers. 3. Receive an immediate `#large-request-processing` event via webhook (optional handling). 4. Later, receive a `#large-request-sending` event indicating the number of expected data payloads. 5. Receive multiple webhook POST requests containing the data chunks. Each chunk will have a `terra-signature` header matching the original `terra-reference`. Use this match to confirm the data belongs to your request. 6. Once all expected payloads (based on `#large-request-sending`) with the matching `terra-signature` are received, the transfer is complete. - Provider Limits on Historical Data: - Garmin: 5 years back from the current time. - Polar: No historical data retrieval permitted. - Health Connect: 30 days back from the moment the user granted permission. - Dashboard Request: You can also trigger historical data pulls manually from the Terra Dashboard (Users tab -> Request Data) for debugging. - Practical Example (Python FastAPI using `requests` - Async): ```python import requests import os from fastapi import FastAPI, Request, HTTPException from datetime import datetime, timedelta app = FastAPI() TERRA_API_URL = "https://api.tryterra.co/v2" TERRA_API_KEY = os.getenv("TERRA_API_KEY") TERRA_DEV_ID = os.getenv("TERRA_DEVELOPER_ID") @app.post("/webhook") async def webhook_handler(request: Request): payload = await request.json() if payload.get("type") == "auth" and payload.get("status") == "success": user_id = payload.get("user", {}).get("user_id") if not user_id: # Handle error: user_id missing print("Auth success but user_id missing") return {"message": "Auth success but user_id missing"} # Request data for the last 28 days asynchronously start_date_dt = (datetime.utcnow() - timedelta(days=28)) end_date_dt = (datetime.utcnow() + timedelta(days=1)) # Include today fully start_date_str = start_date_dt.strftime('%Y-%m-%d') end_date_str = end_date_dt.strftime('%Y-%m-%d') headers = { "x-api-key": TERRA_API_KEY, "dev-id": TERRA_DEV_ID } endpoints_to_fetch = ["/activity", "/daily", "/sleep", "/body"] request_results = {} for endpoint in endpoints_to_fetch: try: response = requests.get( f"{TERRA_API_URL}{endpoint}", headers=headers, params={ "user_id": user_id, "start_date": start_date_str, "end_date": end_date_str, "to_webhook": "true" # Asynchronous } ) response.raise_for_status() # Raises an exception for 4XX/5XX # Store terra-reference if needed for tracking data chunks terra_ref = response.headers.get("terra-reference") request_results[endpoint] = {"status": response.status_code, "terra_reference": terra_ref} print(f"Requested {endpoint} for user {user_id}, terra_reference: {terra_ref}") except requests.exceptions.HTTPError as e: print(f"Error requesting {endpoint} for user {user_id}: {e.response.status_code} - {e.response.text}") request_results[endpoint] = {"status": e.response.status_code, "error": e.response.text} # Optionally raise HTTPException or handle specific errors like 401/403 immediately return {"message": "Historical data request(s) initiated", "details": request_results} elif payload.get("type") in ["activity", "daily", "sleep", "body"]: # Process incoming data from webhooks (historical or real-time updates) terra_signature = request.headers.get("terra-signature") print(f"Received data webhook: type={payload.get('type')}, user_id={payload.get('user',{}).get('user_id')}, terra_signature={terra_signature}") # Add processing logic here - store data, match terra_signature if it's from an async request return {"message": "Data webhook received"} elif payload.get("type") == "large-request-processing" or payload.get("type") == "large-request-sending": print(f"Received large request event: {payload.get('type')}, message: {payload.get('message')}") return {"message": "Large request event received"} elif payload.get("type") == "deauth": print(f"Received deauth event for user_id: {payload.get('user', {}).get('user_id')}") # Handle user deauthorization in your system return {"message": "Deauth event received"} else: # Handle other webhook types or ignore print(f"Received unhandled webhook type: {payload.get('type')}") return {"message": "Webhook received, not processed by this specific logic"} # Fallback/default response return {"message": "Webhook processed"} # Remember to implement proper security (webhook signature verification), # error handling, and robust data storage. ``` 2.6. Write Data (Planned Workouts) - Purpose: Create structured workout plans that users can follow on their wearable devices (where supported by the provider). - Supported Providers: Garmin, Hammerhead, Coros, TodaysPlan (check docs/GitHub for current list). - Endpoint: `POST /v2/plannedWorkout` - Headers: - `dev-id` (string, required): Your Developer ID. - `x-api-key` (string, required): Your API Key. - `Content-Type`: `application/json` - Body: A JSON object containing a `data` array. Each element in the `data` array is a `PlannedWorkout` object. - `PlannedWorkout` Object: - `user_id` (string, required): The Terra user ID to send the workout to. - `metadata` (object, required): Workout metadata. - `name` (string, required): Workout name. - `description` (string, optional): Workout description. - `type` (enum string, required): Activity type (e.g., "RUNNING", "CYCLING", "LAP_SWIMMING"). From provider-specific list. - `estimated_duration_seconds` (number, optional): Estimated total duration. - `estimated_distance_meters` (number, optional): Estimated total distance. - `planned_date` (string `YYYY-MM-DDTHH:mm:ssZ` or `YYYY-MM-DDTHH:mm:ss+00:00`, optional): Date for the workout. - `estimated_calories` (number, optional) - `estimated_tss` (number, optional - Training Stress Score) - `estimated_tscore` (number, optional - Training Impulse Score) - `estimated_if` (number, optional - Intensity Factor) - `pool_length_meters` (number, optional): For swimming. - `steps` (array, required): Array of workout steps (can be nested for repeat blocks). - Workout Step Object: - `type` (integer enum, required): `0` for a single step/segment, `1` for a repeat block. - `order` (integer, required): Order of this step within its parent (0-indexed). - `description` (string, optional): Name/description of the step (e.g., "Warm Up", "Interval 1"). - If `type: 0` (Single Step/Segment): - `intensity` (integer enum, optional): Intensity label (e.g., `1`: Warmup, `2`: Active, `3`: Recovery, `4`: Cooldown, `5` Active, etc. - check Terra docs for full enum). - `durations` (array, required): Array of `Duration` objects defining how this step ends (at least one required). Multiple durations usually mean "whichever comes first". - `targets` (array, optional): Array of `Target` objects defining target zones/values for this step. - If `type: 1` (Repeat Block): - `durations` (array, required): Must contain exactly one `Duration` object with `duration_type: 9` (Repetitions) and the corresponding `reps` value. - `steps` (array, required): Nested array of workout step objects (type 0 or type 1) to be repeated. - `Duration` Object (defines completion criteria for a step): - `duration_type` (integer enum, required): e.g., `0` (Time), `1` (Distance), `2` (Calories Burned), `3` (Heart Rate Greater Than), `4` (Heart Rate Less Than), `5` (Power Greater Than), `6` (Power Less Than), `8` (Lap Button Press), `9` (Repetitions - only for repeat blocks). - Value fields (number, required based on `duration_type`): - `duration_type: 0` -> `seconds` - `duration_type: 1` -> `meters` - `duration_type: 2` -> `calories` - `duration_type: 3 or 4` -> `bpm` - `duration_type: 5 or 6` -> `watts` - `duration_type: 9` -> `reps` - `Target` Object (defines target zone for a step): - `target_type` (integer enum, required): e.g., `0` (Pace), `1` (Speed), `2` (Cadence), `3` (Power Zone - usually 1-7), `4` (Heart Rate Zone - usually 1-5), `12` (Heart Rate % Max HR), `13` (Heart Rate % FTP/Threshold), `15` (Power % FTP). Check specific enum values in Terra docs. - Value fields (number, required based on `target_type`): Define the range or single value. - For ranges: `pace_low` / `pace_high` (seconds/km or seconds/mile), `speed_low` / `speed_high` (m/s), `cadence_low` / `cadence_high` (rpm), `power_low` / `power_high` (watts), `hr_low` / `hr_high` (bpm). - For % types: `hr_percentage_low` / `hr_percentage_high`, `power_percentage_low` / `power_percentage_high`. - Use `null` for one side of an open-ended target (e.g., `hr_percentage_low: 80, hr_percentage_high: null` means "above 80%"). - Response (Success 201 Created): ```json { "message": "text", // e.g., "Workout uploaded successfully" "log_ids": [ "provider_specific_workout_id1", "provider_specific_workout_id2" ], // List of IDs for the created workouts on the provider's system "status": "success", "user": { /* user details */ } } ``` - Store the `log_ids` if you need to retrieve or delete these specific workouts later using `GET` or `DELETE` on `/v2/plannedWorkout`. The delete endpoint uses these log IDs. - Response (Error 4xx/5xx): Standard error responses (e.g., 400 for malformed request, 404 if `user_id` not found). - Example Use Case: Creating interval training (e.g., 5x [3 min at Zone 4 Power, 2 min at Zone 1 Power]), structured warmups/cooldowns. - iOS 17+: Planned workouts can also be written *via the iOS SDK* (`postPlannedWorkout`) for Apple Health/Watch users. See Mobile SDK section. 2.7. Provider-Specific Guides & Credentials - Integrations Subject to Change: Provider APIs can change without notice, potentially disrupting Terra's service beyond their control. - Creating Own Access Keys: Some providers might require you to use your own application keys (obtained from their developer portal) instead of Terra's managed keys, often to avoid rate limits or comply with terms. This usually involves creating an app on the provider's platform and entering the client ID/secret into the Terra Dashboard connection settings. Providers mentioned requiring this: - Strava: 1. Log in to `https://strava.com/settings/api`. 2. Create an application. 3. Set Authorization Callback Domain to `api.tryterra.co`. 4. Save the Client ID and Client Secret. 5. In Terra Dashboard (API -> Connections -> Add More -> Strava), enter the obtained Client ID and Secret in the connection settings. - Virtuagym: 1. Request an API key from `https://virtuagym.com/public-api`. 2. In Terra Dashboard (API -> Connections -> Add More -> Virtuagym), go to options. 3. Fill in your portal name as the `client_id` and your API Key as the `client_secret`. - Huawei: 1. Create Huawei ID & verify identity (DUNS/Business License or personal docs guides linked). 2. Apply for HUAWEI ID Service via AppGallery Connect (guide linked). This is where you get `client_id` and `client_secret`. 3. Enter obtained `client_id` and `client_secret` into Terra Dashboard for Huawei connection. 4. Set Redirect URL in Huawei Console (AppGallery Connect): `https://api.tryterra.co/v2/auth/huawei/oauth2`. 5. Set App access URL in Huawei Console: `https://api.tryterra.co/`. 6. Set Callback address for Health Kit webhooks (for data updates from Huawei Health Kit to Terra) in Huawei Console: `https://api.tryterra.co/v2/hooks/huawei/healthkit/notifications`. Follow webhook registration steps provided by Huawei (guide linked in Terra docs). - Dedicated Credentials & Special Setup: Some integrations (marked N/A or requiring specific credentials in the docs table, e.g., WHOOP, Dexcom, FreestyleLibre Germany/Austria/France) need credentials/setup handled via Terra support or specific configurations. - FreestyleLibre (DE, AT, FR): Requires proof of a doctor's license / registered practice ID in those regions due to regulations. Provide details to Terra. 2.8. Debugging FAQ (Web API Focused) (See also Section 7 for combined Debugging) - User logged in with wrong account: Verify `provider_user_id` from `/athlete` endpoint against provider's app/website. - Data shown on provider app but not via Terra API: Check provider's *web dashboard* first. Force sync. Check for known delays (Garmin API, Google Fit API). Check MyFitnessPal diary sharing settings. - Destination Erroring (Retries): Check your server logs for errors. Terra retries for ~24h on 5xx errors. --- ## 3. Mobile SDKs Purpose: 1. Access data from sources without a Web API (Apple Health, Samsung Health, Google Fit via Health Connect). 2. Act as the Producer for the Streaming API (connect to BLE/ANT+ devices and send data to Terra broker - See Section 4 for RT SDKs). This section focuses on SDKs for accessing stored health data. 3.1. Overview - SDKs exist for iOS (Swift - `TerraiOS`), Android (Kotlin - `terra-android`), React Native (`terra-react`), and Flutter (`terra_flutter_bridge`). - They handle native platform interactions (HealthKit, Health Connect). - Require backend interaction to generate secure authentication tokens for initialization. - Provide functions for initializing connections, fetching data, and sometimes writing data. - Data flow typically involves the SDK retrieving data from the phone's health store and either returning it to the app or pushing it to your backend webhook (if `schedulerOn` or background delivery is configured and supported). 3.2. Relevant SDK-Only Integrations: - Apple Health (iOS): Accessed via HealthKit. SDK is the *only* way. - Samsung Health (Android): Relies on Health Connect. Terra SDK handles Health Connect interaction. - Google Fit (Android): Accessed via Health Connect. The Google Fit REST API is sunsetting (June 30, 2025). Terra SDK manages Health Connect. 3.3. General SDK Workflow (Health Data Access): 1. Install & Configure: Add SDK dependency, configure native projects (Info.plist, AndroidManifest, capabilities, permissions, Gradle). 2. Initialize SDK Library: Initialize the Terra library itself when the app starts (provide `dev_id`, optional `reference_id`). This is different from `initConnection`. 3. Generate Auth Token (Backend): Your backend calls `POST /auth/generateAuthToken` (using API Key) to get a single-use `token`. 4. Send Token to Client: Securely transmit the token to the mobile app. 5. Initialize Connection (Client): App calls SDK's `initConnection` function, passing the `token`, connection type (e.g., `.APPLE_HEALTH`, `.SAMSUNG` (for Health Connect), `.GOOGLE_FIT` (for Health Connect)), and optionally custom permissions / background delivery settings. This triggers the native permission prompt. 6. Fetch/Receive Data (Client): Use SDK functions (`getDaily`, `getActivity`, etc.) to request data. Data can be returned directly to the app (if `toWebhook=false` in function call) or sent to your backend webhook (if `schedulerOn` or background delivery is configured and the function's `toWebhook` parameter is true or defaults to it). 7. Write Data (Client - Limited): Some SDKs allow writing data (e.g., `postActivity`, `postNutrition` on iOS for Apple Health). 3.4. iOS SDK (Swift) - `TerraiOS` - Health Data Access (Pages 84-97 in PDF) - Security: API Key on backend. SDK is *only* for mobile-only integrations if not using streaming. - Installation: Swift Package Manager: `https://github.com/tryterra/TerraiOS`. - Project Setup: - Capabilities: - HealthKit: Add "HealthKit". - Background Delivery: Add "Background Delivery" under HealthKit. - Background Modes: Add "Background processing" and "Background fetch". - `Info.plist`: - `Privacy - Health Share Usage Description` (NSHealthShareUsageDescription): Custom text for Apple Health permission screen (read access). - `Privacy - Health Update Usage Description` (NSHealthUpdateUsageDescription): Custom text if writing data. - `Permitted background task scheduler identifiers` (Array): Add `co.tryterra.data.post.request`. - Initialization (SDK Library): ```swift import TerraiOS var terra: TerraManager? // Store as a class property // In AppDelegate or main app struct/class func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Terra.instance(devId: "YOUR_DEV_ID", referenceId: "YOUR_INTERNAL_USER_ID_OR_NIL") { [weak self] managerInstance in self?.terra = managerInstance // SDK is ready, now you can call initConnection when needed } // ... other setup ... Terra.setUpBackgroundDelivery() // For webhook updates from HealthKit return true } ``` - Initialize `Terra.instance` once, e.g., in `didFinishLaunchingWithOptions`. It should be initialized every time the app is brought to the foreground according to docs. - Initialize Connection (to Apple Health): 1. Backend: Call `POST /auth/generateAuthToken` to get `sdk_token`. 2. Client (Swift): ```swift let sdkTokenFromBackend = "..." let customPermissions: Set = [] // Empty for all default, or specify e.g., [.WORKOUTS, .ACTIVITY_SUMMARY] let schedulerOn = true // Enable background delivery to webhook terra?.initConnection( type: .APPLE_HEALTH, token: sdkTokenFromBackend, customReadTypes: customPermissions, // For read permissions // customWriteTypes: customPermissions, // If writing data schedulerOn: schedulerOn ) { success, error in if success { print("Apple Health Connection successful!") } else if let error = error { print("Apple Health Connection failed: \(error.localizedDescription) - \(error.message)") } } ``` - `initConnection` only needs to be run once successfully per set of permissions. Apple Health prohibits repeated permission popups. - If your app is webview-based, interrupt webview, call `initConnection`, then re-open webview. - Background Delivery (Webhook Updates): Enabled by `schedulerOn: true` and `Terra.setUpBackgroundDelivery()`. Data is sent to your Destination webhook when device is unlocked and new data is available. - Validate Connection: ```swift if let currentUserId = terra?.getUserid() { // Synchronous call print("Apple Health user ID: \(currentUserId)") } else { print("No active Apple Health connection or user ID.") // May need to call initConnection again if connection was expected } ``` - Disconnecting a User: Call the Web API endpoint `POST /deauthenticateUser` (or `DELETE /user`) from your backend, providing the `user_id`. - Historical Data Retrieval (Client-Side, direct to app): ```swift let startDate = Calendar.current.date(byAdding: .day, value: -7, to: Date())! let endDate = Date() terra?.getDaily(type: .APPLE_HEALTH, startDate: startDate, endDate: endDate, toWebhook: false) { success, dataPayload, error in if success, let payload = dataPayload { // Process TerraDataPayload directly in the app // payload.type, payload.data (array of data objects), payload.user } else { print("Failed to get daily data: \(error?.localizedDescription ?? "Unknown error")") } } ``` - Writing Data to Apple Health (Client-Side): - `terra.postActivity(type: .APPLE_HEALTH, payload: terraActivityData) { success, error in ... }` - `terraActivityData` needs `device_data` (e.g., `TerraDeviceData(name: "My App")`). - Uses `HKWorkout` internally (deprecated iOS 17, but likely still functional or Terra updates). - `terra.postNutrition(type: .APPLE_HEALTH, payload: terraNutritionData) { ... }` - `terra.postBody(type: .APPLE_HEALTH, payload: terraBodyData) { ... }` - Planned Workouts (iOS 17+): ```swift // Create TerraPlannedWorkout payload (see Web API section for structure) let plannedWorkout = TerraPlannedWorkout(...) if #available(iOS 17, *) { terra?.postPlannedWorkout(type: .APPLE_HEALTH, payload: plannedWorkout) { success, error in // ... handle result ... } } else { print("Planned Workouts require iOS 17+ for Apple Health SDK writing.") } ``` 3.5. Android SDK (Kotlin) - `terra-android` - Health Data Access (Pages 98-106 in PDF) - Security: API Key on backend. - Requirements: Health Connect app installed on user's device. Android 28 (Pie) and above. - Installation: In `build.gradle (Module :app)`: ```gradle dependencies { implementation 'co.tryterra:terra-android:{LATEST_VERSION_FROM_MAVEN_CENTRAL}' } ``` Add `mavenCentral()` to project-level `build.gradle` if not present. Sync project. - Project Setup (Health Connect): - User Onboarding: Guide users to install Health Connect and grant permissions within the Health Connect app for *your app* and for *source apps* (e.g., Samsung Health, Google Fit) to write data *to* Health Connect. - `AndroidManifest.xml`: - Permissions: Declare `` for *every* Health Connect data type your app intends to read. ```xml ``` If you are *not* using a specific permission, you can add `tools:remove="android.permission.health.PERMISSION_NAME"` to the `` tag to explicitly exclude it if it's inherited. - Privacy Policy Intent Filter: Add to your activity that handles permission rationale: ```xml ``` - Google Health Connect API Access: For production apps, you must apply for permissions to access the Health Connect API with Google using their application form. - Initialization (SDK Library): ```kotlin import co.tryterra.terra.Terra // Main SDK class // import co.tryterra.terra.TerraManager // May be needed for older examples/types private lateinit var terra: Terra // Store as a Activity/Fragment property override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... your other onCreate code ... Terra.instance("YOUR_DEV_ID", "YOUR_INTERNAL_USER_ID_OR_NULL", this) { manager, error -> if (manager != null) { this.terra = manager // SDK is ready, proceed to initConnection when needed } else { // Handle initialization error (e.g., log it) Log.e("TerraSDK", "Failed to initialize Terra: ${error?.message}") } } } ``` - Initialize once. Docs say "should be initialized every time your app is started". - Initialize Connection (to Health Connect source, e.g., Samsung Health, Google Fit): 1. Backend: Call `POST /auth/generateAuthToken` to get `sdk_token`. 2. Client (Kotlin): ```kotlin // sdkTokenFromBackend = "..." val customPermissionsSet: Set = emptySet() // or specify permissions val enableScheduler = true // For background updates to webhook terra.initConnection( connection = co.tryterra.terra.Connections.SAMSUNG, // Or .GOOGLE_FIT token = sdkTokenFromBackend, context = this, // Your Activity or Application context customPermissions = customPermissionsSet, schedulerOn = enableScheduler, startIntent = null // Deprecated, always pass null ) { success, error -> if (success) { Log.i("TerraSDK", "Health Connect (${co.tryterra.terra.Connections.SAMSUNG}) connection successful!") } else { Log.e("TerraSDK", "Health Connect connection failed: ${error?.message}") } } ``` - Health Connect permission prompt is shown by the system. Only appears once per set of permissions. - Validate Connection: ```kotlin val currentUserId = terra.getUserId() // Synchronous if (currentUserId != null) { Log.i("TerraSDK", "Active Health Connect user ID: $currentUserId") } else { Log.w("TerraSDK", "No active Health Connect connection.") } ``` - Disconnecting a User: Call Web API `POST /deauthenticateUser` from backend. - Historical Data Retrieval (Client-Side, direct to app): ```kotlin val cal = Calendar.getInstance() cal.add(Calendar.DAY_OF_YEAR, -7) val startDate = cal.time val endDate = Date() // Now terra.getDaily( type = co.tryterra.terra.Connections.SAMSUNG, startDate = startDate, endDate = endDate, toWebhook = false // Data returned in callback ) { success, dataPayload, error -> if (success && dataPayload != null) { // Process dataPayload (co.tryterra.terra.TerraDataPayload) Log.i("TerraSDK", "Daily data received: ${dataPayload.data.size} records") } else { Log.e("TerraSDK", "Failed to get daily data: ${error?.message}") } } ``` 3.6. React Native SDK - `terra-react` - Health Data Access (Pages 107-119 in PDF) - Security: API Key on backend. - Installation: `npm install terra-react` or `yarn add terra-react`. - Native Project Setup: Crucial: You must complete the native iOS (`TerraiOS`) and Android (`terra-android` with Health Connect) setup steps described in sections 3.4 and 3.5 within your React Native project's `/ios` and `/android` folders respectively. This includes: - iOS: Adding TerraiOS Swift Package, configuring Capabilities (HealthKit, Background Modes), `Info.plist` entries (`NSHealthShareUsageDescription`, etc.), and modifying `AppDelegate.h`/`AppDelegate.m` (or `.swift`) to call `[Terra setUpBackgroundDelivery];` (Objective-C) or `Terra.setUpBackgroundDelivery()` (Swift). - Android: Adding `terra-android` Gradle dependency, Health Connect setup in `AndroidManifest.xml` (permissions, intent filters), and ensuring your app is prepared for Google Health Connect API access review. - Initialization (SDK Library): ```javascript import { initTerra } from 'terra-react'; import { useEffect } from 'react'; const App = () => { useEffect(() => { const initializeTerra = async () => { try { const successMessage = await initTerra('YOUR_DEV_ID', 'YOUR_INTERNAL_USER_ID_OR_NULL'); console.log('Terra React Native initialized successfully:', successMessage); } catch (error) { console.error('Failed to initialize Terra React Native:', error); } }; initializeTerra(); }, []); // ... rest of your app }; ``` - Initialize Connection (to Apple Health / Health Connect source): 1. Backend: Call `POST /auth/generateAuthToken` to get `sdk_token`. 2. Client (React Native): ```javascript import { initConnection, Connections, CustomPermissions } from 'terra-react'; const connectHealthSource = async (sdkTokenFromBackend) => { try { // For Apple Health: // const connectionType = Connections.APPLE; // For Health Connect (e.g., Samsung Health): const connectionType = Connections.SAMSUNG; // Or Connections.GOOGLE_FIT const customPermissionsArray = []; // E.g., [CustomPermissions.STEPS, CustomPermissions.HEART_RATE] const enableScheduler = true; const successMessage = await initConnection( connectionType, sdkTokenFromBackend, enableScheduler, customPermissionsArray, null // startIntent - deprecated, always null ); console.log(`${connectionType} connection successful:`, successMessage); } catch (error) { console.error(`${connectionType} connection failed:`, error); } }; ``` - Validate Connection: ```javascript import { getUserId, Connections } from 'terra-react'; const checkConnectionStatus = async () => { try { const userIdPayload = await getUserId(Connections.APPLE); // Or other connection type if (userIdPayload && userIdPayload.user_id) { console.log('Active User ID:', userIdPayload.user_id); } else { console.log('No active connection or user ID found.'); } } catch (error) { console.error('Error getting user ID:', error); } }; ``` - Disconnecting a User: Call Web API `POST /deauthenticateUser` from backend. - Historical Data Retrieval (Client-Side, direct to app): ```javascript import { getDaily, Connections } from 'terra-react'; const fetchHistoricalData = async () => { const startDate = new Date(); startDate.setDate(startDate.getDate() - 7); // 7 days ago const endDate = new Date(); try { const dailyDataMessage = await getDaily( Connections.APPLE, // Or other connection type startDate, endDate, false // toWebhook: false to get data in response ); if (dailyDataMessage.success && dailyDataMessage.data) { console.log('Daily data:', dailyDataMessage.data); } else { console.error('Failed to get daily data:', dailyDataMessage.error); } } catch (error) { console.error('Error fetching daily data:', error); } }; ``` - Writing Data to Apple Health (iOS Only): ```javascript import { postActivity, Connections, ActivityType } from 'terra-react'; const writeActivityToAppleHealth = async () => { const activityPayload = { metadata: { name: 'Morning Run RN', start_time: new Date(Date.now() - 3600 * 1000).toISOString(), // 1 hour ago end_time: new Date().toISOString(), type: ActivityType.RUNNING, }, device_data: { name: 'My React Native App' }, heart_rate_data: { summary: { avg_hr_bpm: 130, max_hr_bpm: 160 } }, distance_data: { summary: { distance_meters: 5000 } }, calories_data: { total_burned_calories: 400 }, }; try { const response = await postActivity(Connections.APPLE, activityPayload); if (response.success) { console.log('Activity data posted successfully to Apple Health!'); } else { console.error('Failed to post activity data:', response.error); } } catch (error) { console.error('Error posting activity data:', error); } }; ``` - `postNutrition` and `postBody` are also available for `Connections.APPLE`. - Planned Workouts via SDK not explicitly mentioned for RN in OCR, but if `TerraiOS` supports it, it might be bridgeable. 3.7. Flutter SDK - `terra_flutter_bridge` - Health Data Access (Pages 120-133 in PDF) - Security: API Key on backend. - Installation: In `pubspec.yaml`: ```yaml dependencies: terra_flutter_bridge: ^{LATEST_VERSION_FROM_PUB_DEV} ``` Run `flutter pub get`. - Native Project Setup: Crucial: Complete native iOS (`TerraiOS`) and Android (`terra-android` with Health Connect) setup (sections 3.4, 3.5) in `/ios` and `/android` folders. - iOS: Add TerraiOS Swift Package, Capabilities, `Info.plist`, `AppDelegate` modifications for `setUpBackgroundDelivery`. - Android: Add `terra-android` Gradle dependency, Health Connect setup in `AndroidManifest.xml`, Google API access. - Initialization (SDK Library): ```dart import 'package:terra_flutter_bridge/terra_flutter_bridge.dart'; import 'package:flutter/material.dart'; // For WidgetsFlutterBinding Future initializeTerraFlutter() async { WidgetsFlutterBinding.ensureInitialized(); // Required before calling Terra init try { final String devID = 'YOUR_DEV_ID'; final String? referenceID = 'YOUR_INTERNAL_USER_ID_OR_NULL'; SuccessMessage? result = await TerraFlutterBridge.initTerra(devID, referenceID); if (result?.success == true) { print('Terra Flutter initialized successfully'); } else { print('Terra Flutter initialization failed: ${result?.error}'); } } catch (e) { print('Error during Terra Flutter initialization: $e'); } } ``` - Initialize Connection (to Apple Health / Health Connect source): 1. Backend: Call `POST /auth/generateAuthToken` to get `sdk_token`. 2. Client (Flutter): ```dart // sdkTokenFromBackend = "..." // For Apple Health: // final connectionType = Connection.apple; // For Health Connect (e.g., Samsung Health): final connectionType = Connection.samsung; // Or Connection.googleFit final List customPermissionsList = []; // E.g., [CustomPermission.STEPS, CustomPermission.HEART_RATE] final bool enableScheduler = true; try { SuccessMessage? result = await TerraFlutterBridge.initConnection( connectionType, sdkTokenFromBackend, enableScheduler, customPermissionsList, // startIntent parameter is not present in the Flutter SDK OCR ); if (result?.success == true) { print('${connectionType.toString()} connection successful!'); } else { print('${connectionType.toString()} connection failed: ${result?.error}'); } } catch (e) { print('Error connecting to ${connectionType.toString()}: $e'); } ``` - Validate Connection: ```dart Future checkFlutterConnectionStatus() async { try { UserId? userIdResult = await TerraFlutterBridge.getUserId(Connection.apple); // Or other connection if (userIdResult?.userId != null) { print('Active User ID (Flutter): ${userIdResult!.userId}'); } else { print('No active connection or user ID found (Flutter).'); } } catch (e) { print('Error getting user ID (Flutter): $e'); } } ``` - Disconnecting a User: Call Web API `POST /deauthenticateUser` from backend. - Historical Data Retrieval (Client-Side, direct to app): ```dart Future fetchFlutterHistoricalData() async { final DateTime endDate = DateTime.now(); final DateTime startDate = endDate.subtract(Duration(days: 7)); try { DataMessage? dailyDataMessage = await TerraFlutterBridge.getDaily( Connection.apple, // Or other connection startDate, endDate, false // toWebhook: false ); if (dailyDataMessage?.success == true && dailyDataMessage?.data != null) { print('Daily data (Flutter): ${dailyDataMessage!.data}'); // data is List } else { print('Failed to get daily data (Flutter): ${dailyDataMessage?.error}'); } } catch (e) { print('Error fetching daily data (Flutter): $e'); } } ``` - Writing Planned Workouts to Apple Health (iOS 17+, Apple Only): ```dart // Construct TerraPlannedWorkoutData payload (similar structure to Web API) // final TerraPlannedWorkoutData plannedWorkoutPayload = TerraPlannedWorkoutData(...); // if (Platform.isIOS) { // Ensure it's iOS // // Check iOS version if possible for 17+ // try { // SuccessMessage? response = await TerraFlutterBridge.postPlannedWorkout( // Connection.apple, // Must be Apple // plannedWorkoutPayload // ); // if (response?.success == true) { // print('Planned workout posted successfully to Apple Health (Flutter)!'); // } else { // print('Failed to post planned workout (Flutter): ${response?.error}'); // } // } catch (e) { // print('Error posting planned workout (Flutter): $e'); // } // } ``` (Note: The `TerraPlannedWorkoutData` class structure for Flutter would need to be inferred or found in the plugin's specific documentation, as it's not fully detailed in the OCR beyond the example usage.) --- ## Streaming API (Real-Time) - Detailed Purpose: Low-latency data streaming (e.g., per-second heart rate, steps) from wearables (via BLE/ANT+) or phone sensors, through a mobile app, to your backend. ### Overview & Components - Metrics: Steps, Heart Rate, Distance covered, etc., on a ~per-second basis. - Contrast: Health & Fitness API is for longer-span/historical data (workouts, sleep, daily totals). - Requirements: Device broadcasts via BLE (Bluetooth Low Energy), ANT+, or specific custom Bluetooth protocols (e.g., Apple Watch, Polar H10). Can also use phone sensors. - Architecture (Data Flow): 1. Wearable Device: The source of raw sensor data (e.g., a heart rate strap, smartwatch). 2. Producer (Mobile App): Your mobile application running a Terra Real-Time (RT) SDK (`TerraRTiOS` or `terra-rtandroid`). It connects to the wearable, collects data, and forwards it. 3. Terra WebSocket Broker: Terra's central server (`ws.tryterra.co` - confirm endpoint). It receives data from producers and dispatches it to consumers. 4. Consumer (Your Backend): Your server-side application that connects to the Terra WebSocket Broker to receive the real-time data stream. ### Workflow Part 1: Wearable -> App (Producer RT SDK Setup) - RT SDKs: - iOS (Swift): `TerraRTiOS` (from `https://github.com/tryterra/TerraRTiOS`). - Android (Kotlin): `terra-rtandroid` (from Maven Central, e.g., `co.tryterra:terra-rtandroid:X.X.X`). - A. iOS Setup (`TerraRTiOS`): - Installation: Add `TerraRTiOS` Swift Package. - `Info.plist`: - `Privacy - Bluetooth Always Usage Description` (NSBluetoothAlwaysUsageDescription): For BLE scanning and connection. - `Privacy - Motion Usage Description` (NSMotionUsageDescription): If intending to stream phone's motion sensor data. - Apple Developer Program: Real-time streaming on iOS is only available to those registered on the Apple Developer Program. - B. Android Setup (`terra-rtandroid`): - Installation: Add Gradle dependency: `implementation 'co.tryterra:terra-rtandroid:{LATEST_VERSION}'`. - Permissions (`AndroidManifest.xml`): - `` (for API 31+) - `` (for API 31+) - `` (for older APIs) - `` (for older APIs) - `` (Required for BLE scanning on many Android versions). - Possibly others depending on specific ANT+ or sensor usage. - C. Initialization (Both Platforms - RT SDK Library): - Import the RT SDK. - Create and store an instance of the `TerraRT` class. - iOS: `let terraRT = TerraRT(devId: "YOUR_DEV_ID", referenceId: "YOUR_INTERNAL_USER_ID_OR_NIL") { success in /* ... */ }` - Android: `terraRT = TerraRT(devId = "YOUR_DEV_ID", context = this, referenceId = "YOUR_INTERNAL_USER_ID_OR_NIL") { success -> /* ... */ }` - Initialize this when the app is launched or brought to the foreground. - D. Step 1: Connect the Phone Itself as a Streaming Producer: - Backend Call: Your server calls `POST /auth/generateAuthToken` to get a single-use token. (This is the *same endpoint* used for standard Mobile SDK initialization, but the token's purpose here is to authenticate the phone app as a *producer* to the streaming service). - Mobile RT SDK Call: - iOS: `terraRT.initConnection(token: "token_from_backend_for_phone_producer") { success in /* ... */ }` - Android: `terraRT.initConnection(token = "token_from_backend_for_phone_producer") { success -> /* ... */ }` - This registers the phone with Terra's streaming infrastructure. - E. Step 2: Scan and Connect to a Wearable Device: - Mobile RT SDK Call: - iOS: `terraRT.startBluetoothScan(type: .BLE) { success in /* ... */ }` (Shows a modal UI for device selection). - Android: `terraRT.startDeviceScan(type = Connections.BLE) { success -> /* ... */ }` (Can use cached devices or show widget). - This establishes a Bluetooth connection between the phone app and the wearable. - F. Step 3: Start Streaming Data (From Wearable *to the App Itself*): - Mobile RT SDK Call: - iOS: `terraRT.startRealtime(type: .BLE, dataType: [.heartRate, .steps]) { update: TerraRTiOS.Update in handleInAppUpdate(update) }` - Android: `terraRT.startRealtime(type = Connections.BLE, dataTypes = setOf(DataTypes.HEART_RATE, DataTypes.STEPS)) { update: Update -> handleInAppUpdate(update) }` - The `handleInAppUpdate` callback receives data packets (timestamp, type, value) from the connected wearable *within the mobile app*. This is useful for displaying live data in the app's UI. - G. Step 4: Stop In-App Streaming / Disconnect Device: - `terraRT.stopRealtime(type: .BLE)` (or relevant connection type) - `terraRT.disconnect(type: .BLE)` (or relevant connection type) ### Workflow Part 2: App -> Broker (Forwarding Data to Terra) - Purpose: To send the real-time data (that the app is receiving from the wearable as per section 4.2.F) to the Terra WebSocket Broker. - Step 1: Generate User Streaming Token (Backend Call): - Your server calls: `POST /auth/user` - Headers: `dev-id: YOUR_DEV_ID`, `x-api-key: YOUR_API_KEY`. - Query Parameter: `id` (string, required): The Terra `user_id` of the end-user whose data is being streamed. This `user_id` must have been previously obtained through the Health & Fitness API authentication flow (Section 2.3) or a Mobile SDK connection (Section 3). - Response (200 OK): `{"token": "USER_STREAMING_TOKEN_FOR_THIS_USER_SESSION"}` - Your backend sends this `USER_STREAMING_TOKEN_FOR_THIS_USER_SESSION` to the mobile app. - Step 2: Start Streaming to Broker (Mobile App - Modified `startRealtime` call): - The RT SDKs provide a variant of `startRealtime` that accepts this user-specific streaming token and a `connectionCallback` to indicate if the connection *to the broker* was successful. - iOS (`TerraRTiOS`): ```swift let userStreamingToken = "TOKEN_FROM_BACKEND_AUTH_USER" terraRT.startRealtime( type: .BLE, dataType: dataTypes, token: userStreamingToken, // This is the key addition realtimeCallback: { update in // For in-app data handleInAppUpdate(update: update) }, connectionCallback: { success in // For broker connection status if success { print("Successfully connected to Terra Broker for user data streaming.") } else { print("Failed to connect to Terra Broker for streaming.") } } ) ``` - Android (`terra-rtandroid`): ```kotlin val userStreamingToken = "TOKEN_FROM_BACKEND_AUTH_USER" terraRT.startRealtime( type = Connections.BLE, dataTypes = dataTypes, token = userStreamingToken, // Key addition updateHandler = { update -> handleInAppUpdate(update) }, // For in-app data connectionCallback = { success -> // For broker connection status if (success) { println("Successfully connected to Terra Broker for user data streaming.") } else { println("Failed to connect to Terra Broker for streaming.") } } ) ``` - Once `connectionCallback` reports `success`, the mobile app is actively forwarding the data for that `user_id` (associated with the `userStreamingToken`) to the Terra WebSocket Broker. ### Workflow Part 3: Broker -> Your Backend (Consumer Setup) - Purpose: Your backend service connects to the Terra WebSocket Broker to receive the real-time data streams forwarded by mobile producer apps. - Protocol: WebSocket. The endpoint is likely `wss://ws.tryterra.co` (verify with Terra). - Lifecycle & Payloads (Op Codes - Operations): 1. Connect: Your backend establishes a WebSocket connection to the Terra Broker. 2. Receive HELLO (Op 2): Immediately upon connection, the Broker sends: ```json { "op": 2, "d": { "heartbeat_interval": 40000 } } ``` - `heartbeat_interval` is in milliseconds. Your client must respect this for heartbeating. 3. Send Heartbeat (Op 0) & Receive Heartbeat ACK (Op 1): - To keep the WebSocket connection alive, your client must send heartbeats. - Client Sends: `{ "op": 0 }` - Broker Responds: `{ "op": 1 }` (acknowledging the heartbeat) - Timing: - The *first* heartbeat should be sent after `heartbeat_interval * jitter` milliseconds, where `jitter` is a random value between 0.1 and 1.0. - Subsequent heartbeats should be sent *at most* at the `heartbeat_interval` specified in the HELLO payload. - If a Heartbeat ACK (Op 1) is not received after sending a heartbeat, the client should assume the connection is dead, close it, and attempt to re-establish. 4. Send IDENTIFY (Op 3): After receiving Op 2 HELLO and successfully initiating heartbeating, your client must authenticate itself as a consumer. - Client Sends: ```json { "op": 3, "d": { "token": "YOUR_TERRA_API_KEY", // Use your main x-api-key here "type": 0 // Type 0 indicates a consumer connection } } ``` 5. Receive READY (Op 4): If the IDENTIFY payload is valid, the Broker responds with: ```json { "op": 4 } ``` - The connection is now authenticated and ready to receive data dispatches. 6. Receive DISPATCH (Op 5): The Broker sends data payloads as they arrive from producer apps: ```json { "op": 5, "d": { // The actual data payload from the sensor/wearable "t": "2022-05-04T10:26:11.268507+01:00", // Timestamp of the data point "val": 95 // Value of the data point (e.g., heart rate BPM) // 'd' structure can vary for more complex data like accelerometer }, "uid": "TERRA_USER_ID_OF_DATA_SOURCE", // The Terra user_id this data belongs to "seq": 73, // Sequence number for this user's stream. Increments for each message. "t": "HEART_RATE" // Type of data (e.g., "STEPS", "CALORIES", "DISTANCE", "ACCELEROMETER_X") } ``` - `uid`: Critical for associating the data with the correct end-user in your system. - `seq`: Use this to detect missed messages if your connection drops and then re-establishes. - `t` (top-level): The data type string. - `d` (nested): Contains the data point itself, usually with its own timestamp (`t`) and value (`val`). 7. Send REPLAY (Op 7) (Optional - For Recovering Missed Data): If your consumer disconnects and later reconnects, you can use the `seq` numbers to request missed data. - Client Sends: ```json { "op": 7, "d": { "after": 72, // The sequence number of the last message you successfully processed "before": 75 // (Optional) The sequence number up to which you want to replay } } ``` - The Broker will attempt to re-send messages with `seq` numbers greater than `after` (and less than or equal to `before`, if specified). ### WatchOS Streaming Integration - Concept: The Apple Watch, via a companion WatchOS app, can act as a direct data source. - Setup: - Create a WatchOS target within your iOS project. - Add `TerraRTiOS` Swift Package to the WatchOS target. - Enable HealthKit and Background Modes for the WatchOS app target. - WatchOS App Code: ```swift import WatchKit import TerraRTiOS class InterfaceController: WKInterfaceController { var terraRT_watch: TerraRTiOS.Terra? override func awake(withContext context: Any?) { super.awake(withContext: context) do { terraRT_watch = try TerraRTiOS.Terra() terraRT_watch?.connect() print("WatchOS Terra SDK Initialized and Connected") } catch { print("WatchOS Terra SDK init/connect failed: \(error)") } } @IBAction func startStreamingDataOnWatch() { let dataTypes: Set = [.HEART_RATE, .STEPS] terraRT_watch?.startStream(forDataTypes: dataTypes) { success, error in if success { print("Watch streaming TO PAIRED IOS APP started") } else { print("Watch streaming failed to start: \(error?.localizedDescription ?? "unknown error")") } } } } ``` - iOS App Code: ```swift func setupWatchOSListener() { terraRT?.connectWithWatchOS() { success in if success { print("Connection to WatchOS companion app successful.") let watchDataTypes: Set = [.HEART_RATE, .STEPS] terraRT?.startRealtime(type: .WATCH_OS, dataTypes: watchDataTypes) { update in print("iOS App: Received update from WatchOS: \(update.type) val: \(update.val ?? 0.0)") // This 'update' can now be forwarded to the Terra Broker } } else { print("Failed to connect with WatchOS companion app.") } } } ``` - Note: The data received by the iOS app from `.WATCH_OS` type stream is then handled like any other in-app real-time data. If it needs to go to your backend, the iOS app uses its `userStreamingToken` (from `/auth/user`) and forwards it to the Terra Broker using the `startRealtime` variant with the `token` and `connectionCallback` for broker connection, specifying `.WATCH_OS` as the source type if that's how Terra internally distinguishes it for the broker, or it might just be generic data from the phone producer. The OCR is less explicit on how `.WATCH_OS` data is tagged when *sent to the broker*. Typically, the broker cares about the `user_id` linked to the `userStreamingToken`. ## Combined Debugging FAQ & Troubleshooting This section consolidates common issues and debugging steps across all Terra components. - Authentication & Connection Issues: - Incorrect API Key/Dev ID: Ensure you are using the correct `dev-id` and `x-api-key` from your Terra Dashboard. For server-side calls. - Expired/Invalid SDK/Streaming Tokens: Tokens generated by `/auth/generateAuthToken` (for SDK init) and `/auth/user` (for streaming to broker) are single-use and/or short-lived. Ensure your backend generates a fresh one when needed. - User Logged into Wrong Provider Account: This is a common source of "missing data." - Verification: Use the Terra API (`GET /athlete?user_id=...` or `/userInfo?user_id=...`) to retrieve the `provider_user_id` that Terra has stored for the connection. - Compare: Ask the user to find their ID within their native provider app or website (e.g., Fitbit app profile, Garmin Connect profile online, MyFitnessPal diary URL, Freestyle Libre patient profile URL). - Examples from Docs: - Freestyle Libre: URL format `https://www.libreview.com/patient/{provider_user_id}/profile`. - Fitbit: In Fitbit app: Avatar (top left) -> Tap display name -> Personal -> Fitbit User ID at bottom. - MyFitnessPal: Diary URL `https://www.myfitnesspal.com/food/diary/{provider_user_id}`. If this URL shows "Invalid username", the user likely entered their email instead of MFP username during Terra auth. If it shows "Diary sharing is disabled", they need to enable it in MFP settings. - Resolution: If mismatched, guide the user to deauthorize the incorrect account (via your app or Terra Dashboard if user has access) and re-authenticate with the correct provider account. - Data Syncing Issues (Data appears in native provider app but not via Terra): - Step 1: Check Provider's Cloud/Web Dashboard: Data must sync from wearable -> native app -> provider's cloud servers before Terra can access it. Verify data is visible on the provider's official website (e.g., connect.garmin.com, fitbit.com dashboard). - Step 2: Force Sync in Native App: Ask user to manually trigger a sync in the provider's app. - Step 3: Check for Known API Delays: - Garmin Connect: Can take 1-2 minutes *after* data appears on Garmin's web dashboard for it to be available via API. - Google Fit (via Health Connect): Can have delays of 30-60 minutes. - Step 4: Specific Provider Settings: - MyFitnessPal: Ensure "Diary Sharing" is enabled in MFP settings for the connected account. - Mobile SDK Specific Sync Issues: - Apple Health (iOS): - Background App Refresh: Must be enabled system-wide (Settings > General > Background App Refresh) AND for your specific app. - Low Power Mode: Must be OFF, as it restricts background activity. - Device Locked: HealthKit often restricts background data delivery until the device is unlocked. - Debugging with Backfill: Use the Terra Dashboard (Tools > Debug > Users > Request Data for `user_id`) to trigger a historical data pull for Apple Health. If data comes through this way, it indicates the initial connection is fine, but background delivery might be the issue. Requires `TerraiOS >= 1.6.12`. - Google Fit / Health Connect (Android): - Health Connect App: Must be installed, and user must grant permissions *within Health Connect* for your app to read data, and for source apps (like Samsung Health, Google Fit app) to *write* data *to* Health Connect. - `AndroidManifest.xml`: Double-check all required permissions and intent filters are correctly configured. - Google API Access: Ensure your application for Health Connect API access (for production) has been approved by Google. - Platform Immaturity: Health Connect is a newer platform. Bugs or sync inconsistencies can occur between source apps and Health Connect itself. - Webhook Destination Errors: - Server-Side Errors (5xx): If your webhook endpoint returns a 5xx HTTP status code, Terra will retry sending the event with exponential backoff for approximately 24 hours. Check your server logs to identify and fix the cause of the error. - Client-Side Errors (4xx): If your endpoint returns a 4xx, Terra typically will *not* retry. Ensure your endpoint logic is correct. - Idempotency: Design your webhook handler to be idempotent if possible (i.e., processing the same event multiple times has no additional adverse effect), as retries can lead to duplicate deliveries. - Streaming API Troubleshooting: - Token Mismatch: Ensure correct tokens are used: - Phone Producer Init: Token from `/auth/generateAuthToken` used in RT SDK's `initConnection`. - User Data Streaming to Broker: Token from `/auth/user` (with `user_id`) used in RT SDK's `startRealtime` (broker-connected variant). - Backend Consumer Auth: Your main Terra API Key (`x-api-key`) used in WebSocket `IDENTIFY` (Op 3) payload. - WebSocket Connection Stability: Monitor heartbeats (Op 0 from client, Op 1 from broker). If ACKs are missed, re-establish the WebSocket connection. - RT SDK Callbacks: Check the `success` status in `connectionCallback` of the RT SDK's `startRealtime` (broker-connected variant) to ensure the app successfully connected to the Terra Broker. - Correct `user_id` for `/auth/user`: The `id` parameter for `/auth/user` (generating user streaming token) *must* be the Terra `user_id` of the user whose data is being streamed. - Testing & Development Limits: - User Limits (Dashboard/API): Be aware of user limits (e.g., 50 users) in testing environments. De-authenticate test users (`POST /deauthenticateUser` or via Dashboard) if you hit the limit. - Terra Dashboard Tools: - Payload Simulator: Send test events (auth, data types) to your configured webhook Destination. - User Debugging (Users Tab): View connection status for a `user_id`, request historical data, potentially trigger backfills. ## 8. Security Best Practices Summary - API Key (`x-api-key`): This is highly sensitive. NEVER embed it directly in client-side code (mobile app, frontend JavaScript). It should only be used for server-to-server API calls from your backend. - Tokens (SDK & Streaming): - SDK Initialization Token (`/v2/auth/generateAuthToken`): Generated by your backend and passed to the mobile client. It's single-use or short-lived, mitigating risk if intercepted. - User Streaming Token (`/auth/user`): Generated by your backend (requires `user_id`) and passed to the mobile client for RT SDK to connect to the broker. - Consumer Streaming Token (API Key): Your main API Key is used by your backend to authenticate with the WebSocket broker (`IDENTIFY` Op 3). - Authentication URLs / Widget Redirection: - NO WebViews/IFrames for Auth: Due to security risks (URL bar spoofing, clickjacking), providers may block authentication attempts from WebViews or iframes. Always use a secure in-app browser (like `SFSafariViewController` on iOS, Chrome Custom Tabs on Android) or redirect to the system's default browser for provider login. - `reference_id`: Use this consistently and correctly during user authentication to reliably map Terra's `user_id` back to your application's internal user identifier. This is crucial for data integrity and associating data with the correct user in your system. - Webhook Security: - Use HTTPS for your Destination URL. - Every webhook sent by Terra will include HMAC-based signature header terra-signature , which will take the form: - `t=1723808700,v1=a5ee9dba96b4f65aeff6c841aa50121b1f73ec7990d28d53b201523776d4eb00` - Verification: We recommend that you verify webhooks. You can create a custom solution by following this section. - The `terra-signature` header included in each signed event contains a timestamp and one or more signatures that you must verify. - the timestamp is prefixed by `t=` - each signature is prefixed by a scheme. Schemes start with `v`, followed by an integer. (e.g. `v1`) Example: ``` terra-signature: t=1492774577, v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd, v0=6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39 ``` Terra generates signatures using a hash-based message authentication code (HMAC) with SHA-256. To prevent downgrade attacks, ignore all schemes that aren’t `v1` To create a manual solution for verifying signatures, you must complete the following steps: Step 1: Extract the timestamp and signatures from the header Split the header using the `,` character as the separator to get a list of elements. Then split each element using the `=` character as the separator to get a prefix and value pair. The value for the prefix `t` corresponds to the timestamp, and `v1` corresponds to the signature (or signatures). You can discard all other elements. Step 2: Prepare the signed_payloadstring The `signed_payload` string is created by concatenating: The timestamp (as a string) The character `.` The actual JSON payload (that is, the request body) Step 3: Determine the expected signature Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the `signed_payload` string as the message. Step 4: Compare the signatures Compare the signature (or signatures) in the header to the expected signature. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance. To protect against timing attacks, use a constant-time-string comparison to compare the expected signature to each of the received signatures. - Data Handling & Storage: - Store sensitive health data securely, complying with relevant data privacy regulations (e.g., GDPR, HIPAA if applicable). - Implement robust error handling and data validation on your backend when processing webhook data. - Assume webhook data is a superset; use unique identifiers to overwrite or update your stored records correctly. ```json {"openapi":"3.1.0","info":{"description":"The Terra API","title":"TerraAPI","version":"2022.03.16"},"servers":[{"url":"https://api.tryterra.co/v2"}],"paths":{"/auth/authenticateUser":{"post":{"summary":"Generate an authentication link","description":"Creates a login link that allows end users to connect their fitness tracking account","tags":["Authentication"],"operationId":"AuthenticateUser","parameters":[{"name":"resource","in":"query","description":"resource to authenticate user with","schema":{"type":"string"},"required":true},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"language":{"type":"string"},"reference_id":{"type":"string"},"auth_success_redirect_url":{"type":"string"},"auth_failure_redirect_url":{"type":"string"}}}}},"required":false},"responses":{"200":{"description":"Returned when authentication link could be successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["success","error"],"description":"indicates that the request was successful","example":"success"},"user_id":{"description":"User ID for the user being created","type":"string","example":"23dc2540-7139-44c6-8158-f81196e2cf2e"},"auth_url":{"type":"string","description":"authentication URL the user must be redirected to in order to link their account","example":"https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=23BBG9&scope=settings+nutrition+sleep+heartrate+electrocardiogram+weight+respiratory_rate+oxygen_saturation+profile+temperature+cardio_fitness+activity+location&state=bLqqjPie9ptwoWm6VBxHCu6JkkoWJp"}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. resource)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["success","error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/auth/generateWidgetSession":{"post":{"summary":"Generate an authentication link, using the Terra Authentication Widget","description":"Generates a link to redirect an end user to for them to select an integration and log in with their fitness data provider","tags":["Authentication"],"operationId":"GenerateWidgetSession","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WidgetSessionParams"}}},"required":true},"responses":{"200":{"description":"Returned when authentication link could be successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"description":"Session ID for the widget authentication session","type":"string","example":"23dc2540-7139-44c6-8158-f81196e2cf2e"},"url":{"type":"string","description":"the widget URL the user must be redirected to in order to link their account","example":"https://widget.tryterra.co/session/344d475f-296a-489a-a88c-54183671dafd"},"status":{"type":"string","enum":["success","error"],"description":"indicates that an error happened (value is success)","example":"success"},"expires_in":{"type":"number","description":"a number in seconds depicting how long the url is valid for","example":900}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/users/{user_id}":{"patch":{"summary":"Modify user","description":"Update a Terra user's reference_id or active status","tags":["User Management"],"operationId":"ModifyUser","parameters":[{"name":"user_id","in":"path","description":"Terra user ID to update","required":true,"schema":{"type":"string"}},{"name":"dev-id","in":"header","description":"Your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"Your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reference_id":{"type":"string","description":"Identifier on your system to associate with this user","example":"updatedUser123"},"active":{"type":"boolean","description":"Whether the user should remain active"}}}}}},"responses":{"200":{"description":"Returned upon successful user modification","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"status":{"type":"string","enum":["success","error"],"description":"Indicates that the request was successful","example":"success"}}}}}},"400":{"description":"Returned if the parameters are malformed or no user is found","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"A detailed message describing the error","type":"string"},"status":{"type":"string","enum":["success","error"],"description":"Indicates an error happened"}}}}}}}}},"/auth/deauthenticateUser":{"delete":{"summary":"Deauthenticates a user and deletes any cached data for them","description":"Deletes all records of the user on Terra's end, revoking Terra's access to their data","tags":["Authentication"],"operationId":"DeauthenticateUser","parameters":[{"name":"user_id","in":"query","description":"user_id to deauthenticate for","schema":{"type":"string"},"required":true},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned when authentication link could be successfully generated","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"enum":["success","error"],"description":"indicates that an error happened (value is success)","type":"string"}}}}}},"404":{"description":"Returned when the user_id is not existent","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["success","error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/auth/generateAuthToken":{"post":{"tags":["Authentication"],"summary":"Generates an authentication token for the Terra mobile SDKs","description":"Creates a token to be used with initConnection() functions in the Terra mobile SDKs in order to create a user record for Apple Health or Samsung Health (or equivalent)","operationId":"generate-authentication-token","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"200","content":{"application/json":{"examples":{"Result":{"value":"{\n \"status\": \"success\",\n \"token\": \"250c68b9c21b78e40e7a3285a2d538d3bc24aabd3b4c76a782fb0a571ca4501d\",\n \"expires_in\": 180\n}"}},"schema":{"type":"object","properties":{"status":{"type":"string","example":"success"},"token":{"type":"string","example":"250c68b9c21b78e40e7a3285a2d538d3bc24aabd3b4c76a782fb0a571ca4501d"},"expires_in":{"type":"integer","example":180,"default":0}}}}}},"404":{"description":"404","content":{"application/json":{"examples":{"Result":{"value":"{\n \"status\": \"error\",\n \"message\": \"Invalid dev-id was provided\"\n}"}},"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"message":{"type":"string","example":"Invalid dev-id was provided"}}}}}}},"deprecated":false}},"/activity":{"get":{"summary":"Retrieve activity data for a given user ID","description":"Fetches completed workout sessions, with a defined start and end time and activity type (e.g. running, cycling, etc.)","tags":["Data retrieval"],"operationId":"GetActivity","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"with_samples","in":"query","description":"boolean flag specifying whether to include detailed samples in the returned payload","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Activity"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"},{"$ref":"#/components/schemas/RequestProcessing"},{"$ref":"#/components/schemas/RateLimitRequestProcessing"},{"$ref":"#/components/schemas/LargeRequestProcessingResponse"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"post":{"tags":["Data writing"],"operationId":"PostActivity","description":"Used to post activity data to a provider. Available for Wahoo","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"List of user-tracked workouts to post to data provider","type":"array","items":{"$ref":"#/components/schemas/Activity"}}},"required":["data"]}}},"required":true},"responses":{"201":{"description":"Returned when activity was successfully created on the provider","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"log_ids":{"description":"List of identifiers for the objects created, returned in the same order they were posted. I.e. Posting [ObjectA, ObjectB] will return [IdentifierA, IdentifierB]","type":"array","items":{"type":"string"}},"message":{"type":"string","default":"Activity successfully logged"}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/athlete":{"get":{"summary":"Retrieve user profile info for a given user ID","description":"Fetches relevant profile info such as first & last name, birth date etc. for a given user ID","tags":["Data retrieval"],"operationId":"GetAthlete","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/AthleteCollection"},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/body":{"get":{"summary":"Retrieve body metrics for a given user ID","description":"Fetches body metrics such as weight, height, body fat percentage etc. for a given user ID","tags":["Data retrieval"],"operationId":"GetBody","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"with_samples","in":"query","description":"boolean flag specifying whether to include detailed samples in the returned payload","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Body"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"},{"$ref":"#/components/schemas/RequestProcessing"},{"$ref":"#/components/schemas/RateLimitRequestProcessing"},{"$ref":"#/components/schemas/LargeRequestProcessingResponse"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"post":{"tags":["Data writing"],"operationId":"PostBody","description":"Used to post body data to a provider. Available for Google Fit","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Body measurement metrics to post to data provider","type":"array","items":{"$ref":"#/components/schemas/Body"}}},"required":["data"]}}},"required":true},"responses":{"201":{"description":"Returned when activity was successfully created on the provider","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"log_ids":{"description":"List of identifiers for the objects created, returned in the same order they were posted. I.e. Posting [ObjectA, ObjectB] will return [IdentifierA, IdentifierB]","type":"array","items":{"type":"string"}},"message":{"type":"string","default":"Body data successfully logged"}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"delete":{"tags":["Data writing"],"operationId":"DeleteBody","description":"Used to delete Body metrics the user has registered on their account","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"log_ids":{"type":"array","description":"List of identifiers for body metrics entries to be deleted","items":{"type":"string"},"required":["data"]}}}}},"required":true},"responses":{"200":{"description":"Returned when all records were deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"processed_data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier of the body metric entries whose deletion was attempted"},"response_code":{"type":"integer","description":"Response code from the provider when attempting to delete the body metric entries"}}}}}}}}},"207":{"description":"Returned when multiple status codes were obtained from attempting to delete the requested records","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"processed_data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier of the body metric entry whose deletion was attempted"},"response_code":{"type":"integer","description":"Response code from the provider when attempting to delete the body metric entry"}}}}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/daily":{"get":{"summary":"Retrieve daily activity summaries for a given user ID","description":"Fetches daily summaries of activity metrics such as steps, distance, calories burned etc. for a given user ID","tags":["Data retrieval"],"operationId":"GetDaily","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"with_samples","in":"query","description":"boolean flag specifying whether to include detailed samples in the returned payload","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Daily"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"},{"$ref":"#/components/schemas/RequestProcessing"},{"$ref":"#/components/schemas/RateLimitRequestProcessing"},{"$ref":"#/components/schemas/LargeRequestProcessingResponse"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/menstruation":{"get":{"summary":"Retrieve menstruation data for a given user ID","description":"Fetches menstruation data such as cycle length, period length, ovulation date etc. for a given user ID","tags":["Data retrieval"],"operationId":"GetMenstruation","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"with_samples","in":"query","description":"boolean flag specifying whether to include detailed samples in the returned payload","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Menstruation"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"},{"$ref":"#/components/schemas/RequestProcessing"},{"$ref":"#/components/schemas/RateLimitRequestProcessing"},{"$ref":"#/components/schemas/LargeRequestProcessingResponse"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/nutrition":{"get":{"summary":"Retrieve nutrition log data for a given user ID","description":"Fetches nutrition log data such as meal type, calories, macronutrients etc. for a given user ID","tags":["Data retrieval"],"operationId":"GetNutrition","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"with_samples","in":"query","description":"boolean flag specifying whether to include detailed samples in the returned payload","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Nutrition"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"},{"$ref":"#/components/schemas/RequestProcessing"},{"$ref":"#/components/schemas/RateLimitRequestProcessing"},{"$ref":"#/components/schemas/LargeRequestProcessingResponse"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"post":{"tags":["Data writing"],"operationId":"PostNutrition","description":"Used to post nutrition logs to a provider. Available for Fitbit","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"Nutrition entry to post to data provider","type":"array","items":{"$ref":"#/components/schemas/Nutrition"}}},"required":["data"]}}},"required":true},"responses":{"201":{"description":"Returned when activity was successfully created on the provider","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"log_ids":{"description":"List of identifiers for the objects created, returned in the same order they were posted. I.e. Posting [ObjectA, ObjectB] will return [IdentifierA, IdentifierB]","type":"array","items":{"type":"string"}},"message":{"type":"string","default":"Nutrition successfully logged"}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"delete":{"tags":["Data writing"],"operationId":"DeleteNutrition","description":"Used to delete nutrition logs the user has registered on their account","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","description":"List of identifiers for nutrition entries to be deleted","items":{"type":"string"},"required":["data"]}}}}},"required":true},"responses":{"200":{"description":"Returned when all records were deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"processed_data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier of the nutrition log whose deletion was attempted"},"response_code":{"type":"integer","description":"Response code from the provider when attempting to delete the nutrition log"}}}}}}}}},"207":{"description":"Returned when multiple status codes were obtained from attempting to delete the requested records","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"processed_data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier of the nutrition log whose deletion was attempted"},"response_code":{"type":"integer","description":"Response code from the provider when attempting to delete the nutrition log"}}}}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"enum":["success","error"],"type":"string","description":"indicates that an error happened (value is error)"}}}}}}}}},"/sleep":{"get":{"summary":"Retrieve sleep sessions for a given user ID","description":"Fetches sleep data such as sleep duration, sleep stages, sleep quality etc. for a given user ID, for sleep sessions with a defined start and end time","tags":["Data retrieval"],"operationId":"GetSleep","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"with_samples","in":"query","description":"boolean flag specifying whether to include detailed samples in the returned payload","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/Sleep"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"},{"$ref":"#/components/schemas/RequestProcessing"},{"$ref":"#/components/schemas/RateLimitRequestProcessing"},{"$ref":"#/components/schemas/LargeRequestProcessingResponse"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/plannedWorkout":{"post":{"tags":["Data writing"],"operationId":"PostPlannedWorkout","description":"Used to post workout plans users can follow on their wearable. This can be stregnth workouts (sets, reps, weight lifted) or cardio workouts (warmup, intervals of different intensities, cooldown etc)","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"description":"PlannedWorkout entry to post to data provider","type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}}},"required":["data"]}}},"required":true},"responses":{"201":{"description":"Returned when activity was successfully created on the provider","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"log_ids":{"description":"List of identifiers for the objects created, returned in the same order they were posted. I.e. Posting [ObjectA, ObjectB] will return [IdentifierA, IdentifierB]","type":"array","items":{"type":"string"}},"message":{"type":"string","default":"Nutrition successfully logged"}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"get":{"summary":"Retrieve workout plans for a given user ID","description":"Used to get workout plans the user has registered on their account. This can be stregnth workouts (sets, reps, weight lifted) or cardio workouts (warmup, intervals of different intensities, cooldown etc)","tags":["Data retrieval"],"operationId":"GetPlannedWorkout","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"start_date","in":"query","description":"start date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":true},{"name":"end_date","in":"query","description":"end date of data to query for - either ISO8601 date or unix timestamp","schema":{"oneOf":[{"type":"integer"},{"type":"string","format":"date"}]},"required":false},{"name":"to_webhook","in":"query","description":"boolean flag specifying whether to send the data retrieved to the webhook, or in the response","schema":{"type":"boolean"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned upon successful data request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PlannedWorkout"}},"type":{"type":"string","nullable":true}}},{"$ref":"#/components/schemas/NoDataReturned"},{"$ref":"#/components/schemas/DataSentToWebhook"}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when credentials (dev ID and API key) are invalid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"An error message","type":"string","example":"unauthorized"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}},"delete":{"tags":["Data writing"],"operationId":"DeletePlannedWorkout","description":"Used to delete workout plans the user has registered on their account. This can be stregnth workouts (sets, reps, weight lifted) or cardio workouts (warmup, intervals of different intensities, cooldown etc)","parameters":[{"name":"user_id","in":"query","description":"user ID to query data for","schema":{"type":"string"},"required":true},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","description":"List of identifiers for planned workout entries to be deleted","items":{"type":"string"},"required":["data"]}}}}},"required":true},"responses":{"200":{"description":"Returned when all records were deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"processed_data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier of the planned workout whose deletion was attempted"},"response_code":{"type":"integer","description":"Response code from the provider when attempting to delete the planned workout"}}}}}}}}},"207":{"description":"Returned when multiple status codes were obtained from attempting to delete the requested records","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"processed_data":{"type":"object","properties":{"id":{"type":"string","description":"Identifier of the planned workout whose deletion was attempted"},"response_code":{"type":"integer","description":"Response code from the provider when attempting to delete the planned workout"}}}}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"401":{"description":"Returned when authorization with a data provider has failed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"enum":["success","error"],"type":"string","description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/userInfo":{"get":{"tags":["User Info"],"operationId":"GetUserInfo","description":"Used to query for information on one Terra user ID, or to query for all registered Terra User objects under one reference ID","parameters":[{"name":"user_id","in":"query","description":"user ID to query for","schema":{"type":"string"},"required":false},{"name":"reference_id","in":"query","description":"reference ID to query for","schema":{"type":"string"},"required":false},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"responses":{"200":{"description":"Returned when the provided resources are found","content":{"application/json":{"schema":{"oneOf":[{"description":"User information for one connection (single User object)","type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"status":{"type":"string","enum":["success","error"],"default":"success"},"is_authenticated":{"type":"boolean","nullable":false}}},{"description":"List of multiple User objects the reference_id is associated to","type":"array","items":{"$ref":"#/components/schemas/User"}}]}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/subscriptions":{"get":{"tags":["User Info"],"operationId":"GetAllUsers","description":"Used to query for information for all Terra User IDs. Supports optional pagination via `page` and `per_page`. If `page` is not provided, it returns all users in one go (backwards compatibility).","parameters":[{"name":"dev-id","in":"header","required":true,"schema":{"type":"string"},"description":"Your developer ID","example":"testingTerra"},{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string"},"description":"Your API key","example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"},{"name":"page","in":"query","required":false,"schema":{"type":"integer"},"description":"Zero-based page number. If omitted, results are not paginated.","example":0},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer"},"description":"Number of results per page (default is 500).","example":500}],"responses":{"200":{"description":"Returned upon a successful request","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}},{"type":"object","properties":{"data":{"type":"object","properties":{"next":{"type":"integer","nullable":true,"description":"The next page number, or null if there is no next page"},"max_page":{"type":"integer","description":"The maximum page index"},"results":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}}]}}}},"400":{"description":"Returned when one or more parameters are malformed","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"A detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"Indicates that an error occurred (value is `error`)"}}}}}}}}},"/bulkUserInfo":{"post":{"tags":["User Info"],"operationId":"GetBulkInfo","description":"Used to query for information for multiple Terra User IDs","parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"},"example":"testingTerra"},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"},"example":"OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ"}],"requestBody":{"content":{"application/json":{"schema":{"type":"array","description":"List of user IDs to get information for","items":{"type":"string"}}}},"required":true},"responses":{"200":{"description":"Returned upon successful request","content":{"application/json":{"schema":{"description":"List of User objects","type":"array","items":{"$ref":"#/components/schemas/User"}}}}},"400":{"description":"Returned when one or more parameters is malformed - an appropriate error message will be returned","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}},"404":{"description":"Returned when a parameter does not exist on Terra's end (e.g. user_id)","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"a detailed message describing the error","type":"string"},"status":{"type":"string","enum":["error"],"description":"indicates that an error happened (value is error)"}}}}}}}}},"/integrations":{"get":{"summary":"Get list of available integrations","tags":["Integrations"],"operationId":"GetIntegrations","responses":{"200":{"description":"Returns list of all available integrations on the API","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"string","example":"FITBIT"}},"sdk_resource":{"type":"array","items":{"type":"string","example":"APPLE"}},"status":{"enum":["success","error"],"type":"string","default":"success"}}}}}}},"security":[]}},"/integrations/detailed":{"get":{"tags":["Integrations"],"summary":"Get detailed list of integrations","description":"Retrieve a detailed list of supported integrations, optionally filtered by the developer's enabled integrations and the requirement for SDK usage.","parameters":[{"in":"query","name":"sdk","required":false,"schema":{"type":"boolean"},"description":"If `true`, allows SDK integrations to be included in the response."},{"in":"header","name":"dev-id","required":false,"schema":{"type":"string"},"description":"Developer ID to filter the integrations list based on the developer's enabled integrations."}],"responses":{"200":{"description":"Successful response containing a list of integrations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationsResponse"}}}}},"security":[]}}},"components":{"schemas":{"WidgetSessionParams":{"type":"object","properties":{"providers":{"type":"string","description":"Comma separated list of providers to display on the device selection page. This overrides your selected sources on your dashboard","example":"GARMIN,FITBIT,OURA,WITHINGS,SUUNTO"},"language":{"type":"string","description":"Display language of the widget","example":"en"},"reference_id":{"type":"string","description":"Identifier of the end user on your system, such as a user ID or email associated with them","example":"user123@email.com"},"auth_success_redirect_url":{"type":"string","description":"URL the user is redirected to upon successful authentication","example":"https://myapp.com/success"},"auth_failure_redirect_url":{"type":"string","description":"URL the user is redirected to upon unsuccessful authentication","example":"https://myapp.com/failure"}}},"User":{"type":"object","properties":{"reference_id":{"type":"string","description":"Connection identifier on the developer's end, used to tie connection back to a user on the developer's platform","nullable":true,"example":"user123@email.com"},"last_webhook_update":{"type":"string","description":"Last time at which a webhook update was sent for the connection","format":"date-time","example":"2022-12-12T10:00:00.000000+00:00","nullable":true},"scopes":{"type":"string","description":"(when available) Permissions granted by the user during authentication - to be used as debugging metadata","nullable":true,"example":"activity:read,sleep:read"},"user_id":{"type":"string","description":"Terra identifier for the wearable connection","nullable":true,"example":"123e4567-e89b-12d3-a456-426614174000"},"provider":{"type":"string","description":"Connection data source","nullable":true,"example":"FITBIT"},"active":{"type":"string","description":"whether the user is active or not (inactive users will not receive any data updates and are in considered dormant)","nullable":false,"example":true}}},"NoDataReturned":{"type":"object","properties":{"status":{"type":"string","enum":["success","error"],"nullable":false,"example":"error"},"message":{"type":"string","nullable":false,"example":"No data available for specified time range"},"type":{"type":"string","nullable":true,"example":"no_data"},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]}}},"TerraDataModel":{"type":"object","properties":{}},"DataReturned":{"type":"object","properties":{"data":{"type":"array","description":"List of objects returned by the API, after normalization","items":{"$ref":"#/components/schemas/TerraDataModel"}},"type":{"type":"string","nullable":true,"example":"activity"},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]}}},"RequestProcessing":{"type":"object","properties":{"retry_after_seconds":{"type":"number","description":"Recommended time after which the request may be retried","nullable":true,"example":30},"message":{"type":"string","nullable":false,"example":"Request is being processed"},"type":{"type":"string","nullable":false,"example":"processing"},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]}}},"RateLimitRequestProcessing":{"type":"object","properties":{"message":{"type":"string","nullable":false,"example":"Rate limit exceeded"},"type":{"type":"string","nullable":false,"example":"rate_limit"},"user":{"$ref":"#/components/schemas/User"}},"required":["user"]},"DataSentToWebhook":{"type":"object","properties":{"reference":{"type":"string","description":"Payload reference, tying the request to the webhook payload which will be received","nullable":true,"example":"webhook_ref_123"},"message":{"type":"string","nullable":false,"example":"Data will be sent to webhook"},"type":{"type":"string","nullable":false,"example":"webhook"},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]}}},"ActivityLevelSample":{"type":"object","properties":{"activity_level":{"description":"Intensity of the user's activity at an instant in time","nullable":false,"type":"string","enum":["UNKNOWN","REST","INACTIVE","LOW_INTENSITY","MEDIUM_INTENSITY","HIGH_INTENSITY"],"example":"MEDIUM_INTENSITY"},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond prexcision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"ActiveDurationsData":{"type":"object","properties":{"activity_levels_samples":{"type":"array","description":"Array of detailed samples of the intensity the user was in at various points during the workout","items":{"$ref":"#/components/schemas/ActivityLevelSample"}},"num_continuous_inactive_periods":{"type":"integer","description":"Maximum number of continuous periods spent in an inactive state during the workout","minimum":0,"nullable":true,"example":3},"inactivity_seconds":{"type":"number","description":"Total number of seconds spent in an inactive state during the workout","minimum":0,"nullable":true,"example":600},"vigorous_intensity_seconds":{"type":"number","description":"Total number of seconds spent in a state of vigorous intensity during the workout","minimum":0,"nullable":true,"example":300},"activity_seconds":{"type":"number","description":"Total number of seconds spent in an active state during the workout","minimum":0,"nullable":true,"example":1800},"low_intensity_seconds":{"type":"number","description":"Total number of seconds spent in a low intensity state during the workout","minimum":0,"nullable":true,"example":900},"rest_seconds":{"type":"number","description":"Total number of seconds spent resting during the workout","minimum":0,"nullable":true,"example":120},"moderate_intensity_seconds":{"type":"number","description":"Total number of seconds spent in a moderate intensity state during the workout","minimum":0,"nullable":true,"example":600}}},"LapSample":{"type":"object","properties":{"calories":{"type":"number","description":"Calories burned during the lap","nullable":true,"example":50},"avg_hr_bpm":{"type":"number","description":"Average heart rate in bpm, for the Lap sample","nullable":true,"example":145},"start_time":{"type":"string","description":"The start time of the associated lap, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"avg_speed_meters_per_second":{"type":"number","description":"Average speed of the user during the lap","nullable":true,"example":3.5},"distance_meters":{"type":"number","description":"Distance covered during the lap","nullable":true,"example":400},"total_strokes":{"type":"number","description":"Total strokes performed during the lap - only relevant for swimming activities","nullable":true,"example":20},"end_time":{"type":"string","description":"The end time of the associated lap, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-10-28T10:00:00.000000+01:00","format":"date-time","nullable":true},"stroke_type":{"description":"Stroke type - only relevant for swimming activities","nullable":false,"type":"string","enum":["OTHER","FREESTYLE","BACKSTROKE","BREASTSTROKE","BUTTERFLY","REST"],"example":"FREESTYLE"}}},"LapData":{"type":"object","properties":{"laps":{"type":"array","description":"Array of datapoints for each lap performed by the user during the workout","items":{"$ref":"#/components/schemas/LapSample"}}}},"PolylineMapData":{"type":"object","properties":{"summary_polyline":{"type":"string","description":"The polyline representation of the user's trajectory throughout the workout","nullable":true,"example":"}_p~iF~ps|U_ulLnnqC_mqNvxq`@"}}},"StrainData":{"type":"object","properties":{"strain_level":{"type":"number","description":"Strain level achieved by the user for the given workout","nullable":true,"example":8.5}}},"PositionSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":300},"coords_lat_lng_deg":{"type":"array","description":"Position of the user a given point in time, represented by a 2-value array of latitude, longitude, wherein each of the two numbers can be nullable","example":[-18.4911,130.9123],"nullable":false,"items":{"type":"number"}}}},"PositionData":{"type":"object","properties":{"position_samples":{"type":"array","description":"Array of datapoints of the position of the user, sampled throughout the workout","items":{"$ref":"#/components/schemas/PositionSample"}},"end_pos_lat_lng_deg":{"type":"array","description":"Position of the user at the end of the workout, represented by a 2-value array of latitude, longitude, wherein each of the two numbers can be nullable","example":[-84.4911,-150.9123],"nullable":false,"items":{"type":"number"}},"center_pos_lat_lng_deg":{"type":"array","description":"Position of the user at the midway point of the workout, represented by a 2-value array of latitude, longitude, wherein each of the two numbers can be nullable","example":[-18.4911,130.9123],"nullable":false,"items":{"type":"number"}},"start_pos_lat_lng_deg":{"type":"array","description":"Position of the user at the start of the workout, represented by a 2-value array of latitude, longitude, wherein each of the two numbers can be nullable","example":[80.2394,102.931],"nullable":false,"items":{"type":"number"}}}},"CadenceSample":{"type":"object","properties":{"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":300},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"cadence_rpm":{"type":"number","description":"Cadence of the user at a given instant in time - only relevant to cycling","nullable":true,"example":85}}},"TorqueSample":{"type":"object","properties":{"torque_newton_meters":{"type":"number","description":"Torque generated at a given instant in time","nullable":true,"example":45.5},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":300}}},"SpeedSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":300},"speed_meters_per_second":{"type":"number","description":"User's speed at a given instant in time","nullable":true,"example":3.5}}},"MovementData":{"type":"object","properties":{"max_pace_minutes_per_kilometer":{"type":"number","description":"Maximum pace of the user during the workout","minimum":0,"nullable":true,"example":6.5},"adjusted_max_speed_meters_per_second":{"type":"number","description":"Adjusted max speed of the user during the workout - generated using Terra's algorithms to remove potential affecting the user's actual max speed measurement","minimum":0,"nullable":true,"example":4.2},"avg_velocity_meters_per_second":{"type":"number","description":"Average velocity of the user during the workout - only calculated by certain providers, representing a separate quantity from speed","minimum":0,"nullable":true,"example":3},"max_velocity_meters_per_second":{"type":"number","description":"Maximum velocity of the user during the workout - only calculated by certain providers, representing a separate quantity from speed","minimum":0,"nullable":true,"example":4.5},"avg_speed_meters_per_second":{"type":"number","description":"Average speed of the user during the workout","minimum":0,"nullable":true,"example":3.2},"avg_torque_newton_meters":{"type":"number","description":"Average torque generated by the user during the workout - mainly relevant for cycling activities","minimum":0,"nullable":true,"example":40},"cadence_samples":{"type":"array","description":"Array of cadence values recorded throughout the workout, sampled at intervals determined by the fitness data provider","items":{"$ref":"#/components/schemas/CadenceSample"}},"max_speed_meters_per_second":{"type":"number","description":"Maximum speed of the user during the workout","minimum":0,"nullable":true,"example":4.5},"avg_pace_minutes_per_kilometer":{"type":"number","description":"Average pace of the user during the workout","minimum":0,"nullable":true,"example":6.2},"avg_cadence_rpm":{"type":"number","description":"Maximum speed of the user during the workout","minimum":0,"nullable":true,"example":85},"max_torque_newton_meters":{"type":"number","description":"Maximum torque generated by the user during the workout - mainly relevant for cycling activities","minimum":0,"nullable":true,"example":55.5},"torque_samples":{"type":"array","description":"Array of the datapoints for the user's torque sampled throughout the workout","items":{"$ref":"#/components/schemas/TorqueSample"}},"normalized_speed_meters_per_second":{"type":"number","description":"Average normalized speed of the user during the workout - only calculated by certain providers, representing a separate quantity from speed","minimum":0,"nullable":true,"example":3.8},"max_cadence_rpm":{"type":"number","description":"Maximum cadence of the user during the workout - mainly relevant for cycling activities","minimum":0,"nullable":true,"example":110},"speed_samples":{"type":"array","description":"Array of the datapoints for the user's speed sampled throughout the workout","items":{"$ref":"#/components/schemas/SpeedSample"}}}},"Vo2MaxSample":{"type":"object","properties":{"vo2max_ml_per_min_per_kg":{"type":"number","description":"User's VO2Max - maximum amount of oxygen the user's body can utilize during exercise","nullable":true,"example":45.5},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"RRIntervalSample":{"type":"object","properties":{"rr_interval_ms":{"type":"number","description":"User's RR Interval for a specific heart beat in milliseconds","nullable":true,"example":850},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"hr_bpm":{"type":"number","description":"The heart beat value at that specific instance","nullable":true,"example":72}}},"OxygenSaturationSample":{"type":"object","properties":{"percentage":{"type":"number","description":"User's oxygen saturation percentage - referring to either SpO2 or SmO2, based on the `type` field","nullable":true,"example":98},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"type":{"description":"Type of oxygen saturation measurement (i.e. blood vs muscle)","nullable":false,"oneOf":[{"type":"integer","enum":[0],"description":"Oxygen saturation measurement for blood."},{"type":"integer","enum":[1],"description":"Oxygen saturation measurement for muscle."}]}}},"OxygenData":{"type":"object","properties":{"vo2max_ml_per_min_per_kg":{"type":"number","description":"VO2Max for the given user","nullable":true,"example":42.5},"avg_saturation_percentage":{"type":"number","description":"Average Oxygen Saturation percentage of the user during the workout (SpO2 or SmO2)","nullable":true,"example":97},"vo2_samples":{"type":"array","description":"Array of VO2 datapoints sampled throughout the workout","items":{"$ref":"#/components/schemas/Vo2MaxSample"}},"saturation_samples":{"type":"array","description":"Array of Oxygen Saturation percentage datapoints sampled throughout the workout","items":{"$ref":"#/components/schemas/OxygenSaturationSample"}}}},"OtherDeviceData":{"type":"object","properties":{"manufacturer":{"type":"string","description":"Device manufacturer name","nullable":true,"example":"Garmin"},"hardware_version":{"type":"string","description":"Hardware version of the device","nullable":true,"example":"v2.1"},"serial_number":{"type":"string","description":"Device Serial Number","nullable":true,"example":"GR123456789"},"name":{"type":"string","description":"Device name - note that this can also be the name of the application/package which the data comes from, if coming from a data aggregator such as Google Fit","nullable":true,"example":"Forerunner 945"},"software_version":{"type":"string","description":"Device Software Version","nullable":true,"example":"5.2.1"}}},"DeviceData":{"type":"object","properties":{"manufacturer":{"type":"string","description":"Device manufacturer name","nullable":true,"example":"Garmin"},"hardware_version":{"type":"string","description":"Hardware version of the device","nullable":true,"example":"v2.1"},"other_devices":{"type":"array","description":"Data pertaining to other devices which may have contributed data for this workout","items":{"$ref":"#/components/schemas/OtherDeviceData"}},"activation_timestamp":{"type":"string","description":"Activation timestamp of the device, if applicable","format":"date-time","example":"2022-12-25T00:00:00.000000+05:00","nullable":true},"serial_number":{"type":"string","description":"Device Serial Number","nullable":true,"example":"GR123456789"},"name":{"type":"string","description":"Device name - note that this can also be the name of the application/package which the data comes from, if coming from a data aggregator such as Google Fit","nullable":true,"example":"Forerunner 945"},"software_version":{"type":"string","description":"Device Software Version","nullable":true,"example":"5.2.1"}}},"EnergyData":{"type":"object","properties":{"energy_planned_kilojoules":{"type":"number","description":"Total number of kiloJoules planned to be expended during the workout - represents the user's predefined goal for the workout","nullable":true,"example":1200},"energy_kilojoules":{"type":"number","description":"Total number of kiloJoules expended during the workout","nullable":true,"example":1150}}},"METSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"level":{"type":"number","description":"Metabolic Equivalent of Task of the user's activity level at an instant in time","nullable":true,"example":4.2}}},"METData":{"type":"object","properties":{"num_inactive_minutes":{"type":"number","description":"Number of minutes spent in state of Inactivity during the workout - based off MET scale","minimum":0,"nullable":true,"example":5},"MET_samples":{"type":"array","description":"An array of Metabolic Equivalent Time samples, as calculated by the user's wearable","items":{"$ref":"#/components/schemas/METSample"}},"num_low_intensity_minutes":{"type":"number","description":"Number of minutes spent in Low Intensity during the workout - based off MET scale","minimum":0,"nullable":true,"example":15},"num_moderate_intensity_minutes":{"type":"number","description":"Number of minutes spent in Moderate Intensity during the workout - based off MET scale","minimum":0,"nullable":true,"example":25},"avg_level":{"type":"number","description":"The average MET level of the activity","nullable":true,"example":3.8},"num_high_intensity_minutes":{"type":"number","description":"Number of minutes spent in High Intensity during the workout - based off MET scale","minimum":0,"nullable":true,"example":10}}},"PowerSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":1800},"watts":{"type":"number","description":"Power generated at a given instant in time","nullable":true,"example":250}}},"PowerData":{"type":"object","properties":{"avg_watts":{"type":"number","description":"Average power output of the user during the workout","minimum":0,"nullable":true,"example":185},"max_watts":{"type":"number","description":"Maximum power output of the user during the workout","minimum":0,"nullable":true,"example":350},"power_samples":{"type":"array","description":"Array containing datapoints of the power output of the user sampled throughout the workout","items":{"$ref":"#/components/schemas/PowerSample"}}}},"ElevationSummary":{"type":"object","properties":{"gain_planned_meters":{"type":"number","description":"Elevation gain of the user throughout the workout - this includes all elevation gain, and does _not_ represent net gain","minimum":0,"nullable":true,"example":500},"loss_actual_meters":{"type":"number","description":"Elevation loss of the user throughout the workout","minimum":0,"nullable":true,"example":450},"max_meters":{"type":"number","description":"Maximum elevation of the user during the workout","nullable":true,"example":1200},"min_meters":{"type":"number","description":"Minimum elevation of the user during the workout","nullable":true,"example":800},"gain_actual_meters":{"type":"number","description":"Planned elevation gain for the workout","minimum":0,"nullable":true,"example":550},"avg_meters":{"type":"number","description":"Average elevation of the user throughout the workout","nullable":true,"example":950}}},"SwimmingSummary":{"type":"object","properties":{"num_laps":{"type":"integer","description":"Total number of swimming laps performed during the workout","minimum":0,"nullable":true,"example":20},"num_strokes":{"type":"integer","description":"Total number of swimming strokes performed during the workout","minimum":0,"nullable":true,"example":500},"pool_length_meters":{"type":"integer","description":"Pool length for associated with the workout","minimum":0,"nullable":true,"example":25}}},"DistanceDataSummary":{"type":"object","properties":{"distance_meters":{"type":"number","description":"Total distance covered by the user throughout the workout","nullable":true,"example":5000},"elevation":{"description":"Average elevation of the user throughout the workout","type":"object","allOf":[{"$ref":"#/components/schemas/ElevationSummary"}]},"steps":{"type":"integer","description":"Total number of steps performed during the workout","nullable":true,"example":6500},"floors_climbed":{"type":"integer","description":"Total number of elevation gain in floors climbed equivalent throughout the workout, as determined by the fitness data provider","minimum":0,"nullable":true,"example":10},"swimming":{"description":"Summary information of the user's swimming statistics for the workout, if applicable","type":"object","allOf":[{"$ref":"#/components/schemas/SwimmingSummary"}]}}},"ElevationSample":{"type":"object","properties":{"elev_meters":{"type":"number","description":"User's altitude at a given point in time","nullable":true,"example":925},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":1200}}},"DistanceSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"distance_meters":{"type":"number","description":"Cumulative distance covered up to associated timestamp, since the start of the payload","nullable":true,"example":2500},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":900}}},"FloorsClimbedSample":{"type":"object","properties":{"floors_climbed":{"type":"number","description":"Cumulative number of elevation gain measured in floors climbed up to associated timestamp, since the start of the payload","nullable":true,"example":5},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":600}}},"StepSample":{"type":"object","properties":{"steps":{"type":"number","description":"Cumulative steps performed up to associated timestamp, since the start of the payload","nullable":true,"example":3250},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":900}}},"DistanceDataDetailed":{"type":"object","properties":{"elevation_samples":{"type":"array","description":"Array of detailed samples of elevation throughout the workout","items":{"$ref":"#/components/schemas/ElevationSample"}},"distance_samples":{"type":"array","description":"Array of detailed samples of distance covered throughout the workout","items":{"$ref":"#/components/schemas/DistanceSample"}},"floors_climbed_samples":{"type":"array","description":"Array of detailed samples of floors climbed throughout the workout, as determined by the fitness data provider","items":{"$ref":"#/components/schemas/FloorsClimbedSample"}},"step_samples":{"type":"array","description":"Array of detailed samples of steps performed throughout the workout","items":{"$ref":"#/components/schemas/StepSample"}}}},"DistanceData":{"type":"object","properties":{"summary":{"description":"Object containing summary information related to distance covered throughout the workout","type":"object","allOf":[{"$ref":"#/components/schemas/DistanceDataSummary"}]},"detailed":{"description":"Object containing detailed distance information - this may included second-by-second samples","type":"object","allOf":[{"$ref":"#/components/schemas/DistanceDataDetailed"}]}}},"HeartRateZone":{"type":"object","properties":{"zone":{"nullable":false,"type":"string","enum":["ZONE_0","ZONE_1","ZONE_2","ZONE_3","ZONE_4","ZONE_5","OTHER"],"example":"ZONE_2"},"start_percentage":{"type":"number","description":"Start percentage (based off user's max HR) of the HR zone","minimum":0,"maximum":100,"nullable":true,"example":60},"end_percentage":{"type":"number","description":"End percentage (based off user's max HR) of the HR zone","minimum":0,"maximum":100,"nullable":true,"example":70},"duration_seconds":{"type":"number","description":"Duration spent in the hart rate zone","nullable":true,"example":300},"name":{"type":"string","description":"Name of the associated heart rate zone","nullable":true,"example":"Aerobic"}}},"HeartRateDataSummary":{"type":"object","properties":{"resting_hr_bpm":{"type":"number","description":"Resting HeartRate of the user, as determined by the fitness data provider","minimum":0,"nullable":true,"example":65},"avg_hr_bpm":{"type":"number","description":"Average HeartRate of the user during the workout","minimum":0,"nullable":true,"example":145},"max_hr_bpm":{"type":"number","description":"Maximum HeartRate of the user during the workout","minimum":0,"nullable":true,"example":180},"avg_hrv_sdnn":{"type":"number","description":"Average HeartRate Variability of the user during the workout, computed using SDNN","nullable":true,"example":45.5},"hr_zone_data":{"type":"array","description":"Array of time spent in various HR zones throughout the workout","items":{"$ref":"#/components/schemas/HeartRateZone"}},"min_hr_bpm":{"type":"number","description":"Minimum HeartRate of the user during the workout","minimum":0,"nullable":true,"example":85},"avg_hrv_rmssd":{"type":"number","description":"Average HeartRate Variability of the user during the workout, computed using RMSSD","nullable":true,"example":35.2},"user_max_hr_bpm":{"type":"number","description":"User's maximum HeartRate based on their age, and other factors as determined by the fitness data provider","nullable":true,"example":190}}},"HeartRateVariabilityDataSampleRMSSD":{"type":"object","properties":{"hrv_rmssd":{"type":"number","description":"User's Heart Rate Variability, computed using RMSSD","nullable":true,"example":42.1},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"HeartRateDataSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"bpm":{"type":"number","description":"User's heart rate in bpm","nullable":true,"example":142}}},"HeartRateVariabilityDataSampleSDNN":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"hrv_sdnn":{"type":"number","description":"User's Heart Rate Variability, computed using SDNN","nullable":true,"example":48.3}}},"HeartRateDataDetailed":{"type":"object","properties":{"hrv_samples_rmssd":{"type":"array","description":"Array of HeartRate Variability data samples recorded for the user during the workout, computed using RMSSD","items":{"$ref":"#/components/schemas/HeartRateVariabilityDataSampleRMSSD"}},"hr_samples":{"type":"array","description":"Array of HeartRate data samples recorded for the user during the workout","items":{"$ref":"#/components/schemas/HeartRateDataSample"}},"hrv_samples_sdnn":{"type":"array","description":"Array of HeartRate Variability data samples recorded for the user during the workout, computed using SDNN","items":{"$ref":"#/components/schemas/HeartRateVariabilityDataSampleSDNN"}}}},"HeartRateData":{"type":"object","properties":{"summary":{"description":"Object containing summary heart rate information for the associated workout","type":"object","allOf":[{"$ref":"#/components/schemas/HeartRateDataSummary"}]},"detailed":{"description":"Object containing detailed heart rate information for the associated workout","type":"object","allOf":[{"$ref":"#/components/schemas/HeartRateDataDetailed"}]}}},"TSSSample":{"type":"object","properties":{"intensity_factor_planned":{"type":"number","description":"Planned","nullable":true,"example":0.85},"intensity_factor_actual":{"type":"number","description":"Ratio of Normalized Power to Functional Threshold Power (FTP). IF takes into account differences in fitness within or between individuals","nullable":true,"example":0.82},"normalized_power_watts":{"type":"number","description":"Adjusted Power calculated using an algorithm that into account the variance between a steady workout and a fluctuating workout. It measures the true physiological demands of a training session. It considers both rapid changes in intensity, as well as critical responses in the body associated with those changes.","nullable":true,"example":225},"planned":{"type":"number","description":"Planned Training Stress Score (TSS)","nullable":true,"example":85},"actual":{"type":"number","description":"Achieved Training Stress Score (TSS)","nullable":true,"example":82},"method":{"type":"string","description":"Method of measurement of the Training Stress Score","example":"HeartRate","nullable":true}}},"TSSData":{"type":"object","properties":{"TSS_samples":{"type":"array","description":"Array of TSS information sampled throughout the workout","items":{"$ref":"#/components/schemas/TSSSample"}}}},"CalorieSample":{"type":"object","properties":{"calories":{"type":"number","description":"Cumulative calories burned up to associated timestamp, since the start of the payload","nullable":true,"example":450},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"timer_duration_seconds":{"type":"number","description":"Time elapsed since the start of the workout, subtracting time during which the recording was paused","nullable":true,"example":1800}}},"CaloriesData":{"type":"object","properties":{"BMR_calories":{"type":"number","description":"BMR of the user on the specific day, which contributes to the total calories burned during the workout","minimum":0,"nullable":true,"example":1800},"net_activity_calories":{"type":"number","description":"Net calorie burn coming just from the workout itself, excluding BMR","minimum":0,"nullable":true,"example":450},"total_burned_calories":{"type":"number","description":"Total number of calories burned during the workout, including BMR","minimum":0,"nullable":true,"example":2250},"net_intake_calories":{"type":"number","description":"Net intake of calories during the period of the workout","minimum":0,"nullable":true,"example":200},"calorie_samples":{"type":"array","description":"Detailed samples of cumulative calories burned at various timestamps over the period of the workout","items":{"$ref":"#/components/schemas/CalorieSample"}}}},"ActivityMetadata":{"type":"object","properties":{"state":{"type":"string","description":"The State in which the workout was performed","nullable":true,"example":"California"},"timestamp_localization":{"oneOf":[{"type":"integer","enum":[0],"description":"UTC timestamps with +00:00 offset. This avoids \"Z\" notation to ensure compatibility across parsers."},{"type":"integer","enum":[1],"description":"LOCAL timestamps that may include or exclude the timezone, following ISO8601."}],"description":"Indicates whether the timestamps in this payload are localized (LOCAL) or in UTC","nullable":false,"example":0},"country":{"type":"string","description":"The Country in which the workout was performed","nullable":true,"example":"United States"},"start_time":{"type":"string","description":"The start time of the associated workout, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"summary_id":{"type":"string","description":"A unique identifier for the workout - note that this is unique for the given user, and may not be globally unique","nullable":true,"example":"123e4567-e89b-12d3-a456-426614174000"},"city":{"type":"string","description":"The City in which the workout was performed","nullable":true,"example":"San Francisco"},"type":{"description":"The type of activity performed for the associated workout","nullable":false,"type":"string","enum":["IN_VEHICLE","BIKING","STILL","UNKNOWN","TILTING","WALKING","RUNNING","AEROBICS","BADMINTON","BASEBALL","BASKETBALL","BIATHLON","HANDBIKING","MOUNTAIN_BIKING","ROAD_BIKING","SPINNING","STATIONARY_BIKING","UTILITY_BIKING","BOXING","CALISTHENICS","CIRCUIT_TRAINING","CRICKET","DANCING","ELLIPTICAL","FENCING","AMERICAN_FOOTBALL","AUSTRALIAN_FOOTBALL","ENGLISH_FOOTBALL","FRISBEE","GARDENING","GOLF","GYMNASTICS","HANDBALL","HIKING","HOCKEY","HORSEBACK_RIDING","HOUSEWORK","JUMPING_ROPE","KAYAKING","KETTLEBELL_TRAINING","KICKBOXING","KITESURFING","MARTIAL_ARTS","MEDITATION","MIXED_MARTIAL_ARTS","P90X_EXERCISES","PARAGLIDING","PILATES","POLO","RACQUETBALL","ROCK_CLIMBING","ROWING","ROWING_MACHINE","RUGBY","JOGGING","RUNNING_ON_SAND","TREADMILL_RUNNING","SAILING","SCUBA_DIVING","SKATEBOARDING","SKATING","CROSS_SKATING","INDOOR_ROLLERBLADING","SKIING","BACK_COUNTRY_SKIING","CROSS_COUNTRY_SKIING","DOWNHILL_SKIING","KITE_SKIING","ROLLER_SKIING","SLEDDING","SNOWBOARDING","SNOWMOBILE","SNOWSHOEING","SQUASH","STAIR_CLIMBING","STAIR_CLIMBING_MACHINE","STAND_UP_PADDLEBOARDING","STRENGTH_TRAINING","SURFING","SWIMMING","SWIMMING_SWIMMING_POOL","SWIMMING_OPEN_WATER","TABLE_TENNIS","TEAM_SPORTS","TENNIS","TREADMILL","VOLLEYBALL","VOLLEYBALL_BEACH","VOLLEYBALL_INDOOR","WAKEBOARDING","WALKING_FITNESS","NORDIC_WALKING","WALKING_TREADMILL","WATERPOLO","WEIGHTLIFTING","WHEELCHAIR","WINDSURFING","YOGA","ZUMBA","DIVING","ERGOMETER","ICE_SKATING","INDOOR_SKATING","CURLING","OTHER","CROSSFIT","HIIT","INTERVAL_TRAINING","WALKING_STROLLER","ELEVATOR","ESCALATOR","ARCHERY","SOFTBALL","GUIDED_BREATHING","CARDIO_TRAINING","LACROSSE","STRETCHING","TRIATHLON","INLINE_SKATING","SKY_DIVING","PADDLING","MOUNTAINEERING","FISHING","WATER_SKIING","INDOOR_RUNNING"],"example":"RUNNING"},"end_time":{"type":"string","description":"The end time of the associated workout, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-10-28T10:00:00.000000+01:00","format":"date-time","nullable":true},"name":{"type":"string","description":"The name - either user-entered or given by the fitness data provider - of the associated workout","nullable":true,"example":"Morning Run"},"upload_type":{"description":"The upload type for the associated workout, providing information on whether this was an automatic workout or user-entered","nullable":false,"type":"string","enum":["UNKNOWN","AUTOMATIC","MANUAL","UPDATE","DELETE","PENDING"]}}},"WorkData":{"type":"object","properties":{"work_kilojoules":{"type":"number","description":"Total work output of the user for the workout","nullable":true,"example":450.5}}},"Activity":{"type":"object","properties":{"active_durations_data":{"description":"Number of minutes spent at various intensities during the workout, as reported by the Fitness Data provider","type":"object","allOf":[{"$ref":"#/components/schemas/ActiveDurationsData"}]},"lap_data":{"description":"Object containing information on data for each lap performed by the user - mostly relates to track & field running activities, and swimming activities","type":"object","allOf":[{"$ref":"#/components/schemas/LapData"}]},"cheat_detection":{"type":"number","description":"Cheat detection flag","nullable":true,"example":0},"polyline_map_data":{"description":"Object containing polyline-representation map data, plotting the user's trajectory throughout the workout","type":"object","allOf":[{"$ref":"#/components/schemas/PolylineMapData"}]},"strain_data":{"description":"Object containing information on the cardiovascular strain imposed on the user during the workout","type":"object","allOf":[{"$ref":"#/components/schemas/StrainData"}]},"position_data":{"description":"Object containing information on the user's position throughout the workout","type":"object","allOf":[{"$ref":"#/components/schemas/PositionData"}]},"movement_data":{"description":"Object containing information on the user's movement throughout the workout","type":"object","allOf":[{"$ref":"#/components/schemas/MovementData"}]},"oxygen_data":{"description":"Object containing information on oxygen-related metrics for the workout","type":"object","allOf":[{"$ref":"#/components/schemas/OxygenData"}]},"device_data":{"description":"Object containing information on the device which recorded the specific workout","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceData"}]},"energy_data":{"description":"Object containing information on the energy expended by the user during the workout","type":"object","allOf":[{"$ref":"#/components/schemas/EnergyData"}]},"MET_data":{"description":"Object containing information on the Metabolic Equivalent of Task for the workout","type":"object","allOf":[{"$ref":"#/components/schemas/METData"}]},"power_data":{"description":"Object containing information on the power generated by the user during the workout","type":"object","allOf":[{"$ref":"#/components/schemas/PowerData"}]},"distance_data":{"description":"Object containing information related to distance covered during the associated workout","type":"object","allOf":[{"$ref":"#/components/schemas/DistanceData"}]},"heart_rate_data":{"description":"Object containing heartrate-related information for the workout","type":"object","allOf":[{"$ref":"#/components/schemas/HeartRateData"}]},"TSS_data":{"description":"Object containing information on the stress put on the user's body from a workout","type":"object","allOf":[{"$ref":"#/components/schemas/TSSData"}]},"calories_data":{"description":"Object containing calorie-related information for the user during the specific workout","type":"object","allOf":[{"$ref":"#/components/schemas/CaloriesData"}]},"metadata":{"description":"Object containing workout metadata","type":"object","allOf":[{"$ref":"#/components/schemas/ActivityMetadata"}]},"work_data":{"description":"Object containing information on the work output of the user during the workout","type":"object","allOf":[{"$ref":"#/components/schemas/WorkData"}]}}},"ActivitySample":{"type":"object","properties":{"swimming_strokes":{"type":"integer","default":null,"nullable":true,"example":25},"calories":{"type":"number","default":null,"nullable":true,"example":150.5},"spo2_percentage":{"type":"number","default":null,"nullable":true,"example":98.5},"vo2_max_volume_ml_per_min_per_kg":{"type":"number","default":null,"nullable":true,"example":45.2},"rep_count":{"type":"integer","default":null,"nullable":true,"example":12},"vo2_volume_ml_per_min_per_kg":{"type":"number","default":null,"nullable":true,"example":42.8},"speed_in_meters_per_seconds":{"type":"number","default":null,"nullable":true,"example":3.5},"active_duration_seconds":{"type":"integer","default":null,"nullable":true,"example":3600},"heartrate_bpm":{"type":"number","default":null,"nullable":true,"example":145},"swimming_laps":{"type":"number","default":null,"nullable":true,"example":10},"altitude_in_meters":{"type":"number","default":null,"nullable":true,"example":250.5},"status":{"type":"string","default":null,"nullable":true,"example":"active"},"distance_in_km":{"type":"number","default":null,"nullable":true,"example":5.2},"timestamp":{"type":"string","default":null,"nullable":true,"example":"2023-08-15T14:30:00.000000+00:00"},"resting_duration_seconds":{"type":"integer","default":null,"nullable":true,"example":300},"coordinates_lat_lng":{"type":"array","items":{"type":"number"}}}},"Athlete":{"type":"object","properties":{"gender":{"type":"string","description":"User's gender","nullable":true,"example":"male"},"state":{"type":"string","description":"User's state of residence","example":"California","nullable":true},"bio":{"type":"string","description":"User's bio - a short description they display on their profile","nullable":true,"example":"Passionate runner and cyclist"},"age":{"type":"string","description":"User's age","nullable":true,"example":"28"},"country":{"type":"string","description":"User's country of residence","nullable":true,"example":"United States"},"sex":{"type":"string","description":"User's sex","nullable":true,"example":"male"},"date_of_birth":{"type":"string","description":"User's date of birth, in ISO8601 format","example":"1999-11-23","format":"date","nullable":true},"city":{"type":"string","description":"User's city of residence","nullable":true,"example":"San Francisco"},"account_creation_date":{"type":"string","description":"User's account creation date","nullable":true,"example":"2023-01-15"},"email":{"type":"string","description":"User's email","nullable":true,"example":"user@example.com"},"last_name":{"type":"string","description":"User's last name","nullable":true,"example":"Smith"},"first_name":{"type":"string","description":"User's first name","nullable":true,"example":"John"}}},"AthleteCollection":{"type":"object","properties":{"athlete":{"description":"Object containing the user's information","type":"object","allOf":[{"$ref":"#/components/schemas/Athlete"}]},"type":{"type":"string","nullable":true,"example":"athlete"},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]}}},"MeasurementDataSample":{"type":"object","properties":{"weight_kg":{"type":"number","description":"User's body weight","nullable":true,"example":75.5},"bodyfat_percentage":{"type":"number","description":"User's body far percentage","minimum":0,"maximum":100,"nullable":true,"example":18.5},"BMI":{"type":"number","description":"User's Body Mass Index (BMI)","nullable":true,"example":22.4},"water_percentage":{"type":"number","description":"Total amount of fluid in the user's body","minimum":0,"maximum":100,"nullable":true,"example":60.5},"measurement_time":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"lean_mass_g":{"type":"number","description":"Total lean mass of the user - calculated as the difference between total body weight and body fat weight","nullable":true,"example":61500},"estimated_fitness_age":{"type":"string","description":"Estimate of how fit the user is compared to their actual age, as measured by the device","nullable":true,"example":"25"},"skin_fold_mm":{"type":"number","description":"User's skin fold measurement","nullable":true,"example":12.5},"height_cm":{"type":"number","description":"User's height","nullable":true,"example":178},"bone_mass_g":{"type":"number","description":"User's total bone mass","nullable":true,"example":3200},"insulin_units":{"type":"number","description":"Quantity of insulin administered to the user","nullable":true,"example":10.5},"muscle_mass_g":{"type":"number","description":"User's total muscle mass (i.e. skeletal muscle mass)","nullable":true,"example":35000},"BMR":{"type":"number","description":"User's Basal Metabolic Rate - minimum amount of calories that a person's body needs to perform necessary functions","nullable":true,"example":1800},"urine_color":{"type":"string","description":"Color of the user's urine","nullable":true,"example":"pale yellow"},"RMR":{"type":"number","description":"User's Resting Metabolic Rate - amount of energy that a person's body needs to function while at rest. RMR accounts for additional low-effort daily activities on top of basic body functions","nullable":true,"example":2000},"insulin_type":{"type":"string","description":"Type of insulin administered to the user","nullable":true,"example":"rapid-acting"}}},"MeasurementsData":{"type":"object","properties":{"measurements":{"type":"array","description":"List of body metrics & measurements taken throughout the associated day","items":{"$ref":"#/components/schemas/MeasurementDataSample"}}}},"KetoneDataSample":{"type":"object","properties":{"ketone_mg_per_dL":{"type":"number","description":"Ketone in mg per dL","nullable":true,"example":1.5},"sample_type":{"description":"Flag indicating the ketone sample type (e.g. blood, breath, urine)","nullable":false,"type":"string","enum":["UNKNOWN","BLOOD","BREATH","URINE"]},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"KetoneData":{"type":"object","properties":{"ketone_samples":{"type":"array","description":"List of ketone data sampled through the day.","items":{"$ref":"#/components/schemas/KetoneDataSample"}}}},"HydrationData":{"type":"object","properties":{"hydration_amount_samples":{"type":"number","description":"User's body water content","nullable":true,"example":65.5},"day_total_water_consumption_ml":{"type":"number","description":"User's total water consumption throughout the day","nullable":true,"example":2500}}},"DailyHeartRateDataSummary":{"type":"object","properties":{"resting_hr_bpm":{"type":"number","description":"Resting HeartRate of the user, as determined by the fitness data provider","minimum":0,"nullable":true,"example":60},"avg_hr_bpm":{"type":"number","description":"Average HeartRate of the user during the day","minimum":0,"nullable":true,"example":75},"max_hr_bpm":{"type":"number","description":"Maximum HeartRate of the user during the day","minimum":0,"nullable":true,"example":180},"avg_hrv_sdnn":{"type":"number","description":"Average HeartRate Variability of the user during the day, computed using SDNN","nullable":true,"example":45.5},"hr_zone_data":{"type":"array","description":"Array of time spent in various HR zones throughout the day","items":{"$ref":"#/components/schemas/HeartRateZone"}},"min_hr_bpm":{"type":"number","description":"Minimum HeartRate of the user during the day","minimum":0,"nullable":true,"example":45},"avg_hrv_rmssd":{"type":"number","description":"Average HeartRate Variability of the user during the day, computed using RMSSD","nullable":true,"example":42.8},"user_max_hr_bpm":{"type":"number","description":"User's maximum HeartRate based on their age, and other factors as determined by the fitness data provider","nullable":true,"example":190}}},"DailyHeartRateDataDetailed":{"type":"object","properties":{"hrv_samples_rmssd":{"type":"array","description":"Array of HeartRate Variability data samples recorded for the user during the day, computed using RMSSD","items":{"$ref":"#/components/schemas/HeartRateVariabilityDataSampleRMSSD"}},"hr_samples":{"type":"array","description":"Array of HeartRate data samples recorded for the user during the day","items":{"$ref":"#/components/schemas/HeartRateDataSample"}},"hrv_samples_sdnn":{"type":"array","description":"Array of HeartRate Variability data samples recorded for the user during the day, computed using SDNN","items":{"$ref":"#/components/schemas/HeartRateVariabilityDataSampleSDNN"}}}},"DailyHeartRateData":{"type":"object","properties":{"summary":{"description":"Object containing summary information for the associated day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyHeartRateDataSummary"}]},"detailed":{"description":"Object containing detailed heart rate information for the associated day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyHeartRateDataDetailed"}]}}},"PulseVelocitySample":{"type":"object","properties":{"pulse_wave_velocity_meters_per_second":{"type":"number","description":"User's Pulse Wave Velocity measurement - velocity at which the blood pressure pulse propagates through the circulatory system","nullable":true,"example":7.2},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"AFibClassificationSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"afib_classification":{"type":"string","description":"Flag indicating the atrial fibrillation classification of the individual","nullable":true,"example":"NORMAL"}}},"RawECGSample":{"type":"object","properties":{"potential_uV":{"type":"number","description":"Potential uV in the RawECG Sample","nullable":true,"example":1250.5},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"ECGReading":{"type":"object","properties":{"start_timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"avg_hr_bpm":{"type":"number","description":"User's average heart rate throughout the day","nullable":true,"example":72.5},"afib_classfication":{"type":"integer","description":"User's afib classification throught the day","nullable":true,"example":1},"raw_signal":{"type":"array","description":"List of raw ECG readings sampled through the day.","items":{"$ref":"#/components/schemas/RawECGSample"}}}},"BodyHeartData":{"type":"object","properties":{"heart_rate_data":{"description":"","type":"object","allOf":[{"$ref":"#/components/schemas/DailyHeartRateData"}]},"pulse_wave_velocity_samples":{"type":"array","description":"List of Pulse Wave Velocity measurements sampled throughout the day. This represents a measurement of arterial stiffness that is an independent predictor of cardiovascular risk","items":{"$ref":"#/components/schemas/PulseVelocitySample"}},"afib_classification_samples":{"type":"array","description":"List of Atrial Fibrillation classification measurements sampled through the day.","items":{"$ref":"#/components/schemas/AFibClassificationSample"}},"ecg_signal":{"type":"array","description":"List of ECGReadings sampled through the day.","items":{"$ref":"#/components/schemas/ECGReading"}},"rr_interval_samples":{"type":"array","description":"List of RR Interval samples throughout the day","items":{"$ref":"#/components/schemas/RRIntervalSample"}}}},"GlucoseDataSample":{"type":"object","properties":{"glucose_level_flag":{"description":"Flag indicating state of user's blood glucose level","nullable":false,"type":"string","enum":["NORMAL","HIGH","LOW"],"example":"NORMAL"},"trend_arrow":{"description":"Flag indicating the current trend in the user's blood glucose level (e.g. rising, constant, falling)","nullable":false,"type":"string","enum":["UNKNOWN","FALLING_QUICKLY","FALLING","FLAT","RISING","RISING_QUICKLY"],"example":"FLAT"},"blood_glucose_mg_per_dL":{"type":"integer","description":"User's blood glucose reading","nullable":true,"example":95},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"GlucoseData":{"type":"object","properties":{"detailed_blood_glucose_samples":{"type":"array","description":"List of blood glucose readings sampled throughout the day - this represents additional data points, potentially at higher frequency from the ones in blood_glucose_samples, which may come at a cost of reduced accuracy","items":{"$ref":"#/components/schemas/GlucoseDataSample"}},"blood_glucose_samples":{"type":"array","description":"List of blood glucose readings sampled throughout the day","items":{"$ref":"#/components/schemas/GlucoseDataSample"}},"day_avg_blood_glucose_mg_per_dL":{"type":"number","description":"User's average glucose level throughout the day","nullable":true,"example":92.5}}},"BodyMetadata":{"type":"object","properties":{"start_time":{"type":"string","description":"The start time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"end_time":{"type":"string","description":"The end time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h after start_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-10-28T10:00:00.000000+01:00","format":"date-time","nullable":true},"timestamp_localization":{"oneOf":[{"type":"integer","enum":[0],"description":"UTC timestamps with +00:00 offset. This avoids \"Z\" notation to ensure compatibility across parsers."},{"type":"integer","enum":[1],"description":"LOCAL timestamps that may include or exclude the timezone, following ISO8601."}],"description":"Indicates whether the timestamps in this payload are localized (LOCAL) or in UTC","nullable":false,"example":0}}},"BloodPressureSample":{"type":"object","properties":{"diastolic_bp":{"type":"number","description":"User's diastolic blood pressure, in mmHg","nullable":true,"example":80.5},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"systolic_bp":{"type":"number","description":"User's systolic blood pressure, in mmHg","nullable":true,"example":120.5}}},"BloodPressureData":{"type":"object","properties":{"blood_pressure_samples":{"type":"array","description":"List of Blood Pressure measurements sampled throughout the day","items":{"$ref":"#/components/schemas/BloodPressureSample"}}}},"TemperatureSample":{"type":"object","properties":{"temperature_celsius":{"type":"number","description":"","nullable":true,"example":37.2},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"TemperatureData":{"type":"object","properties":{"body_temperature_samples":{"type":"array","description":"List of body temperature measurements sampled throughout the day","items":{"$ref":"#/components/schemas/TemperatureSample"}},"skin_temperature_samples":{"type":"array","description":"List of skin temperature measurements sampled throughout the day","items":{"$ref":"#/components/schemas/TemperatureSample"}},"ambient_temperature_samples":{"type":"array","description":"List of ambient temperature measurements sampled throughout the day","items":{"$ref":"#/components/schemas/TemperatureSample"}}}},"Body":{"type":"object","properties":{"measurements_data":{"description":"Object containing information on body measurements for the day","type":"object","allOf":[{"$ref":"#/components/schemas/MeasurementsData"}]},"ketone_data":{"description":"Object containing information on user's ketone data for the day","type":"object","allOf":[{"$ref":"#/components/schemas/KetoneData"}]},"hydration_data":{"description":"Object containing information on user's hydration (both internal & consumption of water) for the day","type":"object","allOf":[{"$ref":"#/components/schemas/HydrationData"}]},"device_data":{"description":"Object containing information on the device which recorded data for the payload","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceData"}]},"heart_data":{"description":"Object containing information on user's heart metrics","type":"object","allOf":[{"$ref":"#/components/schemas/BodyHeartData"}]},"oxygen_data":{"description":"Object containing information on user's oxygen-related data","type":"object","allOf":[{"$ref":"#/components/schemas/OxygenData"}]},"glucose_data":{"description":"Object containing information on user's blood glucose for the day","type":"object","allOf":[{"$ref":"#/components/schemas/GlucoseData"}]},"metadata":{"description":"Object containing daily summary metadata","type":"object","allOf":[{"$ref":"#/components/schemas/BodyMetadata"}]},"blood_pressure_data":{"description":"Object containing information on user's Blood Pressure","type":"object","allOf":[{"$ref":"#/components/schemas/BloodPressureData"}]},"temperature_data":{"description":"Object containing temperature information (core, skin, ambient) during the day","type":"object","allOf":[{"$ref":"#/components/schemas/TemperatureData"}]}}},"DailyActiveDurationsData":{"type":"object","properties":{"activity_levels_samples":{"type":"array","description":"Array of detailed samples of the intensity the user was in at various points during the day","items":{"$ref":"#/components/schemas/ActivityLevelSample"}},"num_continuous_inactive_periods":{"type":"integer","description":"Maximum number of continuous periods spent in an inactive state during the day","minimum":0,"nullable":true,"example":3},"inactivity_seconds":{"type":"number","description":"Total number of seconds spent in an inactive state during the day","minimum":0,"nullable":true,"example":28800},"vigorous_intensity_seconds":{"type":"number","description":"Total number of seconds spent in a state of vigorous intensity during the day","minimum":0,"nullable":true,"example":1800},"activity_seconds":{"type":"number","description":"Total number of seconds spent in an active state during the day","minimum":0,"nullable":true,"example":14400},"low_intensity_seconds":{"type":"number","description":"Total number of seconds spent in a low intensity state during the day","minimum":0,"nullable":true,"example":7200},"rest_seconds":{"type":"number","description":"Total number of seconds spent resting during the day","minimum":0,"nullable":true,"example":28800},"moderate_intensity_seconds":{"type":"number","description":"Total number of seconds spent in a moderate intensity state during the day","minimum":0,"nullable":true,"example":5400}}},"Scores":{"type":"object","properties":{"sleep":{"type":"number","description":"Sleep score for the given day, pertaining to the previous night's sleep","minimum":0,"maximum":100,"nullable":true,"example":85},"recovery":{"type":"number","description":"Recovery score for the given day","minimum":0,"maximum":100,"nullable":true,"example":78},"activity":{"type":"number","description":"Activity score for the given day","minimum":0,"maximum":100,"nullable":true,"example":92}}},"DailyStrainData":{"type":"object","properties":{"strain_level":{"type":"number","description":"Object containing information on the cardiovascular strain imposed on the user during the day.","nullable":true,"example":14.5}}},"StressData":{"type":"object","properties":{"avg_stress_level":{"type":"number","description":"Average stress level for the day","nullable":true,"example":45.5},"activity_stress_duration_seconds":{"type":"number","description":"Total number of seconds spent in a stressed state while active during the day","minimum":0,"nullable":true,"example":3600},"low_stress_duration_seconds":{"type":"number","description":"Total number of seconds spent in a state of low stress during the day","minimum":0,"nullable":true,"example":28800},"max_stress_level":{"type":"number","description":"Maximum stress level recorded during the day","nullable":true,"example":85},"medium_stress_duration_seconds":{"type":"number","description":"Total number of seconds spent in a state of medium stress during the day","minimum":0,"nullable":true,"example":7200},"samples":{"type":"array","description":"Array of stress level data points sampled throughout the day","items":{"$ref":"#/components/schemas/StepSample"}},"rest_stress_duration_seconds":{"type":"number","description":"Total number of seconds spent in a stressed state while at rest during the day","minimum":0,"nullable":true,"example":1800},"high_stress_duration_seconds":{"type":"number","description":"Total number of seconds spent in a state of high stress during the day","minimum":0,"nullable":true,"example":900},"stress_duration_seconds":{"type":"number","description":"Total number of seconds spent in a stressed state while at rest during the day","minimum":0,"nullable":true,"example":5400}}},"TagEntry":{"type":"object","properties":{"notes":{"type":"string","description":"User-input notes associated with the given tag","nullable":true,"example":"Felt refreshed after"},"tag_name":{"type":"string","description":"Tag name, representing a certain event associated with the user's day","example":"hot_shower","nullable":true},"timestamp":{"type":"string","description":"Time with which the tag is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"TagData":{"type":"object","properties":{"tags":{"type":"array","description":"Array of user-entered tags for the day","items":{"$ref":"#/components/schemas/TagEntry"}}}},"DailyMETData":{"type":"object","properties":{"num_inactive_minutes":{"type":"number","description":"Number of minutes spent in state of Inactivity during the day - based off MET scale","minimum":0,"nullable":true,"example":480},"MET_samples":{"type":"array","description":"An array of Metabolic Equivalent Time samples, as calculated by the user's wearable","items":{"$ref":"#/components/schemas/METSample"}},"num_low_intensity_minutes":{"type":"number","description":"Number of minutes spent in Low Intensity during the day - based off MET scale","minimum":0,"nullable":true,"example":720},"num_moderate_intensity_minutes":{"type":"number","description":"Number of minutes spent in Moderate Intensity during the day - based off MET scale","minimum":0,"nullable":true,"example":180},"avg_level":{"type":"number","description":"The average MET level of the activity","nullable":true,"example":1.8},"num_high_intensity_minutes":{"type":"number","description":"Number of minutes spent in High Intensity during the day - based off MET scale","minimum":0,"nullable":true,"example":60}}},"DailyDistanceDataDetailed":{"type":"object","properties":{"elevation_samples":{"type":"array","description":"Array of detailed samples of elevation throughout the day","items":{"$ref":"#/components/schemas/ElevationSample"}},"distance_samples":{"type":"array","description":"Array of detailed samples of distance covered throughout the day","items":{"$ref":"#/components/schemas/DistanceSample"}},"floors_climbed_samples":{"type":"array","description":"Array of detailed samples of floors climbed throughout the day, as determined by the fitness data provider","items":{"$ref":"#/components/schemas/FloorsClimbedSample"}},"step_samples":{"type":"array","description":"Array of detailed samples of steps performed throughout the day","items":{"$ref":"#/components/schemas/StepSample"}}}},"DailyElevationSummary":{"type":"object","properties":{"gain_planned_meters":{"type":"number","description":"Elevation gain of the user throughout the day - this includes all elevation gain, and does _not_ represent net gain","minimum":0,"nullable":true,"example":150},"loss_actual_meters":{"type":"number","description":"Elevation loss of the user throughout the day","minimum":0,"nullable":true,"example":120},"max_meters":{"type":"number","description":"Maximum elevation of the user during the day","nullable":true,"example":450},"min_meters":{"type":"number","description":"Minimum elevation of the user during the day","nullable":true,"example":300},"gain_actual_meters":{"type":"number","description":"Planned elevation gain for the day","minimum":0,"nullable":true,"example":130},"avg_meters":{"type":"number","description":"Average elevation of the user throughout the day","nullable":true,"example":375}}},"DailySwimmingSummary":{"type":"object","properties":{"num_laps":{"type":"integer","description":"Total number of swimming laps performed during the day","minimum":0,"nullable":true,"example":20},"num_strokes":{"type":"integer","description":"Total number of swimming strokes performed during the day","minimum":0,"nullable":true,"example":500},"pool_length_meters":{"type":"integer","description":"Pool length for associated with the day","minimum":0,"nullable":true,"example":25}}},"DailyDistanceData":{"type":"object","properties":{"detailed":{"description":"Object containing detailed distance information - this may included second-by-second samples","type":"object","allOf":[{"$ref":"#/components/schemas/DailyDistanceDataDetailed"}]},"distance_meters":{"type":"number","description":"Total distance covered by the user throughout the day","nullable":true,"example":8500},"elevation":{"description":"Average elevation of the user throughout the day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyElevationSummary"}]},"steps":{"type":"integer","description":"Total number of steps performed during the day","nullable":true,"example":12000},"floors_climbed":{"type":"integer","description":"Total number of elevation gain in floors climbed equivalent throughout the day, as determined by the fitness data provider","minimum":0,"nullable":true,"example":15},"swimming":{"description":"Summary information of the user's swimming statistics for the day, if applicable","type":"object","allOf":[{"$ref":"#/components/schemas/DailySwimmingSummary"}]}}},"DailyOxygenData":{"type":"object","properties":{"vo2max_ml_per_min_per_kg":{"type":"number","description":"VO2Max for the given user","nullable":true,"example":45.5},"avg_saturation_percentage":{"type":"number","description":"Average Oxygen Saturation percentage of the user during the day (SpO2 or SmO2)","nullable":true,"example":98},"vo2_samples":{"type":"array","description":"Array of VO2 datapoints sampled throughout the day","items":{"$ref":"#/components/schemas/Vo2MaxSample"}},"saturation_samples":{"type":"array","description":"Array of Oxygen Saturation percentage datapoints sampled throughout the day","items":{"$ref":"#/components/schemas/OxygenSaturationSample"}}}},"DailyCaloriesData":{"type":"object","properties":{"BMR_calories":{"type":"number","description":"BMR of the user on the specific day, which contributes to the total calories burned during the day","minimum":0,"nullable":true,"example":1800},"net_activity_calories":{"type":"number","description":"Net calorie burn coming just from exercise, excluding BMR","minimum":0,"nullable":true,"example":500},"total_burned_calories":{"type":"number","description":"Total number of calories burned during the day, including BMR","minimum":0,"nullable":true,"example":2300},"net_intake_calories":{"type":"number","description":"Net intake of calories during the period of the day","minimum":0,"nullable":true,"example":2000},"calorie_samples":{"type":"array","description":"Detailed samples of cumulative calories burned at various timestamps over the period of the day","items":{"$ref":"#/components/schemas/CalorieSample"}}}},"DailyMetadata":{"type":"object","properties":{"start_time":{"type":"string","description":"The start time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"upload_type":{"description":"The upload type for data in the associated day, providing information on whether data was automatically uploaded or user-entered. If any data point in a day is altered, the day should be classed as manual","nullable":false,"type":"string","enum":["UNKNOWN","AUTOMATIC","MANUAL","UPDATE","DELETE","PENDING"]},"end_time":{"type":"string","description":"The end time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h after start_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-10-28T10:00:00.000000+01:00","format":"date-time","nullable":true},"timestamp_localization":{"oneOf":[{"type":"integer","enum":[0],"description":"UTC timestamps with +00:00 offset. This avoids \"Z\" notation to ensure compatibility across parsers."},{"type":"integer","enum":[1],"description":"LOCAL timestamps that may include or exclude the timezone, following ISO8601."}],"description":"Indicates whether the timestamps in this payload are localized (LOCAL) or in UTC","nullable":false,"example":0}}},"Daily":{"type":"object","properties":{"active_durations_data":{"description":"Object containing information related to the time spent in different activity intensities during over the day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyActiveDurationsData"}]},"scores":{"description":"Scores for the user's performance on different metrics for the given day, as calculated by the fitness data provider","type":"object","allOf":[{"$ref":"#/components/schemas/Scores"}]},"strain_data":{"description":"Object containing information on the strain put on the user's body over a day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyStrainData"}]},"stress_data":{"description":"Object containing information on the stress put on the user over a day","type":"object","allOf":[{"$ref":"#/components/schemas/StressData"}]},"device_data":{"description":"Object containing information on the device which recorded data for the day","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceData"}]},"tag_data":{"description":"Object containing all user-entered or automatically tagged events in the day","type":"object","allOf":[{"$ref":"#/components/schemas/TagData"}]},"heart_rate_data":{"description":"Object containing heartrate-related information for the day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyHeartRateData"}]},"MET_data":{"description":"Object containing information on the Metabolic Equivalent of Task for the day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyMETData"}]},"distance_data":{"description":"Object containing information related to distance covered during the associated day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyDistanceData"}]},"oxygen_data":{"description":"Object containing information on oxygen-related metrics for the day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyOxygenData"}]},"calories_data":{"description":"Object containing calorie-related information for the user during the specific day","type":"object","allOf":[{"$ref":"#/components/schemas/DailyCaloriesData"}]},"metadata":{"description":"Object containing daily summary metadata","type":"object","allOf":[{"$ref":"#/components/schemas/DailyMetadata"}]}}},"MenstruationMetadata":{"type":"object","properties":{"start_time":{"type":"string","description":"The start time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"end_time":{"type":"string","description":"The end time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h after start_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-10-28T10:00:00.000000+01:00","format":"date-time","nullable":true},"timestamp_localization":{"oneOf":[{"type":"integer","enum":[0],"description":"UTC timestamps with +00:00 offset. This avoids \"Z\" notation to ensure compatibility across parsers."},{"type":"integer","enum":[1],"description":"LOCAL timestamps that may include or exclude the timezone, following ISO8601."}],"description":"Indicates whether the timestamps in this payload are localized (LOCAL) or in UTC","nullable":false,"example":0}}},"MenstruationFlowSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"flow":{"description":"Flag indicating the strength of the user's menstrual flow","nullable":false,"type":"string","enum":["UNKNOWN","NONE","LIGHT","MEDIUM","HEAVY","HAD"]}}},"MenstruationData":{"type":"object","properties":{"length_of_current_phase_days":{"type":"integer","description":"Length of current phase","nullable":true,"example":5},"period_length_days":{"type":"integer","description":"Total length of period (i.e. menstrual bleeding)","nullable":true,"example":5},"cycle_length_days":{"type":"string","description":"Total cycle length","nullable":true,"example":"28"},"days_until_next_phase":{"type":"integer","description":"Number of days to reach the next phase (predicted)","nullable":true,"example":23},"is_predicted_cycle":{"type":"string","description":"Flag indicating whether associated object is a prediction or user-logged information","nullable":true,"example":"true"},"last_updated_time":{"type":"string","description":"Last time when the user recorded information regarding their cycle, in ISO8601 format, with microseconds precision","format":"date-time","example":"2022-12-12T14:03:05.000000-05:00","nullable":true},"current_phase":{"type":"integer","description":"Phase in associated cycle, (i.e. menstruation, fertile etc)","nullable":true,"example":1},"period_start_date":{"type":"string","description":"Start date of menstrual cycle, in ISO8601 format","format":"date","example":"2022-11-28","nullable":true},"predicted_cycle_length_days":{"type":"integer","description":"Prediction of the cycle's total length","nullable":true,"example":28},"menstruation_flow":{"type":"array","description":"List of user logs of information related to the strength of user's menstrual flow","items":{"$ref":"#/components/schemas/MenstruationFlowSample"}},"day_in_cycle":{"type":"integer","description":"Number of day in cycle this object is associated with","example":24,"nullable":true}}},"Menstruation":{"type":"object","properties":{"metadata":{"description":"Object containing daily summary metadata","type":"object","allOf":[{"$ref":"#/components/schemas/MenstruationMetadata"}]},"menstruation_data":{"description":"Object containing information on user's menstruation for a given day","type":"object","allOf":[{"$ref":"#/components/schemas/MenstruationData"}]}}},"Micros":{"type":"object","properties":{"iron_mg":{"type":"number","description":"Iron content of the associated food(s)","nullable":true,"example":8.5},"magnesium_mg":{"type":"number","description":"Magnesium content of the associated food(s)","nullable":true,"example":320},"vitamin_A_mg":{"type":"number","description":"Vitamin A content of the associated food(s)","nullable":true,"example":0.9},"vitamin_B12_mg":{"type":"number","description":"Vitamin B12 content of the associated food(s)","nullable":true,"example":0.0024},"biotin_mg":{"type":"number","description":"Biotin content of the associated food(s)","nullable":true,"example":0.03},"cystine_g":{"type":"number","description":"Cystine content of the associated food(s)","nullable":true,"example":0.3},"phenylalanine_g":{"type":"number","description":"Phenylalanine content of the associated food(s)","nullable":true,"example":0.9},"chloride_mg":{"type":"number","description":"Chloride content of the associated food(s)","nullable":true,"example":2300},"vitamin_D3_mg":{"type":"number","description":"Vitamin D3 content of the associated food(s)","nullable":true,"example":0.015},"isoleucine_g":{"type":"number","description":"Isoleucine content of the associated food(s)","nullable":true,"example":0.8},"calcium_mg":{"type":"number","description":"Calcium content of the associated food(s)","nullable":true,"example":1000},"iodine_mg":{"type":"number","description":"Iodine content of the associated food(s)","nullable":true,"example":0.15},"histidine_g":{"type":"number","description":"Histidine content of the associated food(s)","nullable":true,"example":0.6},"chromium_mg":{"type":"number","description":"Chromium content of the associated food(s)","nullable":true,"example":0.035},"vitamin_K_mg":{"type":"number","description":"Vitamin K content of the associated food(s)","nullable":true,"example":0.12},"manganese_mg":{"type":"number","description":"Manganese content of the associated food(s)","nullable":true,"example":2.3},"folic_acid_mg":{"type":"number","description":"Folic Acid content of the associated food(s)","nullable":true,"example":0.4},"leucine_g":{"type":"number","description":"Leucine content of the associated food(s)","nullable":true,"example":1.2},"caffeine_mg":{"type":"number","description":"Caffeine content of the associated food(s)","nullable":true,"example":95},"potassium_mg":{"type":"number","description":"Potassium content of the associated food(s)","nullable":true,"example":3500},"molybdenum_mg":{"type":"number","description":"Molybdenum content of the associated food(s)","nullable":true,"example":0.045},"lysine_g":{"type":"number","description":"Lysine content of the associated food(s)","nullable":true,"example":0.9},"tyrosine_g":{"type":"number","description":"Tyrosine content of the associated food(s)","nullable":true,"example":0.7},"valine_g":{"type":"number","description":"Valine content of the associated food(s)","nullable":true,"example":0.8},"omega6_g":{"type":"number","description":"Omega6 content of the associated food(s)","nullable":true,"example":17},"omega3_g":{"type":"number","description":"Omega3 content of the associated food(s)","nullable":true,"example":1.6},"vitamin_C_mg":{"type":"number","description":"Vitamin C content of the associated food(s)","nullable":true,"example":90},"zinc_mg":{"type":"number","description":"Zinc content of the associated food(s)","nullable":true,"example":11},"vitamin_D2_mg":{"type":"number","description":"Vitamin D2 content of the associated food(s)","nullable":true,"example":0.01},"vitamin_E_mg":{"type":"number","description":"Vitamin E content of the associated food(s)","nullable":true,"example":15},"pantothenic_acid_mg":{"type":"number","description":"Pantothenic content of the associated food(s)","nullable":true,"example":5},"riboflavin_mg":{"type":"number","description":"Riboflavin content of the associated food(s)","nullable":true,"example":1.3},"phosphorus_mg":{"type":"number","description":"Phosphorus content of the associated food(s)","nullable":true,"example":1000},"vitamin_D_mg":{"type":"number","description":"Vitamin D content of the associated food(s)","nullable":true,"example":0.02},"tryptophan_g":{"type":"number","description":"Tryptophan content of the associated food(s)","nullable":true,"example":0.3},"niacin_mg":{"type":"number","description":"Niacin content of the associated food(s)","nullable":true,"example":16},"copper_mg":{"type":"number","description":"Copper content of the associated food(s)","nullable":true,"example":0.9},"threonine_g":{"type":"number","description":"Threonine content of the associated food(s)","nullable":true,"example":0.5},"monounsaturated_fat_g":{"type":"number","description":"Monounsaturated fat content of the associated food(s)","nullable":true,"example":15},"folate_mg":{"type":"number","description":"Folate content of the associated food(s)","nullable":true,"example":0.4},"selenium_mg":{"type":"number","description":"Selenium content of the associated food(s)","nullable":true,"example":0.055},"vitamin_B6_mg":{"type":"number","description":"Vitamin B6 content of the associated food(s)","nullable":true,"example":1.7},"thiamin_mg":{"type":"number","description":"Thiamin content of the associated food(s)","nullable":true,"example":1.2},"polyunsaturated_fat_g":{"type":"number","description":"Polyunsaturated fat content of the associated food(s)","nullable":true,"example":13},"methionine_g":{"type":"number","description":"Methionine content of the associated food(s)","nullable":true,"example":0.6},"starch_g":{"type":"number","description":"Starch content of the associated food(s)","nullable":true,"example":25}}},"Macros":{"type":"object","properties":{"fiber_g":{"type":"number","description":"Fiber content of the associated food(s)","nullable":true,"example":25},"calories":{"type":"number","description":"Calorie content of the associated food(s)","nullable":true,"example":2000},"fat_g":{"type":"number","description":"Fat content of the associated food(s)","nullable":true,"example":65},"sodium_mg":{"type":"number","description":"Sodium content of the associated food(s)","nullable":true,"example":2300},"alcohol_g":{"type":"number","description":"Alcohol content of the associated food(s)","nullable":true,"example":14},"trans_fat_g":{"type":"number","description":"Trans fat content of the associated food(s)","nullable":true,"example":2},"carbohydrates_g":{"type":"number","description":"Carbohydrates content of the associated food(s)","nullable":true,"example":300},"protein_g":{"type":"number","description":"Protein content of the associated food(s)","nullable":true,"example":50},"saturated_fat_g":{"type":"number","description":"Saturated fat content of the associated food(s)","nullable":true,"example":20},"sugar_g":{"type":"number","description":"Sugar content of the associated food(s)","nullable":true,"example":25},"cholesterol_mg":{"type":"number","description":"Cholesterol content of the associated food(s)","nullable":true,"example":300},"net_carbohydrates_g":{"type":"number","description":"Net carbs content of the associated food(s)","nullable":true,"example":275}}},"NutritionSummary":{"type":"object","properties":{"micros":{"description":"Summary of micronutrient information for a given day","type":"object","allOf":[{"$ref":"#/components/schemas/Micros"}]},"macros":{"description":"Summary of macronutrient information for a given day","type":"object","allOf":[{"$ref":"#/components/schemas/Macros"}]},"water_ml":{"type":"number","description":"Water consumption of the user for a given day","nullable":true,"example":2000}}},"DrinkSample":{"type":"object","properties":{"drink_unit":{"type":"string","description":"Unit of measurement for the drink","nullable":true,"example":"ml"},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"drink_name":{"type":"string","description":"Name of drink consumed","nullable":true,"example":"Water"},"drink_volume":{"type":"string","description":"Volume of drink consumed","nullable":true,"example":"250"}}},"NutritionMetadata":{"type":"object","properties":{"start_time":{"type":"string","description":"The start time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"end_time":{"type":"string","description":"The end time of the associated day, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h after start_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-10-28T10:00:00.000000+01:00","format":"date-time","nullable":true},"timestamp_localization":{"oneOf":[{"type":"integer","enum":[0],"description":"UTC timestamps with +00:00 offset. This avoids \"Z\" notation to ensure compatibility across parsers."},{"type":"integer","enum":[1],"description":"LOCAL timestamps that may include or exclude the timezone, following ISO8601."}],"description":"Indicates whether the timestamps in this payload are localized (LOCAL) or in UTC","nullable":false,"example":0}}},"Quantity":{"type":"object","properties":{"unit":{"type":"string","description":"Name of the unit of measurement","example":"grams","nullable":true},"amount":{"type":"number","description":"Amount of food, measured in unit defined in this object","example":100,"minimum":0,"nullable":true}}},"Meal":{"type":"object","properties":{"micros":{"description":"Micronutrient information for associated food","type":"object","allOf":[{"$ref":"#/components/schemas/Micros"}]},"type":{"description":"Enum representing the category the consumed food/meal falls under (i.e. Breakfast/Lunch/Dinner etc","nullable":false,"type":"string","enum":["UNKNOWN","BREAKFAST","MORNING_SNACK","LUNCH","AFTERNOON_SNACK","DINNER","SNACK"],"example":"BREAKFAST"},"id":{"type":"string","description":"Identifier for food logged by the user","nullable":true,"example":"food_123"},"timestamp":{"type":"string","description":"Timestamp the food is associated with, in ISO8601 format, with microsecond precision","format":"date-time","example":"2022-12-12T08:53:00.000000+02:00","nullable":true},"quantity":{"description":"Quantity of the food that was consumed, containing information on amount & units in which this was recorded","type":"object","allOf":[{"$ref":"#/components/schemas/Quantity"}]},"name":{"type":"string","description":"Name of food logged by the user","nullable":true,"example":"Oatmeal"},"macros":{"description":"Macronutrient information for associated food","type":"object","allOf":[{"$ref":"#/components/schemas/Macros"}]}}},"Nutrition":{"type":"object","properties":{"summary":{"description":"Summative nutritional information for a given day","type":"object","allOf":[{"$ref":"#/components/schemas/NutritionSummary"}]},"drink_samples":{"type":"array","description":"Information on drinks the user consumed throughout the day","items":{"$ref":"#/components/schemas/DrinkSample"}},"metadata":{"description":"Object containing daily summary metadata","type":"object","allOf":[{"$ref":"#/components/schemas/NutritionMetadata"}]},"meals":{"type":"array","description":"Information on individual foods consumed throughout a given day","items":{"$ref":"#/components/schemas/Meal"}}}},"SnoringSample":{"type":"object","properties":{"duration_seconds":{"type":"number","description":"Duration of snoring episode","nullable":true,"example":120},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"SnoringData":{"type":"object","properties":{"start_time":{"type":"string","description":"The start time of the recording of snoring data, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T22:00:00.000000+02:00","format":"date-time","nullable":true},"total_snoring_duration_seconds":{"type":"number","description":"Total duration for which the user was snoring","nullable":true,"example":3600},"samples":{"type":"array","description":"List of snoring information data points sampled throughout the sleep session","items":{"$ref":"#/components/schemas/SnoringSample"}},"end_time":{"type":"string","description":"The end time of the recording of snoring data, in ISO8601 format with microsecond precisionTimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-24T08:00:00.000000+01:00","format":"date-time","nullable":true},"num_snoring_events":{"type":"integer","description":"Number of times over the sleep period when the user started snoring, as determined by the device","nullable":true,"example":12}}},"BreathSample":{"type":"object","properties":{"breaths_per_min":{"type":"number","description":"User's respiration rate","nullable":true,"example":16},"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true}}},"BreathsData":{"type":"object","properties":{"avg_breaths_per_min":{"type":"number","description":"Average breathing rate of the user during the sleep session","nullable":true,"example":14},"max_breaths_per_min":{"type":"number","description":"Maximum breathing rate of the user during the sleep session","nullable":true,"example":18},"start_time":{"type":"string","description":"The start time of the recording of breathing rate data, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T22:00:00.000000+02:00","format":"date-time","nullable":true},"min_breaths_per_min":{"type":"number","description":"Minimum breathing rate of the user during the sleep session","nullable":true,"example":10},"samples":{"type":"array","description":"List of breathing rate information sampled throughout the sleep session","items":{"$ref":"#/components/schemas/BreathSample"}},"on_demand_reading":{"type":"boolean","description":"Flag indicating if the reading was performed on demand, or if it was automatically captured by the device","nullable":true,"example":false},"end_time":{"type":"string","description":"The end time of the recording of breathing rate data, in ISO8601 format with microsecond precisionTimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-24T08:00:00.000000+01:00","format":"date-time","nullable":true}}},"OxygenSaturationData":{"type":"object","properties":{"samples":{"type":"array","description":"Array of Oxygen Saturation percentage datapoints sampled throughout the sleep session","items":{"$ref":"#/components/schemas/OxygenSaturationSample"}},"start_time":{"type":"string","description":"The start time of the recording of oxygen saturation, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T22:00:00.000000+02:00","format":"date-time","nullable":true},"avg_saturation_percentage":{"type":"number","description":"Average Oxygen Saturation percentage of the user during the sleep session","nullable":true,"example":97},"end_time":{"type":"string","description":"The end time of the recording of oxygen saturation, in ISO8601 format with microsecond precisionTimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-24T08:00:00.000000+01:00","format":"date-time","nullable":true}}},"RespirationData":{"type":"object","properties":{"snoring_data":{"description":"Object containing information on snoring metrics for the sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/SnoringData"}]},"breaths_data":{"description":"Object containing information on breathing rate for the sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/BreathsData"}]},"oxygen_saturation_data":{"description":"Object containing information on saturation metrics for the sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/OxygenSaturationData"}]}}},"SleepDurationsAwakeData":{"type":"object","properties":{"duration_short_interruption_seconds":{"type":"number","description":"Total duration for which the user was awake during the sleep session, when the interruption of their sleep was less than 90 seconds","nullable":true,"example":180},"duration_awake_state_seconds":{"type":"number","description":"Total duration for which the user was awake during the sleep session","nullable":true,"example":1200},"wake_up_latency_seconds":{"type":"number","description":"Wake up latency, defined as time between the moment the user wakes up and the moment they get out of bed","nullable":true,"example":300},"num_wakeup_events":{"type":"integer","description":"Number of times the user woke up during the sleep session","nullable":true,"example":3},"duration_long_interruption_seconds":{"type":"number","description":"Total duration for which the user was awake during the sleep session, when the interruption of their sleep was greater than 90 seconds.","nullable":true,"example":600},"sleep_latency_seconds":{"type":"number","description":"Sleep latency, defined as time between the moment the user lays in bed with the intention to sleep and the moment they actually fall asleep","nullable":true,"example":900},"num_out_of_bed_events":{"type":"integer","description":"Number of times the user got out of bed during the sleep session","nullable":true,"example":1}}},"SleepDurationsAsleepData":{"type":"object","properties":{"num_REM_events":{"type":"integer","description":"Number of periods of REM sleep captured during the sleep session","nullable":true,"example":4},"duration_asleep_state_seconds":{"type":"number","description":"Total duration for which the user was asleep, in any state","nullable":true,"example":25200},"duration_REM_sleep_state_seconds":{"type":"number","description":"Total duration for which the user was in a state of REM sleep","nullable":true,"example":5400},"duration_light_sleep_state_seconds":{"type":"number","description":"Total duration for which the user was in a state of light sleep","nullable":true,"example":14400},"duration_deep_sleep_state_seconds":{"type":"number","description":"Total duration for which the user was in a state of deep sleep","nullable":true,"example":5400}}},"SleepHypnogramSample":{"type":"object","properties":{"timestamp":{"type":"string","description":"Time with which the record is associated, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"level":{"description":"Sleep stage the user is in at a moment in time (e.g. deep sleep, light sleep etc)","nullable":false,"type":"string","enum":["UNKNOWN","AWAKE","SLEEPING","OUT_OF_BED","LIGHT","DEEP","REM"]}}},"SleepDurationsOtherData":{"type":"object","properties":{"duration_unmeasurable_sleep_seconds":{"type":"number","description":"Total duration during which the user's state (awake, asleep, REM, etc) was unmeasurable during the sleep session","nullable":true,"example":300},"duration_in_bed_seconds":{"type":"number","description":"Total duration of time spent in bed","nullable":true,"example":28800}}},"SleepDurationsData":{"type":"object","properties":{"sleep_efficiency":{"type":"number","description":"Sleep efficiency of the user given as a percentage, measured as time spent asleep divided by time spent in bed","minimum":0,"maximum":100,"nullable":true,"example":87.5},"awake":{"description":"Object containing information on the duration the user spent awake during the sleep recording session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepDurationsAwakeData"}]},"asleep":{"description":"Object containing information on the duration the user spent asleep during the sleep recording session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepDurationsAsleepData"}]},"hypnogram_samples":{"type":"array","description":"List of sleep stage (Hypnogram) samples recorded during the user's sleep session","items":{"$ref":"#/components/schemas/SleepHypnogramSample"}},"other":{"description":"Object containing information on the miscellaneous duration data for the sleep recording session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepDurationsOtherData"}]}}},"SleepHeartRateDataSummary":{"type":"object","properties":{"resting_hr_bpm":{"type":"number","description":"Resting HeartRate of the user, as determined by the fitness data provider","minimum":0,"nullable":true,"example":55},"avg_hr_bpm":{"type":"number","description":"Average HeartRate of the user during the sleep session","minimum":0,"nullable":true,"example":60},"max_hr_bpm":{"type":"number","description":"Maximum HeartRate of the user during the sleep session","minimum":0,"nullable":true,"example":75},"avg_hrv_sdnn":{"type":"number","description":"Average HeartRate Variability of the user during the sleep session, computed using SDNN","nullable":true,"example":45},"min_hr_bpm":{"type":"number","description":"Minimum HeartRate of the user during the sleep session","minimum":0,"nullable":true,"example":48},"avg_hrv_rmssd":{"type":"number","description":"Average HeartRate Variability of the user during the sleep session, computed using RMSSD","nullable":true,"example":35},"user_max_hr_bpm":{"type":"number","description":"User's maximum HeartRate based on their age, and other factors as determined by the fitness data provider","nullable":true,"example":185}}},"SleepHeartRateDataDetailed":{"type":"object","properties":{"hrv_samples_rmssd":{"type":"array","description":"Array of HeartRate Variability data samples recorded for the user during the sleep session, computed using RMSSD","items":{"$ref":"#/components/schemas/HeartRateVariabilityDataSampleRMSSD"}},"hr_samples":{"type":"array","description":"Array of HeartRate data samples recorded for the user during the sleep session","items":{"$ref":"#/components/schemas/HeartRateDataSample"}},"hrv_samples_sdnn":{"type":"array","description":"Array of HeartRate Variability data samples recorded for the user during the sleep session, computed using SDNN","items":{"$ref":"#/components/schemas/HeartRateVariabilityDataSampleSDNN"}}}},"SleepHeartRateData":{"type":"object","properties":{"summary":{"description":"Object containing summary information for the associated sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepHeartRateDataSummary"}]},"detailed":{"description":"Object containing detailed heart rate information for the associated sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepHeartRateDataDetailed"}]}}},"SleepMetadata":{"type":"object","properties":{"is_nap":{"type":"boolean","description":"Flag indicating whether the sleep session was a nap, or the user's main sleep session for the day","nullable":true,"example":false},"start_time":{"type":"string","description":"The start time of the associated sleep session, in ISO8601 format with microsecond precision. Will always fall on midnight of any given day, and will always be equal to 24h before end_time. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"1999-11-23T22:00:00.000000+02:00","format":"date-time","nullable":true},"upload_type":{"type":"integer","description":"The upload type for the associated sleep session, providing information on whether this was an automatic sleep or user-entered","nullable":false,"example":1},"end_time":{"type":"string","description":"The end time of the associated sleep session, in ISO8601 format with microsecond precisionTimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-24T08:00:00.000000+01:00","format":"date-time","nullable":true},"timestamp_localization":{"oneOf":[{"type":"integer","enum":[0],"description":"UTC timestamps with +00:00 offset. This avoids \"Z\" notation to ensure compatibility across parsers."},{"type":"integer","enum":[1],"description":"LOCAL timestamps that may include or exclude the timezone, following ISO8601."}],"description":"Indicates whether the timestamps in this payload are localized (LOCAL) or in UTC","nullable":false,"example":0}}},"ReadinessData":{"type":"object","properties":{"readiness":{"type":"number","description":"User's readiness score for a given day, resulting from the sleep session","minimum":0,"maximum":100,"nullable":true,"example":85},"recovery_level":{"type":"integer","description":"User's recovery score for a given day, resulting from the sleep session - takes Enum value","minimum":0,"maximum":6,"nullable":false,"example":4}}},"SleepTemperatureData":{"type":"object","properties":{"delta":{"type":"number","description":"Variation in user's skin temperature from their baseline","nullable":true,"example":0.5}}},"Sleep":{"type":"object","properties":{"respiration_data":{"description":"Object containing information on the user's respiration throughout the sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/RespirationData"}]},"sleep_durations_data":{"description":"Object containing information on the user's duration spent in various sleep stages","type":"object","allOf":[{"$ref":"#/components/schemas/SleepDurationsData"}]},"device_data":{"description":"Object containing information on the device which recorded data for the payload","type":"object","allOf":[{"$ref":"#/components/schemas/DeviceData"}]},"heart_rate_data":{"description":"Object containing information on the user's heart rate during the sleep session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepHeartRateData"}]},"metadata":{"description":"Object containing daily summary metadata","type":"object","allOf":[{"$ref":"#/components/schemas/SleepMetadata"}]},"readiness_data":{"description":"Object containing information on the user's readiness for the day, based off the quality and duration of their sleep","type":"object","allOf":[{"$ref":"#/components/schemas/ReadinessData"}]},"temperature_data":{"description":"Object containing body temperature information of the user during the sleep recording session","type":"object","allOf":[{"$ref":"#/components/schemas/SleepTemperatureData"}]}}},"PlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"HEART_RATE"}}},"CadencePlannedWorkoutStepTarget":{"type":"object","properties":{"cadence":{"type":"integer","description":"Ideal cadence value to be maintained for the workout step","nullable":true,"example":90},"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"CADENCE"},"cadence_low":{"type":"integer","description":"Minimum cadence threshold for the workout step - i.e. the user is to stay above this value during the workout","nullable":true,"example":85},"cadence_high":{"type":"integer","description":"Maximum cadence threshold for the workout step - i.e. the user is to stay under this value during the workout step","nullable":true,"example":95}}},"HRPlannedWorkoutStepTarget":{"type":"object","properties":{"hr_percentage_low":{"type":"number","description":"Maximum max heart rate percentage threshold for the workout step - i.e. the user is to stay under this value during the workout step","nullable":true,"example":65.5},"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"HEART_RATE"},"hr_percentage_high":{"type":"number","description":"Minimum heart rate percentage threshold for the workout step - i.e. the user is to stay above this value during the workout","nullable":true,"example":85.5},"hr_percentage":{"type":"number","description":"Ideal percentage of user's maximum HR to be maintained workout step","nullable":true,"example":75.5},"hr_bpm_high":{"type":"integer","description":"Maximum heart rate threshold for the workout step - i.e. the user is to stay under this value during the workout step","nullable":true,"example":175},"hr_bpm_low":{"type":"integer","description":"Minimum heart rate threshold for the workout step - i.e. the user is to stay above this value during the workout","nullable":true,"example":130}}},"PowerPlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"POWER"},"power_percentage_low":{"type":"number","description":"Maximum percentage of Functional Threshold Power for the workout step - i.e. the user is to stay under this value during the workout step","nullable":true,"example":65.5},"power_percentage_high":{"type":"number","description":"Minimum percentage of Functional Threshold Power for the workout step - i.e. the user is to stay above this value during the workout","nullable":true,"example":85.5},"power_watt_high":{"type":"integer","description":"Maximum power threshold for the workout step - i.e. the user is to stay under this value during the workout step","nullable":true,"example":300},"power_watt_low":{"type":"integer","description":"Minimum power threshold for the workout step - i.e. the user is to stay above this value during the workout","nullable":true,"example":200},"power_watt":{"type":"integer","default":null,"nullable":true,"example":250},"power_percentage":{"type":"number","description":"Ideal percentage of user's Functional Threshold Power to be maintained workout step","nullable":true,"example":75.5}}},"SpeedPlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"SPEED"},"speed_percentage_high":{"type":"number","description":"Maximum speed threshold for the workout step - i.e. the user is to stay under this value during the workout step","nullable":true,"example":85.5},"speed_percentage_low":{"type":"number","description":"Minimum speed threshold for the workout step - i.e. the user is to stay above this value during the workout step","nullable":true,"example":65.5},"speed_percentage":{"type":"number","description":"Ideal percentage of user's Threshold Speed, based off their Threshold Pace, to be maintained workout step. Usually, the Threshold Pace is defined as the pace one could race at for 50 to 60 minutes","nullable":true,"example":75.5},"speed_meters_per_second":{"type":"number","description":"Ideal speed value to be maintained for the workout step","nullable":true,"example":4.2}}},"PacePlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"PACE"},"pace_minutes_per_kilometer":{"type":"number","description":"Ideal pace value to be maintained for the workout step","nullable":true,"example":5.5}}},"TSSPlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"TSS"},"tss":{"type":"number","description":"Planned Training Stress Score to be achieved for the workout step","nullable":true,"example":100.5}}},"IFPlannedWorkoutStepTarget":{"type":"object","properties":{"if_high":{"type":"number","description":"Maximum Intensity Factor to be achieved for the workout step","nullable":true,"example":1.2},"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"IF"},"if_low":{"type":"number","description":"Minimum Intensity Factor to be achieved for the workout step","nullable":true,"example":0.8}}},"RepetitionPlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"REPETITION"},"repetitions":{"type":"number","description":"Number of repetitions of the workout step to be performed","nullable":true,"example":10}}},"SwimStrokePlannedWorkoutStepTarget":{"type":"object","properties":{"target_type":{"description":"Type of target for the workout - i.e. metric type for which a criterion must be met for the workout to be completed","nullable":false,"type":"string","enum":["SPEED","HEART_RATE","OPEN","CADENCE","POWER","GRADE","RESISTANCE","POWER_LAP","SWIM_STROKE","SPEED_LAP","HEART_RATE_LAP","PACE","HEART_RATE_THRESHOLD_PERCENTAGE","HEART_RATE_MAX_PERCENTAGE","SPEED_PERCENTAGE","POWER_PERCENTAGE","REPETITION","TSS","IF"],"example":"SWIM_STROKE"},"swim_strokes":{"type":"integer","description":"Number of swim strokes to be performed during the workout step","nullable":true,"example":50}}},"PlannedWorkoutStepTargets":{"type":"object","properties":{},"oneOf":[{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/CadencePlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/HRPlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/PowerPlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/SpeedPlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/PacePlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/TSSPlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/IFPlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/RepetitionPlannedWorkoutStepTarget"},{"$ref":"#/components/schemas/SwimStrokePlannedWorkoutStepTarget"}],"discriminator":{"propertyName":"type","mapping":{"PlannedWorkoutStepTarget":"#/components/schemas/PlannedWorkoutStepTarget","CadencePlannedWorkoutStepTarget":"#/components/schemas/CadencePlannedWorkoutStepTarget","HRPlannedWorkoutStepTarget":"#/components/schemas/HRPlannedWorkoutStepTarget","PowerPlannedWorkoutStepTarget":"#/components/schemas/PowerPlannedWorkoutStepTarget","SpeedPlannedWorkoutStepTarget":"#/components/schemas/SpeedPlannedWorkoutStepTarget","PacePlannedWorkoutStepTarget":"#/components/schemas/PacePlannedWorkoutStepTarget","TSSPlannedWorkoutStepTarget":"#/components/schemas/TSSPlannedWorkoutStepTarget","IFPlannedWorkoutStepTarget":"#/components/schemas/IFPlannedWorkoutStepTarget","RepetitionPlannedWorkoutStepTarget":"#/components/schemas/RepetitionPlannedWorkoutStepTarget","SwimStrokePlannedWorkoutStepTarget":"#/components/schemas/SwimStrokePlannedWorkoutStepTarget"}}},"PlannedWorkoutStepDuration":{"type":"object","properties":{"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"TimePlannedWorkoutStepDuration":{"type":"object","properties":{"seconds":{"type":"integer","description":"Time duration to be elapsed for the workout step","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"PowerAbovePlannedWorkoutStepDuration":{"type":"object","properties":{"power_above_watts":{"type":"integer","description":"Threshold power goal to complete the workout step - once the user reaches above this power level, the step will be completed","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"PowerBelowPlannedWorkoutStepDuration":{"type":"object","properties":{"power_below_watts":{"type":"integer","description":"Threshold power goal to complete the workout step - once the user reaches below this power level, the step will be completed","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"FixedRestPlannedWorkoutStepDuration":{"type":"object","properties":{"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]},"rest_seconds":{"type":"integer","description":"Time duration to be elapsed for the rest period","nullable":true}}},"CaloriesPlannedWorkoutStepDuration":{"type":"object","properties":{"calories":{"type":"integer","description":"Calorie burn target for the workout step - once the user reaches the target, the step will be completed","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"HRAbovePlannedWorkoutStepDuration":{"type":"object","properties":{"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]},"hr_above_bpm":{"type":"integer","description":"Threshold heart rate goal to complete the workout step - once the user's heart rate reaches above below this value, the step will be completed","nullable":true}}},"HRBelowPlannedWorkoutStepDuration":{"type":"object","properties":{"hr_below_bpm":{"type":"integer","description":"Threshold heart rate goal to complete the workout step - once the user's heart rate reaches below this value, the step will be completed","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"RepsPlannedWorkoutStepDuration":{"type":"object","properties":{"reps":{"type":"integer","description":"Target number of reps for the workout step - once the user completes this rep target, the step will be completed","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"DistancePlannedWorkoutStepDuration":{"type":"object","properties":{"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]},"distance_meters":{"type":"integer","description":"Target distance for the workout step - once the user covers this distance, the step will be completed","nullable":true}}},"StepsPlannedWorkoutStepDuration":{"type":"object","properties":{"steps":{"type":"integer","description":"Target number of steps for the workout step - once the user performs this number of steps, the step will be completed","nullable":true},"duration_type":{"description":"Type of condition that must be fulfilled to consider the workout step complete","nullable":false,"type":"string","enum":["TIME","DISTANCE_METERS","HR_LESS_THAN","HR_GREATER_THAN","CALORIES","OPEN","POWER_LESS_THAN","POWER_GREATER_THAN","REPETITION_TIME","REPS","FIXED_REST","TIME_AT_VALID_CDA","STEPS"]}}},"PlannedWorkoutStepDurations":{"type":"object","properties":{},"oneOf":[{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/TimePlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/PowerAbovePlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/PowerBelowPlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/FixedRestPlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/CaloriesPlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/HRAbovePlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/HRBelowPlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/RepsPlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/DistancePlannedWorkoutStepDuration"},{"$ref":"#/components/schemas/StepsPlannedWorkoutStepDuration"}],"discriminator":{"propertyName":"type","mapping":{"PlannedWorkoutStepDuration":"#/components/schemas/PlannedWorkoutStepDuration","TimePlannedWorkoutStepDuration":"#/components/schemas/TimePlannedWorkoutStepDuration","PowerAbovePlannedWorkoutStepDuration":"#/components/schemas/PowerAbovePlannedWorkoutStepDuration","PowerBelowPlannedWorkoutStepDuration":"#/components/schemas/PowerBelowPlannedWorkoutStepDuration","FixedRestPlannedWorkoutStepDuration":"#/components/schemas/FixedRestPlannedWorkoutStepDuration","CaloriesPlannedWorkoutStepDuration":"#/components/schemas/CaloriesPlannedWorkoutStepDuration","HRAbovePlannedWorkoutStepDuration":"#/components/schemas/HRAbovePlannedWorkoutStepDuration","HRBelowPlannedWorkoutStepDuration":"#/components/schemas/HRBelowPlannedWorkoutStepDuration","RepsPlannedWorkoutStepDuration":"#/components/schemas/RepsPlannedWorkoutStepDuration","DistancePlannedWorkoutStepDuration":"#/components/schemas/DistancePlannedWorkoutStepDuration","StepsPlannedWorkoutStepDuration":"#/components/schemas/StepsPlannedWorkoutStepDuration"}}},"PlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTargets"}},"type":{"description":"Type of workout step - either repeat or one-off","type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"description":"Planned intensity for the workout step","type":"string","enum":["REST","WARMUP","COOLDOWN","RECOVERY","INTERVAL","ACTIVE"]},"order":{"type":"integer","description":"Position of the workout step in the overall workout"},"description":{"type":"string","description":"Description of workout step"},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDurations"}},"name":{"type":"string","description":"Name of workout step"}}},"PlannedWorkoutRepeatStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"steps":{"type":"array","description":"List of steps to be repeated for this workout step - e.g. if a user wants to schedule 5 repetitions of 100m sprints plus 20s rest in between","items":{"$ref":"#/components/schemas/PlannedWorkoutStep"}},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"SwimmingPlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"equipement_type":{"description":"Workout equipment to be used during the workout step","nullable":false,"type":"string","enum":["NONE","SWIM_FINS","SWIM_KICKBOARD","SWIM_PADDLES","SWIM_PULL_BUOY","SWIM_SNORKEL"]},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"name":{"type":"string","description":"Name of workout step","nullable":true},"stroke_type":{"description":"Stroke type used for the workout step (e.g. breaststroke)","nullable":false,"type":"string","enum":["OTHER","FREESTYLE","BACKSTROKE","BREASTSTROKE","BUTTERFLY","REST"]}}},"CardioPlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"exercice_name":{"type":"string","description":"Name of exercise to be performed for the workout step","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"exercice_category":{"description":"Type of exercise to be performed for the workout step","nullable":false,"type":"string","enum":["UNKNOWN","BENCH_PRESS","CALF_RAISE","CARDIO","CARRY","CHOP","CORE","CRUNCH","CURL","DEADLIFT","FLYE","HIP_RAISE","HIP_STABILITY","HIP_SWING","HYPEREXTENSION","LATERAL_RAISE","LEG_CURL","LEG_RAISE","LUNGE","OLYMPIC_LIFT","PLANK","PLYO","PULL_UP","PUSH_UP","ROW","SHOULDER_PRESS","SHOULDER_STABILITY","SHRUG","SIT_UP","SQUAT","TOTAL_BODY","TRICEPS_EXTENSION","WARM_UP","RUN","BIKE","CARDIO_SENSORS"]},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"StrengthPlannedWorkoutStep":{"type":"object","properties":{"weight_kg":{"type":"number","description":"Weight to be lifted for the exercise","nullable":true},"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"exercice_name":{"type":"string","description":"Name of strength exercise to be performed for the workout step","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"exercice_category":{"description":"Type of strength exercise to be performed for the workout step","nullable":false,"type":"string","enum":["UNKNOWN","BENCH_PRESS","CALF_RAISE","CARDIO","CARRY","CHOP","CORE","CRUNCH","CURL","DEADLIFT","FLYE","HIP_RAISE","HIP_STABILITY","HIP_SWING","HYPEREXTENSION","LATERAL_RAISE","LEG_CURL","LEG_RAISE","LUNGE","OLYMPIC_LIFT","PLANK","PLYO","PULL_UP","PUSH_UP","ROW","SHOULDER_PRESS","SHOULDER_STABILITY","SHRUG","SIT_UP","SQUAT","TOTAL_BODY","TRICEPS_EXTENSION","WARM_UP","RUN","BIKE","CARDIO_SENSORS"]},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"YogaPlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"PilatesPlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"RunningPlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"CyclingPlannedWorkoutStep":{"type":"object","properties":{"targets":{"type":"array","description":"List of targets for the workout","items":{"$ref":"#/components/schemas/PlannedWorkoutStepTarget"}},"type":{"description":"Type of workout step - either repeat or one-off","nullable":false,"type":"string","enum":["STEP","REPEAT_STEP"]},"intensity":{"type":"integer","description":"Planned intensity for the workout step","nullable":true},"order":{"type":"integer","description":"Position of the workout step in the overall workout","nullable":true},"description":{"type":"string","description":"Description of workout step","nullable":true},"durations":{"type":"array","description":"List of conditions to be fulfilled for the workout step to be completed - all of the conditions must be completed","items":{"$ref":"#/components/schemas/PlannedWorkoutStepDuration"}},"name":{"type":"string","description":"Name of workout step","nullable":true}}},"PlannedWorkoutSteps":{"type":"object","properties":{},"oneOf":[{"$ref":"#/components/schemas/PlannedWorkoutStep"},{"$ref":"#/components/schemas/PlannedWorkoutRepeatStep"},{"$ref":"#/components/schemas/SwimmingPlannedWorkoutStep"},{"$ref":"#/components/schemas/CardioPlannedWorkoutStep"},{"$ref":"#/components/schemas/StrengthPlannedWorkoutStep"},{"$ref":"#/components/schemas/YogaPlannedWorkoutStep"},{"$ref":"#/components/schemas/PilatesPlannedWorkoutStep"},{"$ref":"#/components/schemas/RunningPlannedWorkoutStep"},{"$ref":"#/components/schemas/CyclingPlannedWorkoutStep"}],"discriminator":{"propertyName":"type","mapping":{"PlannedWorkoutStep":"#/components/schemas/PlannedWorkoutStep","PlannedWorkoutRepeatStep":"#/components/schemas/PlannedWorkoutRepeatStep","SwimmingPlannedWorkoutStep":"#/components/schemas/SwimmingPlannedWorkoutStep","CardioPlannedWorkoutStep":"#/components/schemas/CardioPlannedWorkoutStep","StrengthPlannedWorkoutStep":"#/components/schemas/StrengthPlannedWorkoutStep","YogaPlannedWorkoutStep":"#/components/schemas/YogaPlannedWorkoutStep","PilatesPlannedWorkoutStep":"#/components/schemas/PilatesPlannedWorkoutStep","RunningPlannedWorkoutStep":"#/components/schemas/RunningPlannedWorkoutStep","CyclingPlannedWorkoutStep":"#/components/schemas/CyclingPlannedWorkoutStep"}}},"PlannedWorkoutMetadata":{"type":"object","properties":{"estimated_energy_kj":{"type":"number","description":"Estimated energy expenditure for the workout","nullable":true},"estimated_speed_meters_per_second":{"type":"number","description":"Estimated speed for the workout","nullable":true},"estimated_elevation_gain_meters":{"type":"number","description":"Estimated elevation gain for the workout","nullable":true},"estimated_tss":{"type":"number","description":"Estimated Training Stress Score for the workout","nullable":true},"estimated_calories":{"type":"integer","description":"Estimated calorie burn for the workout","nullable":true},"created_date":{"type":"string","description":"The creation datetime of the associated workout, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-23T09:00:00.000000+02:00","format":"date-time","nullable":true},"planned_date":{"type":"string","description":"The planned start datetime, in ISO8601 format with microsecond precision. TimeZone info will be provided whenever possible. If absent, the time corresponds to the user's local time","example":"2022-11-24T09:00:00.000000+02:00","format":"date-time","nullable":true},"type":{"description":"The name - either user-entered or given by the fitness data provider - of the associated workout plan","nullable":false,"type":"string","enum":["IN_VEHICLE","BIKING","STILL","UNKNOWN","TILTING","WALKING","RUNNING","AEROBICS","BADMINTON","BASEBALL","BASKETBALL","BIATHLON","HANDBIKING","MOUNTAIN_BIKING","ROAD_BIKING","SPINNING","STATIONARY_BIKING","UTILITY_BIKING","BOXING","CALISTHENICS","CIRCUIT_TRAINING","CRICKET","DANCING","ELLIPTICAL","FENCING","AMERICAN_FOOTBALL","AUSTRALIAN_FOOTBALL","ENGLISH_FOOTBALL","FRISBEE","GARDENING","GOLF","GYMNASTICS","HANDBALL","HIKING","HOCKEY","HORSEBACK_RIDING","HOUSEWORK","JUMPING_ROPE","KAYAKING","KETTLEBELL_TRAINING","KICKBOXING","KITESURFING","MARTIAL_ARTS","MEDITATION","MIXED_MARTIAL_ARTS","P90X_EXERCISES","PARAGLIDING","PILATES","POLO","RACQUETBALL","ROCK_CLIMBING","ROWING","ROWING_MACHINE","RUGBY","JOGGING","RUNNING_ON_SAND","TREADMILL_RUNNING","SAILING","SCUBA_DIVING","SKATEBOARDING","SKATING","CROSS_SKATING","INDOOR_ROLLERBLADING","SKIING","BACK_COUNTRY_SKIING","CROSS_COUNTRY_SKIING","DOWNHILL_SKIING","KITE_SKIING","ROLLER_SKIING","SLEDDING","SNOWBOARDING","SNOWMOBILE","SNOWSHOEING","SQUASH","STAIR_CLIMBING","STAIR_CLIMBING_MACHINE","STAND_UP_PADDLEBOARDING","STRENGTH_TRAINING","SURFING","SWIMMING","SWIMMING_SWIMMING_POOL","SWIMMING_OPEN_WATER","TABLE_TENNIS","TEAM_SPORTS","TENNIS","TREADMILL","VOLLEYBALL","VOLLEYBALL_BEACH","VOLLEYBALL_INDOOR","WAKEBOARDING","WALKING_FITNESS","NORDIC_WALKING","WALKING_TREADMILL","WATERPOLO","WEIGHTLIFTING","WHEELCHAIR","WINDSURFING","YOGA","ZUMBA","DIVING","ERGOMETER","ICE_SKATING","INDOOR_SKATING","CURLING","OTHER","CROSSFIT","HIIT","INTERVAL_TRAINING","WALKING_STROLLER","ELEVATOR","ESCALATOR","ARCHERY","SOFTBALL","GUIDED_BREATHING","CARDIO_TRAINING","LACROSSE","STRETCHING","TRIATHLON","INLINE_SKATING","SKY_DIVING","PADDLING","MOUNTAINEERING","FISHING","WATER_SKIING","INDOOR_RUNNING"]},"id":{"type":"string","default":null,"nullable":true},"estimated_duration_seconds":{"type":"integer","description":"Estimated workout duration","nullable":true},"estimated_pace_minutes_per_kilometer":{"type":"number","description":"Estimated pace for the workout","nullable":true},"provider":{"type":"string","description":"Name of the original source of the workout plan","nullable":false},"estimated_tscore":{"type":"number","description":"","nullable":true},"description":{"type":"string","description":"Description of the workout","nullable":true},"name":{"type":"string","description":"Name of the workout plan","nullable":true},"estimated_distance_meters":{"type":"integer","description":"Estimated distance for the workout","nullable":true},"estimated_if":{"type":"number","description":"Estimated Intensity Factor for the workout","nullable":true},"pool_length_meters":{"type":"integer","description":"Pool length of the pool used for the workout - only relevant for swimming activity types","nullable":true}}},"PlannedWorkout":{"type":"object","properties":{"steps":{"type":"object","description":"List of exercises/steps/intervals for the workout plan","items":{"$ref":"#/components/schemas/PlannedWorkoutSteps"}},"metadata":{"description":"Metadata for the workout plan","type":"object","allOf":[{"$ref":"#/components/schemas/PlannedWorkoutMetadata"}]}}},"UserAuthWebhook":{"type":"object","properties":{"user":{"type":"object","description":"The user for which this authentication webhook is for","items":{"$ref":"#/components/schemas/User"}},"widget_session_id":{"description":"The widget session from which the user authenticated from","type":"string","nullable":true},"reference_id":{"type":"string","description":"Connection identifier on the developer's end, used to tie connection back to a user on the developer's platform","nullable":true},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'auth'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"PermissionChangeWebhook":{"type":"object","properties":{"type":{"type":"string","description":"The type of webhook event received. For this webhook, this would be 'permission_change'."},"user":{"type":"object","description":"The user for which this permission change webhook is for","items":{"$ref":"#/components/schemas/User"}},"status":{"type":"string","enum":["success"],"description":"Status of the webhook event."},"message":{"type":"string","description":"A message describing the webhook event."},"version":{"type":"string","description":"The version of the webhook.","nullable":false},"scopes_added":{"type":"string","description":"A comma-separated list of scopes that were added during the permission change."},"scopes_removed":{"type":"string","description":"A comma-separated list of scopes that were removed during the permission change."}}},"UserReauthWebhook":{"type":"object","properties":{"old_user":{"type":"object","description":"The old user that was authenticated","items":{"$ref":"#/components/schemas/User"}},"new_user":{"type":"object","description":"The new user that was authenticated","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'user_reauth'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"UserDeauthWebhook":{"type":"object","properties":{"user":{"type":"object","description":"The user that was deauthenticated","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'deauth'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"AccessRevokedWebhook":{"type":"object","properties":{"user":{"type":"object","description":"The user for which has revoked access to Terra or your application","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'access_revoked'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"GoogleNoDataSourceWebhook":{"type":"object","properties":{"user":{"type":"object","description":"The user for which no google data sources can be found for.","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'google_no_datasource'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"ConnectionErrorWebhook":{"type":"object","properties":{"user":{"type":"object","description":"A connection error has happened for this user. Generally this means, you should deauthenticate them.","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'connection_error'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"DataProcessingWebhook":{"type":"object","properties":{"user":{"type":"object","description":"The user for which data is processing for","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'processing'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"LargeRequestSendingWebhook":{"type":"object","properties":{"user":{"type":"object","description":"The user for which large request is sending for","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"expected_payloads":{"type":"number","description":"The number of payloads you should be expecting from the large request"},"reference":{"type":"string","description":"The reference for which all payloads will have in their 'terra-reference' headers"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'large_request_sending'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This is either 'success' or 'error'"},"version":{"type":"string","nullable":false}}},"LargeRequestProcessingResponse":{"type":"object","properties":{"user":{"type":"object","description":"The user for which large request is sending for","items":{"$ref":"#/components/schemas/User"}},"message":{"type":"string","description":"A message giving a small context on what the payload is about"},"reference":{"type":"string","description":"The reference for which all payloads will have in their 'terra-reference' headers"},"type":{"type":"string","description":"The type of webhook event received. For this, webhook, this would be 'large_request_prcessing'"},"status":{"type":"string","enum":["success","error"],"description":"Status of the webhook. This will be 'processing'"},"version":{"type":"string","nullable":false}}},"AuthenticationFailedWebhook":{"type":"object","properties":{"reference_id":{"type":"string","nullable":true},"type":{"type":"string","nullable":false},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]},"reason":{"type":"string","nullable":false},"status":{"type":"string","enum":["success","error"],"nullable":false},"message":{"type":"string","nullable":false},"widget_session_id":{"type":"string","nullable":true},"version":{"type":"string","nullable":false}}},"ConnectionDegradedWebhook":{"type":"object","properties":{"status":{"type":"string","enum":["success","error"],"nullable":false},"message":{"type":"string","nullable":false},"type":{"type":"string","nullable":false},"user":{"description":"Terra User object","type":"object","allOf":[{"$ref":"#/components/schemas/User"}]},"version":{"type":"string","nullable":false}}},"IntegrationsResponse":{"type":"object","properties":{"status":{"type":"string","description":"Status of the API response","example":"success"},"providers":{"type":"array","description":"List of integration providers with their details","items":{"$ref":"#/components/schemas/IntegrationProvider"}}}},"IntegrationProvider":{"type":"object","properties":{"provider":{"type":"string","description":"Identifier for the provider","example":"MAPMYFITNESS"},"name":{"type":"string","description":"Display name of the integration","example":"MapMyFitness"},"icon":{"type":"string","description":"URL for the provider's icon image","example":"https://api.tryterra.co/v2/static/assets/img/app_icons/mapmyfitness.webp"},"setup":{"type":"string","description":"Indicates how the integration is set up","example":"API_KEYS_MANAGED"},"enabled":{"type":"boolean","description":"Whether the integration is enabled","example":true},"types":{"type":"object","description":"Indicates the types of data available through the provider","properties":{"activity":{"type":"boolean","example":true},"body":{"type":"boolean","example":false},"nutrition":{"type":"boolean","example":false},"daily":{"type":"boolean","example":false},"sleep":{"type":"boolean","example":false},"menstruation":{"type":"boolean","example":false}}}}},"WebhookEvents":{"type":"object","properties":{},"oneOf":[{"$ref":"#/components/schemas/UserAuthWebhook"},{"$ref":"#/components/schemas/UserReauthWebhook"},{"$ref":"#/components/schemas/UserDeauthWebhook"},{"$ref":"#/components/schemas/AccessRevokedWebhook"},{"$ref":"#/components/schemas/GoogleNoDataSourceWebhook"},{"$ref":"#/components/schemas/ConnectionErrorWebhook"},{"$ref":"#/components/schemas/DataProcessingWebhook"},{"$ref":"#/components/schemas/LargeRequestSendingWebhook"},{"$ref":"#/components/schemas/AuthenticationFailedWebhook"},{"$ref":"#/components/schemas/ConnectionDegradedWebhook"}],"discriminator":{"propertyName":"type","mapping":{"UserAuthWebhook":"#/components/schemas/UserAuthWebhook","UserReauthWebhook":"#/components/schemas/UserReauthWebhook","UserDeauthWebhook":"#/components/schemas/UserDeauthWebhook","AccessRevokedWebhook":"#/components/schemas/AccessRevokedWebhook","GoogleNoDataSourceWebhook":"#/components/schemas/GoogleNoDataSourceWebhook","ConnectionErrorWebhook":"#/components/schemas/ConnectionErrorWebhook","DataProcessingWebhook":"#/components/schemas/DataProcessingWebhook","LargeRequestSendingWebhook":"#/components/schemas/LargeRequestSendingWebhook"}}}}}} ``` ```json {"openapi":"3.1.0","info":{"title":"Real Time Streaming API","version":2},"servers":[{"url":"https://ws.tryterra.co"}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key"},"devIdAuth":{"type":"apiKey","in":"header","name":"dev-id"}}},"security":[{"apiKeyAuth":[],"devIdAuth":[]}],"paths":{"/auth/developer":{"post":{"parameters":[{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"}},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"}}],"summary":"Stream - Generate developer token","description":"Endpoint for generation of a token for a developer (consumer) connection","operationId":"generate-developer-token","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","example":"cG9RvLY5.yvKm778XMIPm1ig93BJEoRCVGHzlrBNjzWdeXePTaMM"}}},"examples":{"Result":{"value":"{\n \"token\": \"cG9RvLY5.yvKm778XMIPm1ig93BJEoRCVGHzlrBNjzWdeXePTaMM\"\n}\n"}}}}},"403":{"description":"Forbidden","content":{"text/plain":{"examples":{"Result":{"value":""}}}}}}}},"/auth/user":{"post":{"summary":"Stream - Generate user token","description":"Endpoint for generation of a token for a user (producer) connection","operationId":"generate-user-token","parameters":[{"name":"id","in":"query","description":"The ID of the user to generate a token for","schema":{"type":"string"}},{"name":"dev-id","in":"header","description":"your developer ID","required":true,"schema":{"type":"string"}},{"name":"x-api-key","in":"header","description":"your API key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","example":"OTYwNWFi5ZWQMTAxMjg0Y2Qw.gzrPzZcS3Gy8QDOxbiPRwu30PTB3VxW0eE"}}},"examples":{"Result":{"value":"{\n \"token\": \"OTYwNWFi5ZWQMTAxMjg0Y2Qw.gzrPzZcS3Gy8QDOxbiPRwu30PTB3VxW0eE\"\n}\n"}}}}},"403":{"description":"Forbidden","content":{"text/plain":{"examples":{"Result":{"value":""}}}}}}}}}} ``` # Event Types {% hint style="info" %} Every request made to Terra, and every event sent from Terra will contain a `terra-reference` header containing a unique identifier for the request or event. \ The `terra-reference` identifier uniquely ties together a request -> event pair, whenever a request for data leads to data being asynchronously sent to your server. This can be useful for keeping track of whether or not a data transfer request has been fulfilled, or is still pending transfer {% endhint %} | Type | Explanation | | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `auth` | Occurs when a user attempts to authenticate | | `deauth` | Occurs when a user deauthenticates through the Terra | | `user_reauth` | Occurs when a user successfully authenticates for a second time, with the same account. You will receive a successful `auth` and a `user_reauth` payload | | `access_revoked` | Occurs when a user revokes Terra's access from the provider's end | | `connection_error` | Occurs when a request to a provider returns an HTTP response of 401, 403 or 412 | | `google_no_datasource` | Occurs when a Google Fit user doesn't have a data source linked to their account. All data requests for the user will be empty unless they link a data source | | `processing` | Occurs when data is being fetched asynchronously from the provider. The data will be sent through automatically via your [Destination](core-concepts.md#destinations), and you can also safely request for it after the time in the `retry_after_seconds` field. | | `large_request_sending` | Occurs when more than one month of data has been requested and all data has been successfully submitted | | `rate_limit_hit` | Occurs when an asynchronous request has failed due to rate limiting and is going to be retried. | | `large_request_processing` | Occurs when more a request for over one month of data has been submitted to Terra. Data will be sent in chunks with the same reference field after this request. Each chunk will be at most 10 objects in the `data` field or 10 MB in size, whichever limit gets hit first | | `activity`, `athlete`, `body`, `daily`, `menstruation`, `nutrition`, `sleep` | Occurs throughout the day as users use their wearables. `activity` updates when new activity is completed | | `healthcheck` | Healthcheck event sent periodically to verify your event is functional | ## Misc
Healthcheck Slug: `healthcheck` Trigger: This will periodically be sent to your [destination](core-concepts.md#destinations) to confirm its health status Recommended action: N/A Format: ```json { "type": "healthcheck", "status": String, "creation_timestamp": String, "trend_percentage": Number, "sent_webhooks_last_hour": Number } ```
## Authentication
Device account connection success/failure Slug: `auth` Trigger: A user just finished going through connecting a device account to your app Recommended action: If `status` = `success`, save the [user](core-concepts.md#user) to your database. Use the `reference_id` parameter to link this back to an [end user](core-concepts.md#end-user) profile in your app. If `status` = `error`, do nothing and treat this as an informational event Format: ```json { "type": "auth", "user": User, "status": "success", "reference_id": String, "widget_session_id": String } ``` ```json { "type": "auth", "user": User, "provider": "GARMIN" }, "status": "error", "message": "User failed to authenticate and has been deleted", "reason": String, "reference_id": String, "widget_session_id": String } ```
Device account disconnection/deauthentication Slug: `deauth` Trigger: A user just disconnected their device account from your app Recommended action: Remove the user from your database using the `user_id` field and remove all their data records according to your data retention policy. Format: ``` { "type": "deauth", "user": User, "status": "success", "message": "User has deauthenticated" } ```
Device account re-authentication Slug: `user_reauth` Trigger: A user just connected a wearable account to your app which was previously already linked to you. Recommended action: Remove the `old_user` record from your database using the `user_id` field Replace all references of the `user_id` from `old_user` with the `user_id` from `new_user` Format: ```json { "type": "user_reauth", "new_user": User, "old_user": User, "status": "warning", "message": "User has reauthenticated and old ID has been deleted" } ```
User access revoked Slug: `access_revoked` Trigger: A user revoked access to data access for your app through their wearable app's settings Recommended action: Remove the user from your database and remove all their data records according to your data retention policy. Format: ```json { "type": "access_revoked", "user": User, "status": "warning", "message": "User revoked access" } ```
User data connection error Slug: `connection_error` Trigger: A user potentially revoked access to data access for your app through their wearable app's settings, or modified their credentials in a way which invalidated the tokens for data access. Unlike [#user-access-revoked](event-types.md#user-access-revoked "mention"), Terra cannot determine for sure that this was due to the user explicitly removing access for your app, or that this isn't a temporary connection error. Recommended action: Remove the user from your database, and ask the user to re-connect their device. You may need remove all their data records according to your data retention policy. Format: ```json { "type": "connection_error", "user": User, "status": "warning", "message": "User connection degraded" } ```
No datasource available Slug: `google_no_datasource` Trigger: A user was just created, but has no data sources associated with their account. Recommended action: Inform the user they do not have any data sources associated with their Google Fit account, and you may not receive data for them as a result. Keep a log of this for future reference (e.g. if the [end user](core-concepts.md#end-user) logs a support ticket regarding this) Format: ```json { "type": "connection_error", "user": User, "status": "warning", "message": "User connection degraded" } ```
Permission change Slug: `permission_change` Trigger: A user has authenticated a second time, potentially on a separate dev ID, and modified the permissions granted to Terra Recommended action: Log this as an informational bit of data, which would help understand why some data type(s) are no longer provided down the line. Format: ```json { "type": "permission_change", "user": User, "status": "warning", "message": "User permissions have been modified", "version": "2022-03-16", "scopes_added": "fitness.blood_glucose.read", "scopes_removed": "fitness.oxygen_saturation.read" } ```
## Data requests
Request processing Slug: `google_no_datasource` Trigger: Terra needs to fetch data asynchronously from the data provider. This is an HTTP-only event (only returned as an HTTP response, and never sent as an [event](core-concepts.md#event) to your [destination](core-concepts.md#destinations)) Recommended action: - Await for the data to arrive in your destination - Request for it after (at worst) the time allotted in the `retry_after_seconds` field. Format: ```json { "type": "processing", "status": "success", "message": "Request is processing, try again later", "user": TerraUser, "retry_after_seconds": Number, } ```
Large request processing Slug: `large_request_processing` Trigger: A [large request](core-concepts.md#large-request) has been submitted to Terra, and is being processed. A [`large_request_sending`](event-types.md#large-request-sending) [event](core-concepts.md#event) will follow Recommended action: Await for data chunks to be sent. Use the `terra_reference` to keep track of any events tied back to the initial request. The `terra_reference` will match the one in the response to the initial HTTP request. Format: ```json { "type": "large_request_processing", "status": "processing", "message": "Large request is processing", "user": TerraUser, "reference": String } ```
Large request sending Slug: `large_request_sending` Trigger: A [large request](core-concepts.md#large-request) has been submitted to Terra, and data pertaining to it is about to be sent. [Data chunks](event-types.md#data-types) will be sent to your [destination](core-concepts.md#destinations) following this [event](core-concepts.md#event), all with the same `terra_reference` header Recommended action: Await for data chunks to be sent. Use the `terra_reference` header or `reference` field to tie those back to the initial request. The `reference` will match the `terra-reference` header in the response to the initial HTTP request. You may keep track of the sync progress using the `expected_payloads` field, which indicates how many [data events](event-types.md#data-events) will follow Format: ```json { "user": User, "reference": String, "message": "Large request is being sent", "expected_payloads": Number, "type": "large_request_sending", } ```
Rate limit hit Slug: rate\_limit\_hit Trigger: Rate limits for the data provider were hit during an asynchronous request. Terra will reschedule the request for a later time Recommended action: Keep a log of this occurrence Format: ```json { "user": User, "start_date": DateTime, "end_date": DateTime, "retrying_at": DateTime, "message": "Rate limit reached whilst processing async query", "type": "rate_limit_hit", } ```
## Data events
Activity Slug: `activity` Trigger: New activity records are available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database, and overwrite previous records based off the `metadata.summary_id` entry Format: ```json { "data": [Activity], "user": User, "type": "activity", "version": String } ```
Athlete Slug: `athlete` Trigger: New data is available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database Format: ```json { "athlete": Athlete, "user": User, "type": "athlete", "version": String } ```
Body Slug: `body` Trigger: New body data is available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database, and overwrite previous records based off the `metadata.start_time` entry Format: ```json { "data": [Body], "user": User, "type": "body", "version": String } ```
Daily Slug: `daily` Trigger: New daily activity summary data is available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database, and overwrite previous records based off the `metadata.start_time` entry Format: ```json { "data": [Daily], "user": User, "type": "daily", "version": String } ```
Menstruation Slug: `menstruation` Trigger: New menstruation data is available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database, and overwrite previous records based off the `metadata.start_time` entry Format: ```json { "data": [Menstruation], "user": User, "type": "menstruation", "version": String } ```
Nutrition Slug: `nutrition` Trigger: New nutrition records are available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database, and overwrite previous records based off the `metadata.start_time` entry Format: ```json { "data": [Nutrition], "user": User, "type": "nutrition", "version": String } ```
Sleep Slug: `sleep` Trigger: New sleep records are available for the [user](core-concepts.md#user) account Recommended action: Save the data in your database, and overwrite previous records based off the `metadata.summary_id` entry Format: ```json { "data": [Sleep], "user": User, "type": "sleep", "version": String } ```
# Samples ### MenstruationFlowSample ```json { "timestamp": String, "flow": MenstruationFlow } ``` ### DailyPatternSample (BETA - subject to change) ```json { "time_from_midnight": int, "percentile_5": Number, "percentile_25": Number, "percentile_50": Number, "percentile_75": Number, "percentile_95": Number } ``` ### DrinkSample ```json { "timestamp": String, // required "drink_volume": Number, // numeric quantity "drink_unit": String, // e.g. "ml", "fl oz" "drink_name": String } ``` ### KetoneSample ```json { "timestamp": String, // required "ketone_mg_per_dL": Number, // required "sample_type": Number // device‑specific enum } ``` ### TorqueSample ```json { "timestamp": String, // required – ISO‑8601 "timer_duration_seconds": Number, "torque_newton_meters": Number } ``` ### ECGReading ```json { "start_timestamp": String, "avg_hr_bpm": Number, "afib_classification": AFibFlag, "raw_signal": Array } ``` ### GlucoseDataSample ```json { "timestamp": String, "blood_glucose_mg_per_dL": Number, "glucose_level_flag": Number, "trend_arrow": Number, } ``` ### HeartRateDataSample ```json { "timestamp": String, "bpm": Number, "timer_duration_seconds": Number, "context": HeartRateContext } ``` ### LapSample ```json { "start_time": String, "end_time": String, "distance_meters": Number, "calories": Number, "total_strokes": Number, "stroke_type": StrokeType, "avg_speed_meters_per_second": Number } ``` ### HeartRateVariabilityDataSampleRMSSD ```json { "timestamp": String, "hrv_rmssd": Number } ``` ### HeartRateVariabilityDataSampleSDNN ```json { "timestamp": String, "hrv_sdnn": Number } ``` ### HeartRateZoneData ```json { "zone": HeartRateZone, "start_percentage": Number, "end_percentage": Number, "name": String, "duration_seconds": Number } ``` ### FloorsClimbedSample ```json { "timestamp": String, "floors_climbed": Number, "timer_duration_seconds": Number } ``` ### CalorieSample ```json { "timestamp": String, "calories": Number, "timer_duration_seconds": Number } ``` ### DistanceSample ```json { "timestamp": String, "distance_meters": Number, "timer_duration_seconds": Number } ``` ### StepSample ```json { "timestamp": String, "steps": Number, "timer_duration_seconds": Number } ``` ### ElevationSample ```json { "timestamp": String, "elev_meters": Number, "timer_duration_seconds": Number } ``` ### PositionSample ```json { "timestamp": String, "coords_lat_lng_deg": [Number, Number], "timer_duration_seconds": Number } ``` ### PowerSample ```json { "timestamp": String, "watts": Number, "timer_duration_seconds": Number } ``` ### SpeedSample ```json { "timestamp": String, "speed_meters_per_second": Number, "timer_duration_seconds": Number } ``` ### CadenceSample ```json { "timestamp": String, "cadence_rpm": Number, "timer_duration_seconds": Number } ``` ### ActivityLevelSample ```json { "timestamp": String, "level": ActivityLevel } ``` ### METSample ```json { "timestamp": String, "level": Number } ``` ### TSSSample ```json { "planned": Number, "actual": Number, "method": String, "intensity_factor_planned": Number, "intensity_factor_actual": Number, "normalized_power_watts": Number } ``` ### SleepHypnogramSample ```json { "timestamp": String, "level": SleepLevel } ``` ### OxygenSaturationSample ```json { "timestamp": String, "percentage": Number } ``` ### BreathSample ```json { "timestamp": String, "breaths_per_min": Number } ``` ### RawECGSample ```json { "potential_uV": Number, "timestamp: String } ``` ### SnoringSample ```json { "timestamp": String, "duration_seconds": Number } ``` ### StressSample ```json { "timestamp": String, "level": Number } ``` ### BodyBatterySample ```json { "timestamp": String, "level": Number } ``` ### BloodPressureSample ```json { "timestamp": String, "diastolic_bp": Number, "systolic_bp": Number } ``` ### MeasurementDataSample ```json { "measurement_time": String, "BMI": Number, "BMR": Number, "RMR": Number, "estimated_fitness_age": Number, "skin_fold_mm": Number, "bodyfat_percentage": Number, "weight_kg": Number, "height_cm": Number, "bone_mass_g": Number, "muscle_mass_g": Number, "lean_mass_g": Number, "water_percentage": Number, "insulin_units": Number, "insulin_type": String, "urine_color": String, "user_notes": String } ``` ### TemperatureSample ```json { "timestamp": String, "temperature_celsius": Number } ``` ### OtherDeviceData ```json { "name": String, "manufacturer": String, "serial_number": String, "software_version": String, "hardware_version": String, "last_upload_date": String, "data_provided": Array } ``` ### HydrationlevelSample ```json { "timestamp": String, "hydration_level": Number } ``` ### HydrationMeasurementSample ```json { "timestamp": String, "hydration_kg": Number } ``` ### Vo2MaxSample ```json { "timestamp": String, "vo2max_ml_per_min_per_kg": Number } ``` ### PulseVelocitySample ```json { "timestamp": String, "pulse_wave_velocity_meters_per_second": Number } ``` ### AFibClassificationSample ```json { "timestamp": String, "afib_classification": AfibFlag } ``` ### Meal ```json { "name": String, "id": String, "timestamp": String, "type": Number, "quantity": { "unit": NutritionUnits, "amount": Number }, "macros": { "calories": Number, "protein_g": Number, "carbohydrates_g": Number, "fat_g": Number, "sugar_g": Number, "cholesterol_mg": Number, "fiber_g": Number, "sodium_mg": Number, "alcohol_g": Number }, "micros": { "selenium_mg": Number, "niacin_mg": Number, "magnesium_mg": Number, "copper_mg": Number, "vitamin_B12_mg": Number, "vitamin_B6_mg": Number, "vitamin_C_mg": Number, "zinc_mg": Number, "vitamin_E_mg": Number, "manganese_mg": Number, "vitamin_D_mg": Number, "iodine_mg": Number, "chloride_mg": Number, "folate_mg": Number, "calcium_mg": Number, "molybdenum_mg": Number, "vitamin_A_mg": Number, "riboflavin_mg": Number, "folic_acid_mg": Number, "iron_mg": Number, "thiamin_mg": Number, "pantothenic_acid_mg": Number, "caffeine_mg": Number, "vitamin_K_mg": Number, "chromium_mg": Number, "potassium_mg": Number, "biotin_mg": Number, "phosphorus_mg": Number } } ``` ### TagEntry ```json { "timestamp": String, "tag_name": String, "notes": String } ``` ### RRInterval ```Text { "timestamp": String, "rr_interval_ms": Number, "hr_bpm": Number } ``` # Websocket Reference ## Identify Data Schema
Field NameTypeDescription
tokenStringAuthentication token generated from the appropriate endpoint.
typeIntegerConnection type. Value from the IdentifyType enum.
## Identify Type Enum
NameValue
User (Producer)0
Developer (Consumer)1
## Payload Format
Field NameTypeDescription
opIntegerOpcode for the payload.
d?ObjectEvent data.
uid?*StringUser ID that the event relates to.
seq?*LongSequence number of the payload. Used when replaying missed payloads.
t?*StringThe Data Type for the payload.
{% hint style="info" %} `? -> Optional field`\ `* -> Only send with payloads of the`[`DISPATCH`](websocket-reference.md#opcodes)`opcode` {% endhint %} ### Example Payload {% tabs %} {% tab title="Dispatch Payload" %} ```json { "op": 5, "d": { "ts": "2022-05-04T10:26:11.268507+01:00", "val": 95 }, "uid": "8e864e3a-979a-4d04-b4e9-b6d020f20ba0", "seq": 73, "t": "HEART_RATE" } ``` {% endtab %} {% tab title="Client Payload" %} ```python { "op": 6, "d": { "val": 95 }, "t": "HEART_RATE" } ``` {% endtab %} {% tab title="Heartbeat" %} Heartbeat Upon opening a websocket connection to the server, you will immediately be sent an `Op 2 HELLO` payload containing the heartbeat interval (in milliseconds) that the client should use when sending heartbeat messages to the server. ```json { "op": 2, "d": { "heartbeat_interval": 40000 } } ``` Heartbeat Acknowledged ```json {"op": 1} // Sent back to the client by the server every "heartbeat_interval" ms ``` {% endtab %} {% endtabs %} ## Opcodes
NameValueDescriptionSent By
HEARTBEAT0Fired periodically by the client to keep the connection alive.Client
HEARTBEAT_ACK1Sent by the server to acknowledge that a heartbeat has been received.Server
HELLO2Sent by the server immediately after connecting. Contains the heartbeat interval for the client to use.Server
IDENTIFY3Sent by the client to complete the handshake with the server. Contains authentication details.Client
READY4Sent by the server once the handshake has been completed. The client will be able to receive/send payloads immediately after this is received.Server
DISPATCH5An event dispatched by the server. Contains details about the event, as well as the data payload for the event.Server
SUBMIT6Sent by the client to notify the server of new data which will then be routed to the correct consumer appropriately.Client (producer only)
REPLAY7Request a replay of payloads after the given lower bound, and optionally before a given upper bound.Client (consumer only)
## Data Types
NameDescription
HEART_RATEval is the BPM of each reading
STEPSval the amount of accumulated steps
DISTANCEval the accumulated distance in meters
FLOORS_CLIMBEDval the accumulated floors climbed
STEPS_CADENCEval the amount of steps per second taken by the user
SPEEDval the speed in meter per second of the user
ACCELERATIONd the acceleration data of the device.
- d[0] -> acceleration in x direction
- d[1] -> acceleration in y direction
- d[2] -> acceleration in z direction
GYROSCOPEd the rotation rate of the device.
- d[0] -> rotation in x direction
- d[1] -> rotation in y direction
- d[2] -> rotation in z direction
### Close Codes
CodeReason
1001Server is shutting down
1007Invalid or unrecognised JSON data was provided
4000IDENTIFY payload expected but was not received
4001Improper token was passed for IDENTIFY
4002Duplicate connection opened
4003Multiple IDENTIFY payloads received
4004Unrecognised opcode was received
# Data models > 📘 Note > > All fields in all models are nullable unless explicitly stated otherwise. > 🚧 Uniquely identifying payloads > > ### Activity > > Activity payloads can be uniquely identified by their `summary_id` field under `metadata`. If an activity comes in a second time, with the same summary ID, you should update the previous entries with the new data corresponding to that `summary_id` > > ### Body, Daily, Sleep, Nutrition > > Body, Daily, Sleep, Nutrition payloads can be uniquely identified by the combination of their `start_time` and `end_time` fields under `metadata`. If one of these payloads comes in again, with the same `start_time` and `end_time`, you should update the previous entries with the new data. > > For example, the `Daily` payload can come in multiple times in one day, with updated `steps` field every time > 🚧 Schema parser > > When building a parser for the models below, please take into account that more fields may be added down the line (but no field name should ever change within a given version) > > Also note that this version is equivalent to `2022_03_16` ### TerraUser ```json { "user_id": String, // not nullable "provider": String, // not nullable "last_webhook_update": String, "scopes": String, "reference_id": String } ``` ## Core data types ### Activity ```json { // NOTE ──────────────────────────────────────────────────── // ❶ Unless a property is explicitly documented as // “required / non‑null”, it may be `null`. // ❷ Date‑time strings are ISO‑8601. // ❸ Top‑level keys are listed alphabetically for clarity. // ───────────────────────────────────────────────────────── "active_durations_data": { "activity_levels_samples": Array, "activity_seconds": Number, "inactivity_seconds": Number, "low_intensity_seconds": Number, "moderate_intensity_seconds": Number, "num_continuous_inactive_periods": Number, "rest_seconds": Number, "standing_hours_count": Number, "standing_seconds": Number, "vigorous_intensity_seconds": Number }, "calories_data": { "BMR_calories": Number, "calorie_samples": Array, "net_activity_calories": Number, "net_intake_calories": Number, "total_burned_calories": Number }, "cheat_detection": CheatDetection, "data_enrichment": { "stress_score": Number }, "device_data": { "activation_timestamp": String, "data_provided": Array, "hardware_version": String, "last_upload_date": String, "manufacturer": String, "name": String, "other_devices": Array, "serial_number": String, "software_version": String }, /* schema for each element of other_devices */ "OtherDeviceData": { "activation_timestamp": String, "data_provided": Array, "hardware_version": String, "last_upload_date": String, "manufacturer": String, "name": String, "serial_number": String, "software_version": String }, "distance_data": { "detailed": { "distance_samples": Array, "elevation_samples": Array, "floors_climbed_samples": Array, "step_samples": Array }, "summary": { "distance_meters": Number, "elevation": { "avg_meters": Number, "gain_actual_meters": Number, "gain_planned_meters": Number, "loss_actual_meters": Number, "max_meters": Number, "min_meters": Number }, "floors_climbed": Number, "steps": Number, "swimming": { "num_laps": Number, "num_strokes": Number, "pool_length_meters": Number } } }, "energy_data": { "energy_kilojoules": Number, "energy_planned_kilojoules": Number }, "heart_rate_data": { "detailed": { "hr_samples": Array, "hrv_samples_rmssd": Array, "hrv_samples_sdnn": Array }, "summary": { "avg_hr_bpm": Number, "avg_hrv_rmssd": Number, "avg_hrv_sdnn": Number, "hr_zone_data": Array, "max_hr_bpm": Number, "min_hr_bpm": Number, "resting_hr_bpm": Number, "user_max_hr_bpm": Number } }, "lap_data": { "laps": Array }, "MET_data": { "MET_samples": Array, "avg_level": Number, "num_high_intensity_minutes": Number, "num_inactive_minutes": Number, "num_low_intensity_minutes": Number, "num_moderate_intensity_minutes": Number }, "metadata": { "city": String, "country": String, "end_time": String, // required "name": String, "start_time": String, // required "state": String, "summary_id": String, // required "timestamp_localization": Number, "type": ActivityType, // required "upload_type": UploadType // required }, "movement_data": { "adjusted_max_speed_meters_per_second": Number, "avg_cadence_rpm": Number, "avg_pace_minutes_per_kilometer": Number, "avg_speed_meters_per_second": Number, "avg_torque_newton_meters": Number, "avg_velocity_meters_per_second": Number, "cadence_samples": Array, "max_cadence_rpm": Number, "max_pace_minutes_per_kilometer": Number, "max_speed_meters_per_second": Number, "max_torque_newton_meters": Number, "max_velocity_meters_per_second": Number, "normalized_speed_meters_per_second": Number, "speed_samples": Array, "torque_samples": Array }, "oxygen_data": { "avg_saturation_percentage": Number, "saturation_samples": Array, "vo2_samples": Array, "vo2max_ml_per_min_per_kg": Number }, "polyline_map_data": { "summary_polyline": String }, "position_data": { "center_pos_lat_lng_deg": [Number, Number], "end_pos_lat_lng_deg": [Number, Number], "position_samples": Array, "start_pos_lat_lng_deg": [Number, Number] }, "power_data": { "avg_watts": Number, "max_watts": Number, "power_samples": Array }, "strain_data": { "strain_level": Number }, "TSS_data": { "TSS_samples": Array }, "work_data": { "work_kilojoules": Number } } ``` ### Athlete ```json { "age": Number, "country": String, "bio": String, "state": String, "last_name": String, "sex": String, "city": String, "email": String, "date_of_birth": String, "first_name": String, "gender": String, "joined_provider": String, "devices": Array } ``` ### Body ```json { "blood_pressure_data": { "blood_pressure_samples": Array }, "device_data": { "activation_timestamp": String, "data_provided": Array, "hardware_version": String, "last_upload_date": String, "manufacturer": String, "name": String, "other_devices": Array, "sensor_state": String, "serial_number": String, "software_version": String }, "heart_data": { "afib_classification_samples": Array, "ecg_signal": Array, "heart_rate_data": { "detailed": { "hr_samples": Array, "hrv_samples_rmssd": Array, "hrv_samples_sdnn": Array }, "summary": { "avg_hr_bpm": Number, "avg_hrv_rmssd": Number, "avg_hrv_sdnn": Number, "hr_zone_data": Array, "max_hr_bpm": Number, "min_hr_bpm": Number, "resting_hr_bpm": Number, "user_max_hr_bpm": Number } }, "pulse_wave_velocity_samples": Array, "rr_interval_samples": Array }, "hydration_data": { "day_total_water_consumption_ml": Number, "hydration_amount_samples": Array, "hydration_level_samples": Array }, "ketone_data": { "ketone_samples": Array }, "measurements_data": { "measurements": Array }, "metadata": { "end_time": String, // required "start_time": String // required }, "oxygen_data": { "avg_saturation_percentage": Number, "saturation_samples": Array, "vo2_samples": Array, "vo2max_ml_per_min_per_kg": Number }, "temperature_data": { "ambient_temperature_samples": Array, "body_temperature_samples": Array, "skin_temperature_samples": Array, "day_avg_blood_glucose_mg_per_dL": Number, "daily_patterns": Array, "detailed_blood_glucose_samples": Array, "gmi": Number, "sensor_usage": Number, "time_in_range": Number } } ``` ### Daily ```json { "active_durations_data": { "activity_levels_samples": Array, "activity_seconds": Number, "inactivity_seconds": Number, "low_intensity_seconds": Number, "moderate_intensity_seconds": Number, "num_continuous_inactive_periods": Number, "rest_seconds": Number, "standing_hours_count": Number, "standing_seconds": Number, "vigorous_intensity_seconds": Number }, "calories_data": { "BMR_calories": Number, "calorie_samples": Array, "net_activity_calories": Number, "net_intake_calories": Number, "total_burned_calories": Number }, "data_enrichment": { "cardiovascular_contributors": Array, "cardiovascular_score": Number, "immune_contributors": Array, "immune_index": Number, "readiness_contributors": Array, "readiness_score": Number, "respiratory_contributors": Array, "respiratory_score": Number, "start_time": String, "stress_contributors": Array, "total_stress_score": Number }, "device_data": { "activation_timestamp": String, "data_provided": Array, "hardware_version": String, "last_upload_date": String, "manufacturer": String, "name": String, "other_devices": Array, "serial_number": String, "software_version": String }, "distance_data": { "detailed": { "distance_samples": Array, "elevation_samples": Array, "floors_climbed_samples": Array, "step_samples": Array }, "distance_meters": Number, "elevation": { "avg_meters": Number, "gain_actual_meters": Number, "gain_planned_meters": Number, "loss_actual_meters": Number, "max_meters": Number, "min_meters": Number }, "floors_climbed": Number, "steps": Number, "swimming": { "num_laps": Number, "num_strokes": Number, "pool_length_meters": Number } }, "heart_rate_data": { "detailed": { "hr_samples": Array, "hrv_samples_rmssd": Array, "hrv_samples_sdnn": Array }, "summary": { "avg_hr_bpm": Number, "avg_hrv_rmssd": Number, "avg_hrv_sdnn": Number, "hr_zone_data": Array, "max_hr_bpm": Number, "min_hr_bpm": Number, "resting_hr_bpm": Number, "user_max_hr_bpm": Number } }, "MET_data": { "MET_samples": Array, "avg_level": Number, "num_high_intensity_minutes": Number, "num_inactive_minutes": Number, "num_low_intensity_minutes": Number, "num_moderate_intensity_minutes": Number }, "metadata": { "end_time": String, // required "start_time": String, // required "timestamp_localization": Number, "upload_type": UploadType // required }, "oxygen_data": { "avg_saturation_percentage": Number, "saturation_samples": Array, "vo2_samples": Array, "vo2max_ml_per_min_per_kg": Number }, "scores": { "activity": Number, "recovery": Number, "sleep": Number }, "strain_data": { "strain_level": Number }, "stress_data": { "activity_stress_duration_seconds": Number, "avg_stress_level": Number, "body_battery_samples": Array, "high_stress_duration_seconds": Number, "low_stress_duration_seconds": Number, "max_stress_level": Number, "medium_stress_duration_seconds": Number, "rest_stress_duration_seconds": Number, "samples": Array, "stress_duration_seconds": Number, "stress_rating": Number }, "tag_data": { "tags": Array } } ``` ### Menstruation ```json { "metadata": { "end_time": String, "start_time": String }, "menstruation_data": { "period_length_days": Number, "current_phase": MenstruationFlow, "length_of_current_phase_days": Number, "days_until_next_phase": Number, "period_start_date": String, "predicted_cycle_length_days": Number, "day_in_cycle": Number, "last_updated_time": String, "cycle_length_days": Number, "is_predicted_cycle": String, "menstruation_flow": Array } } ``` ### Nutrition ```json { "drink_samples": Array, "meals": Array, "metadata": { "end_time": String, // required – ISO‑8601 "start_time": String, // required – ISO‑8601 "timestamp_localization": Number }, "summary": { "macros": { "alcohol_g": Number, "calories": Number, "carbohydrates_g": Number, "cholesterol_mg": Number, "fat_g": Number, "fiber_g": Number, "net_carbohydrates_g": Number, "protein_g": Number, "saturated_fat_g": Number, "sodium_mg": Number, "sugar_g": Number, "trans_fat_g": Number }, "micros": { "biotin_mg": Number, "caffeine_mg": Number, "calcium_mg": Number, "chloride_mg": Number, "chromium_mg": Number, "copper_mg": Number, "folate_mg": Number, "folic_acid_mg": Number, "iodine_mg": Number, "iron_mg": Number, "magnesium_mg": Number, "manganese_mg": Number, "molybdenum_mg": Number, "niacin_mg": Number, "pantothenic_acid_mg": Number, "phosphorus_mg": Number, "potassium_mg": Number, "riboflavin_mg": Number, "selenium_mg": Number, "thiamin_mg": Number, "vitamin_A_mg": Number, "vitamin_B12_mg": Number, "vitamin_B6_mg": Number, "vitamin_C_mg": Number, "vitamin_D_mg": Number, "vitamin_D2_mg": Number, "vitamin_D3_mg": Number, "vitamin_E_mg": Number, "vitamin_K_mg": Number, "zinc_mg": Number, /* amino‑acids & related */ "cystine_g": Number, "histidine_g": Number, "isoleucine_g": Number, "leucine_g": Number, "lysine_g": Number, "methionine_g": Number, "phenylalanine_g": Number, "threonine_g": Number, "tryptophan_g": Number, "tyrosine_g": Number, "valine_g": Number, /* fats & fatty‑acids */ "monounsaturated_fat_g": Number, "polyunsaturated_fat_g": Number, "omega3_g": Number, "omega6_g": Number, /* carbohydrates */ "starch_g": Number }, "water_ml": Number, "drink_ml": Number } } ``` ### Sleep ```json { "data_enrichment": { "sleep_contributors": Array, "sleep_score": Number }, "device_data": { "activation_timestamp": String, "data_provided": Array, "hardware_version": String, "last_upload_date": String, "manufacturer": String, "name": String, "other_devices": Array, "serial_number": String, "software_version": String }, "heart_rate_data": { "detailed": { "hr_samples": Array, "hrv_samples_rmssd": Array, "hrv_samples_sdnn": Array }, "summary": { "avg_hr_bpm": Number, "avg_hrv_rmssd": Number, "avg_hrv_sdnn": Number, "hr_zone_data": Array, "max_hr_bpm": Number, "min_hr_bpm": Number, "resting_hr_bpm": Number, "user_max_hr_bpm": Number } }, "metadata": { "end_time": String, // required – ISO‑8601 "start_time": String, // required – ISO‑8601 "is_nap": Boolean, "summary_id": String, "timestamp_localization": Number, "upload_type": SleepUploadType // required }, "readiness_data": { "readiness": Number, "recovery_level": RecoveryLevel }, "respiration_data": { "breaths_data": { "avg_breaths_per_min": Number, "end_time": String, "max_breaths_per_min": Number, "min_breaths_per_min": Number, "on_demand_reading": Boolean, "samples": Array, "start_time": String }, "oxygen_saturation_data": { "avg_saturation_percentage": Number, "end_time": String, "samples": Array, "start_time": String }, "snoring_data": { "end_time": String, "num_snoring_events": Number, "samples": Array, "start_time": String, "total_snoring_duration_seconds": Number } }, "scores": { "sleep": Number }, "sleep_durations_data": { "asleep": { "duration_asleep_state_seconds": Number, "duration_deep_sleep_state_seconds": Number, "duration_light_sleep_state_seconds": Number, "duration_REM_sleep_state_seconds": Number, "num_REM_events": Number }, "awake": { "duration_awake_state_seconds": Number, "duration_long_interruption_seconds": Number, "duration_short_interruption_seconds": Number, "num_out_of_bed_events": Number, "num_wakeup_events": Number, "sleep_latency_seconds": Number, "wake_up_latency_seconds": Number }, "hypnogram_samples": Array, "other": { "duration_in_bed_seconds": Number, "duration_unmeasurable_sleep_seconds": Number }, "sleep_efficiency": Number }, "temperature_data": { "delta": Number } } ``` ## Enums ### DeviceDataType Represents data types that a certain device contributed to | Name | Value | | ------------------------- | ------------------ | | Steps | STEPS | | Active minutes | ACTIVE\_MINUTES | | BMR | BMR | | Calories | CALORIES | | Distance | DISTANCE | | Heart Rate | HEART\_RATE | | Oxygen Saturation (SpO2) | OXYGEN\_SATURATION | | Sleep type classification | SLEEP\_TYPE | | Speed | SPEED | | Cadence | CADENCE | ### AFibFlag | Name | Value | | --------------------------- | ----- | | Negative (AFib not present) | 0 | | Positive (AFib present) | 1 | | Inconclusive | 2 | ### ActivityType | Name | Value | | ----------------------- | ----- | | In Vehicle | 0 | | Biking | 1 | | Still | 3 | | Unknown | 4 | | Tilting | 5 | | Walking | 7 | | Running | 8 | | Aerobics | 9 | | Badminton | 10 | | Baseball | 11 | | Basketball | 12 | | Biathlon | 13 | | Handbiking | 14 | | Mountain Biking | 15 | | Road Biking | 16 | | Spinning | 17 | | Stationary Biking | 18 | | Utility Biking | 19 | | Boxing | 20 | | Calisthenics | 21 | | Circuit Training | 22 | | Cricket | 23 | | Dancing | 24 | | Elliptical | 25 | | Fencing | 26 | | American Football | 27 | | Australian Football | 28 | | English Football | 29 | | Frisbee | 30 | | Gardening | 31 | | Golf | 32 | | Gymnastics | 33 | | Handball | 34 | | Hiking | 35 | | Hockey | 36 | | Horseback Riding | 37 | | Housework | 38 | | Jumping Rope | 39 | | Kayaking | 40 | | Kettlebell Training | 41 | | Kickboxing | 42 | | Kitesurfing | 43 | | Martial Arts | 44 | | Meditation | 45 | | Mixed Martial Arts | 46 | | P90X Exercises | 47 | | Paragliding | 48 | | Pilates | 49 | | Polo | 50 | | Racquetball | 51 | | Rock Climbing | 52 | | Rowing | 53 | | Rowing Machine | 54 | | Rugby | 55 | | Jogging | 56 | | Running On Sand | 57 | | Treadmill Running | 58 | | Sailing | 59 | | Scuba Diving | 60 | | Skateboarding | 61 | | Skating | 62 | | Cross Skating | 63 | | Indoor Rollerblading | 64 | | Skiing | 65 | | Back Country Skiing | 66 | | Cross Country Skiing | 67 | | Downhill Skiing | 68 | | Kite Skiing | 69 | | Roller Skiing | 70 | | Sledding | 71 | | Snowboarding | 73 | | Snowmobile | 74 | | Snowshoeing | 75 | | Squash | 76 | | Stair Climbing | 77 | | Stair Climbing Machine | 78 | | Stand Up Paddleboarding | 79 | | Strength Training | 80 | | Surfing | 81 | | Swimming | 82 | | Swimming In Pool | 83 | | Open Water Swimming | 84 | | Table Tennis | 85 | | Team Sport | 86 | | Tennis | 87 | | Treadmill | 88 | | Volleyball | 89 | | Beach Volleyball | 90 | | Indoor Volleyball | 91 | | Wakeboarding | 92 | | Walking Fitness | 93 | | Nording Walking | 94 | | Walking Treadmill | 95 | | Waterpolo | 96 | | Weightlifting | 97 | | Wheelchair | 98 | | Windsurfing | 99 | | Yoga | 100 | | Zumba | 101 | | Diving | 102 | | Ergometer | 103 | | Ice Skating | 104 | | Indoor Skating | 105 | | Curling | 106 | | Other | 108 | | Crossfit | 113 | | HIIT | 114 | | Interval Training | 115 | | Walkin Stroller | 116 | | Elevator | 117 | | Escalator | 118 | | Archery | 119 | | Softball | 120 | | Guided Breathing | 122 | | Cardio Training | 123 | | Lacrosse | 124 | | Stretching | 125 | | Triathlon | 126 | | INLINE\_SKATING | 127 | | SKY\_DIVING | 128 | | PADDLING | 129 | | MOUNTAINEERING | 130 | | FISHING | 131 | | WATER\_SKIING | 132 | | INDOOR\_RUNNING | 133 | | PADEL\_TENNIS | 134 | | DRIVING | 135 | | OFF\_ROAD\_DRIVING | 136 | | MOTORBIKING | 137 | | MOTOR\_RACING | 138 | | ENDURO | 139 | | CANOEING | 140 | | ORIENTEERING | 141 | | HANG\_GLIDING | 142 | | FLYING | 143 | | HOT\_AIR\_BALLOONING | 144 | | JET\_SKIING | 145 | | POWER\_BOATING | 146 | | GAELIC\_FOOTBALL | 147 | | HURLING | 148 | ### ActivityLevel | Name | Value | | ---------------- | ----- | | Unknown | 0 | | Rest | 1 | | Inactive | 2 | | Low Intensity | 3 | | Medium Intensity | 4 | | High Intensity | 5 | ### HeartRateZone | Name | Value | | --------------------------- | ----- | | Zone 0 | 0 | | Zone 1 | 1 | | Zone 2 | 2 | | Zone 3 | 3 | | Zone 4 | 4 | | Zone 5 | 5 | | Other/custom classification | 6 | ### SleepLevel | Name | Value | | ---------- | ----- | | Unknown | 0 | | Awake | 1 | | Sleeping | 2 | | Out of Bed | 3 | | Light | 4 | | Deep | 5 | | REM | 6 | ### UploadType | Name | Value | | ------------------ | ----- | | Unknown | 0 | | Automatic | 1 | | Manual | 2 | | Update | 3 | | Delete | 4 | | Pending | 5 | | Third party upload | 6 | ### SleepUploadType | Name | Value | | ------------- | ----- | | Unknown | 0 | | Manual | 1 | | Automatic | 2 | | Tentative | 3 | | Indeterminate | 4 | ### StrokeType | Name | Value | | ------------ | ------------ | | OTHER | other | | FREESTYLE | freestyle | | BACKSTROKE | backstroke | | BREASTSTROKE | breaststroke | | BUTTERFLY | butterfly | ### GlucoseFlag | Name | Value | | ------ | ----- | | Normal | 0 | | High | 1 | | Low | 2 | ### NutritionUnits | Name | Value | | ----------- | ----- | | Unknown | 0 | | Gram | 1 | | Teaspoon | 2 | | Tablespoon | 3 | | Cup | 4 | | Medium Egg | 5 | | Large Egg | 6 | | Small Egg | 7 | | Milliliter | 8 | | Ounce | 9 | | Count | 10 | | Scoop | 11 | | Fluid Ounce | 12 | ### RecoveryLevel | Name | Value | | ----------- | ----- | | Unknown | 0 | | Very Poor | 1 | | Poor | 2 | | Compromised | 3 | | Ok | 4 | | Good | 5 | | Very Good | 6 | ### TrendArrow | Name | Value | | ---------------- | ----- | | UNKNOWN | 0 | | FALLING\_QUICKLY | 1 | | FALLING | 2 | | FLAT | 3 | | RISING | 4 | | RISING\_QUICKLY | 5 | ### MenstruationFlow | Name | Value | | ------- | ----- | | UNKNOWN | 0 | | NONE | 1 | | LIGHT | 2 | | MEDIUM | 3 | | HEAVY | 4 | | HAD | 5 | ### MealType | Name | Value | | ---------------- | ----- | | UNKNOWN | 0 | | BREAKFAST | 1 | | MORNING\_SNACK | 2 | | LUNCH | 3 | | AFTERNOON\_SNACK | 4 | | DINNER | 5 | | SNACK | 6 | ### StressLevel | Name | Value | | ----------------- | -------- | | NOT\_ENOUGH\_DATA | -1 | | REST | 1 - 25 | | LOW | 26 - 50 | | MEDIUM | 51 - 75 | | HIGH | 76 - 100 | ### HeartRateContext | Name | Value | | ----------- | ----- | | NOT\_SET | 0 | | ACTIVE | 1 | | NOT\_ACTIVE | 2 | ### MenstrualPhase | Name | Value | | ----------------- | ----------------- | | MENSTRUAL | menstrual | | FOLLICULAR | follicular | | OVULATION | ovulation | | LUTEAL | luteal | | PMS | pms | | FERTILE | fertile | | FIRST\_TRIMESTER | first\_trimester | | SECOND\_TRIMESTER | second\_trimester | | THIRD\_TRIMESTER | third\_trimester | | UNKNOWN | unknown | ## PlannedWorkout ``` { "steps": Array, "metadata": { "id": String, "estimated_if": Number, "provider": String, "estimated_distance_meters": Number, "estimated_elevation_gain_meters": Number, "estimated_energy_kj": Number, "estimated_speed_meters_per_second": Number, "planned_date": ISO-Date-String, "created_date": ISO-Date-String, "estimated_tss": Number, "type": WorkoutTypeNumber, "name": String, "description": String, "pool_length_meters": Number, "estimated_calories": Number, "estimated_duration_seconds": Number } } ``` The Workout Types follow the same numbering as the Activity Types that can be found in [https://docs.tryterra.co/reference/enums#activitytype](https://docs.tryterra.co/reference/enums#activitytype) ### PlannedWorkoutStep ``` { "description": String, "order": Number, "intensity": Number, "durations": Array "type": 0, "targets": Array, "stroke_type": Number, //for Swimming Workouts "equipement_type": Number, //for Swimming Workouts "exercice_category" : Number, //for Strength and Cardio workouts "exercice_name": String, //for Strength and Cardio workouts "weight_kg": Number, //for Strength workouts } ``` | Step Intensity Name | Step Intensity Number Value | | ------------------- | --------------------------- | | REST | 0 | | WARMUP | 1 | | COOLDOWN | 2 | | RECOVERY | 3 | | INTERVAL | 4 | | ACTIVE | 5 | ### PlannedWorkoutRepeatStep ``` { "description": String, "order": Number, "intensity": Number, "durations": Array "type": 1, "targets": Array, "steps" : Array } ``` ### PlannedWorkoutStepTarget | Step Target Type | Model | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | SPEED | `{ "target_type": 0, "speed_meters_per_second": Number, "speed_meters_per_second_low": Number, "speed_meters_per_second_high": Number }` | | HEART\_RATE | `{ "target_type": 1, "hr_bpm_high": Number, "hr_bpm_low": Number }` | | OPEN | `{ "target_type": 2 }` | | CADENCE | `{ "target_type": 3, "cadence": Number, "cadence_high": Number, "cadence_low" : Number }` | | POWER | `{ "target_type": 4, "power_watt_high": Number, "power_watt_low": Number, "power_watt": Number }` | | SWIM\_STROKE | `{ "target_type": 8, "swim_strokes": Number }` | | PACE | `{ "target_type": 11, "speed_meters_per_second": Number, "speed_meters_per_second_low": Number, "speed_meters_per_second_high": Number }` | | HEART\_RATE\_THRESHOLD\_PERCENTAGE | `{ "target_type": 12, "hr_percentage": Number, "hr_percentage_low": Number, "hr_percentage_high": Number }` | | HEART\_RATE\_MAX\_PERCENTAGE | `{ "target_type": 13, "hr_percentage": Number, "hr_percentage_low": Number, "hr_percentage_high": Number }` | | SPEED\_PERCENTAGE | `{ "target_type": 14, "speed_percentage": Number, "speed_percentage_low": Number, "speed_percentage_high": Number }` | | POWER\_PERCENTAGE | `{ "target_type": 15, "power_percentage": Number, "power_percentage_low": Number, "power_percentage_high": Number }` | | REPETITION | `{ "target_type": 16, "repetitions": Number }` | ### PlannedWorkoutStepDuration | Step Duration Type | Model | | -------------------- | ----------------------------------------------------- | | TIME | `{ "duration_type": 0, "seconds": Number }` | | DISTANCE\_METERS | `{ "duration_type": 1, "distance_meters": Number }` | | HR\_LESS\_THAN | `{ "duration_type": 2, "hr_below_bpm": Number }` | | HR\_GREATER\_THAN | `{ "duration_type": 3, "hr_above_bpm": Number }` | | CALORIES | `{ "duration_type": 4, "calories": 2.0 }` | | OPEN | `{ "duration_type": 5 }` | | POWER\_LESS\_THAN | `{ "duration_type": 6, "power_below_watts": Number }` | | POWER\_GREATER\_THAN | `{ "duration_type": 7, "power_above_watts": Number }` | | REPS | `{ "duration_type": 9, "reps": Number }` | | FIXED\_REST | `{ "duration_type": 10, "rest_seconds": Number }` | | STEPS | `{ "duration_type": 12, "steps": Number }` | ### Enums | Stroke Type | Value | | ------------ | ----- | | BACKSTROKE | 0 | | BREASTSTROKE | 1 | | DRILL | 2 | | BUTTERFLY | 3 | | FREESTYLE | 4 | | MIXED | 5 | | IM | 6 | | Equipement Type | Value | | ---------------- | ----- | | NONE | 0 | | SWIM\_FINS | 1 | | SWIM\_KICKBOARD | 2 | | SWIM\_PADDLES | 3 | | SWIM\_PULL\_BUOY | 4 | | SWIM\_SNORKEL | 5 | | Exercice Type | Value | | ------------------- | ----- | | UNKNOWN | 0 | | BENCH\_PRESS | 1 | | CALF\_RAISE | 2 | | CARDIO | 3 | | CARRY | 4 | | CHOP | 5 | | CORE | 6 | | CRUNCH | 7 | | CURL | 8 | | DEADLIFT | 9 | | FLYE | 10 | | HIP\_RAISE | 11 | | HIP\_STABILITY | 12 | | HIP\_SWING | 13 | | HYPEREXTENSION | 14 | | LATERAL\_RAISE | 15 | | LEG\_CURL | 16 | | LEG\_RAISE | 17 | | LUNGE | 18 | | OLYMPIC\_LIFT | 19 | | PLANK | 20 | | PLYO | 21 | | PULL\_UP | 22 | | PUSH\_UP | 23 | | ROW | 24 | | SHOULDER\_PRESS | 25 | | SHOULDER\_STABILITY | 26 | | SHRUG | 27 | | SIT\_UP | 28 | | SQUAT | 29 | | TOTAL\_BODY | 30 | | TRICEPS\_EXTENSION | 31 | | WARM\_UP | 32 | | RUN | 33 | | BIKE | 34 | | CARDIO\_SENSORS | 35 |