{"openapi":"3.1.0","info":{"title":"BodySpec API","description":"This API allows BodySpec users to integrate their DEXA scan data with other platforms.\n\nIt is currently in early-access and we welcome feedback to help us improve it. For help with the API, please email us at dev-support@bodyspec.com\n\n## Authentication\n\nMost endpoints require authentication. BodySpec uses an OIDC-compliant authentication server that supports two authentication methods:\n\n#### Option 1: OAuth2 (Recommended for interactive use)\nBest for web applications and interactive API testing:\n\n1. Click the \"Authorize\" button in the API documentation\n2. You'll be redirected to BodySpec's Auth Portal to login\n3. After successful login, you'll be redirected back with an access token\n4. The token will be automatically used for API requests\n\n#### Option 2: Partner Auth (For partner integrations only)\nFor authorized partner integrations:\n\n1. Contact BodySpec to obtain partner credentials\n2. Include credentials in the `Authorization` header: `Basic base64(partner_id:partner_secret)`\n","license":{"name":"Proprietary"},"version":"0.19.1"},"servers":[{"url":"https://app.bodyspec.com","description":"Production server"}],"paths":{"/api/info":{"get":{"tags":["API Status"],"summary":"Get API info","description":"Get basic information about the API including version and available endpoints.","operationId":"api_info_api_info_get","responses":{"200":{"description":"API information","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Api Info Api Info Get"},"example":{"name":"BodySpec API","version":"0.19.1","description":"API for users to access their BodySpec data","documentation":"/docs","openapi":"/openapi.json","health":"/health"}}}}}}},"/health":{"get":{"tags":["API Status"],"summary":"Check API health","description":"Check if the API is healthy and responding to requests.","operationId":"health_check_health_get","responses":{"200":{"description":"API is healthy","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Check Health Get"},"example":{"status":"healthy","environment":"development","version":"0.19.1"}}}}}}},"/api/v1/users/me":{"get":{"tags":["Users"],"summary":"Get user info","description":"Retrieve the profile information of the currently authenticated user.","operationId":"_get_user_api_v1_users_me_get","responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"},"example":{"user_id":"12345678901234567890123456789012","email":"user@example.com","first_name":"John","last_name":"Doe","phone":"+14155551234"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"User not found","content":{"application/json":{"example":{"detail":"User not found"}}}}},"security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}]},"patch":{"tags":["Users"],"summary":"Update user info","description":"Update the profile information of the currently authenticated user.","operationId":"_update_user_api_v1_users_me_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdateRequest"}}},"required":true},"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"},"example":{"user_id":"12345678901234567890123456789012","email":"user@example.com","first_name":"John","last_name":"Smith","phone":"+14155559999"}}}},"400":{"description":"Invalid request data","content":{"application/json":{"example":{"detail":[{"loc":["body","phone"],"msg":"string does not match regex","type":"value_error.str.regex"}]}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"User not found","content":{"application/json":{"example":{"detail":"User not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}]}},"/api/v1/users/me/appts":{"get":{"tags":["Appointments"],"summary":"List user appts","description":"Retrieve paginated list of appointments for the authenticated user.","operationId":"_list_appts_api_v1_users_me_appts_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ApptStatus"},{"type":"null"}],"description":"Filter by appointment outcome: 'scheduled', 'pending_scan' (started but results not published yet, or no evidence the location was operating), 'completed', or 'no_show'","title":"Status"},"description":"Filter by appointment outcome: 'scheduled', 'pending_scan' (started but results not published yet, or no evidence the location was operating), 'completed', or 'no_show'"},{"name":"sort_order","in":"query","required":false,"schema":{"$ref":"#/components/schemas/SortOrder","description":"Sort order: 'newest_first' (latest appointments first) or 'oldest_first' (earliest appointments first)","default":"newest_first"},"description":"Sort order: 'newest_first' (latest appointments first) or 'oldest_first' (earliest appointments first)"},{"name":"start_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time = value","title":"Start Time[Eq]"},"description":"ISO 8601 datetime: start_time = value"},{"name":"start_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time > value","title":"Start Time[Gt]"},"description":"ISO 8601 datetime: start_time > value"},{"name":"start_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time >= value","title":"Start Time[Gte]"},"description":"ISO 8601 datetime: start_time >= value"},{"name":"start_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time < value","title":"Start Time[Lt]"},"description":"ISO 8601 datetime: start_time < value"},{"name":"start_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time <= value","title":"Start Time[Lte]"},"description":"ISO 8601 datetime: start_time <= value"},{"name":"reserve_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time = value","title":"Reserve Time[Eq]"},"description":"ISO 8601 datetime: reserve_time = value"},{"name":"reserve_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time > value","title":"Reserve Time[Gt]"},"description":"ISO 8601 datetime: reserve_time > value"},{"name":"reserve_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time >= value","title":"Reserve Time[Gte]"},"description":"ISO 8601 datetime: reserve_time >= value"},{"name":"reserve_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time < value","title":"Reserve Time[Lt]"},"description":"ISO 8601 datetime: reserve_time < value"},{"name":"reserve_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time <= value","title":"Reserve Time[Lte]"},"description":"ISO 8601 datetime: reserve_time <= value"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApptListResponse"},"example":{"appts":[{"appt_id":"a5950beea2a7487fb79cfdbeb77fa555","start_time":"2024-03-15T10:30:00-07:00","duration_minutes":12,"location":{"location_id":"a5950beea2a7487fb79cfdbeb77fa555","name":"BodySpec - Culver City","address":"5847 Uplander Way","city_state":"Culver City, CA","timezone":"US/Pacific"},"service":{"name":"DEXA","description":"Dual-energy X-ray Absorptiometry scan for body composition"},"status":"completed","reserve_time":"2024-03-01T15:00:00Z"},{"appt_id":"d7ecde6e9518008607e0e1e3b4b960d9","start_time":"2024-04-20T14:00:00-05:00","duration_minutes":12,"location":{"location_id":"d7ecde6e9518008607e0e1e3b4b960d9","name":"BodySpec - Dallas","address":"6310 Lemmon Ave Suite 150","city_state":"Dallas, TX","timezone":"US/Central"},"service":{"name":"DEXA","description":"Dual-energy X-ray Absorptiometry scan for body composition"},"status":"scheduled"}],"pagination":{"page":1,"page_size":20,"has_more":true}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"User not found","content":{"application/json":{"example":{"detail":"User not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/appts/{appt_id}":{"get":{"tags":["Appointments"],"summary":"Get appt details","description":"Retrieve detailed information for a specific appointment.","operationId":"_get_appt_api_v1_users_me_appts__appt_id__get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"appt_id","in":"path","required":true,"schema":{"type":"string","title":"Appt Id"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"},"example":{"appt_id":"a5950beea2a7487fb79cfdbeb77fa555","start_time":"2024-03-15T10:30:00-07:00","duration_minutes":12,"location":{"location_id":"a5950beea2a7487fb79cfdbeb77fa555","name":"BodySpec - Culver City","address":"5847 Uplander Way","city_state":"Culver City, CA","timezone":"US/Pacific"},"service":{"name":"DEXA","description":"Dual-energy X-ray Absorptiometry scan for body composition"},"status":"completed"}}}},"404":{"description":"User not found","content":{"application/json":{"example":{"detail":"User not found"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/":{"get":{"tags":["Results"],"summary":"List user results","description":"Get a paginated list of all results for the authenticated user.","operationId":"_get_results_api_v1_users_me_results__get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsListResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}":{"get":{"tags":["Results"],"summary":"Get result details","description":"Get detailed information about a specific result including available sections.","operationId":"_get_result_detail_api_v1_users_me_results__result_id__get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultDetailResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found","content":{"application/json":{"example":{"detail":"Result not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}/dexa/scan-info":{"get":{"tags":["Results"],"summary":"DEXA - Scan info","description":"Get basic scan metadata and information for a DEXA result.","operationId":"_get_dexa_scan_info_api_v1_users_me_results__result_id__dexa_scan_info_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaScanInfoResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found or section not available","content":{"application/json":{"example":{"detail":"Result not found"}}}},"400":{"description":"Result is not a DEXA scan","content":{"application/json":{"example":{"detail":"Result is not a DEXA scan"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}/dexa/composition":{"get":{"tags":["Results"],"summary":"DEXA - Body comp","description":"Get body composition data by region for a DEXA result.","operationId":"_get_dexa_composition_api_v1_users_me_results__result_id__dexa_composition_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaCompositionResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found or section not available","content":{"application/json":{"example":{"detail":"Result not found"}}}},"400":{"description":"Result is not a DEXA scan","content":{"application/json":{"example":{"detail":"Result is not a DEXA scan"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}/dexa/bone-density":{"get":{"tags":["Results"],"summary":"DEXA - Bone density","description":"Get bone mineral density measurements for a DEXA result.","operationId":"_get_dexa_bone_density_api_v1_users_me_results__result_id__dexa_bone_density_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaBoneDensityResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found or section not available","content":{"application/json":{"example":{"detail":"Result not found"}}}},"400":{"description":"Result is not a DEXA scan","content":{"application/json":{"example":{"detail":"Result is not a DEXA scan"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}/dexa/percentiles":{"get":{"tags":["Results"],"summary":"DEXA - Percentiles","description":"Get age and gender-matched percentile rankings for a DEXA result.","operationId":"_get_dexa_percentiles_api_v1_users_me_results__result_id__dexa_percentiles_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}},{"name":"min_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum age for reference range (optional)","title":"Min Age"},"description":"Minimum age for reference range (optional)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Maximum age for reference range (optional)","title":"Max Age"},"description":"Maximum age for reference range (optional)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaPercentilesResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found or section not available","content":{"application/json":{"example":{"detail":"Result not found"}}}},"400":{"description":"Result is not a DEXA scan","content":{"application/json":{"example":{"detail":"Result is not a DEXA scan"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}/dexa/visceral-fat":{"get":{"tags":["Results"],"summary":"DEXA - Visceral fat","description":"Get visceral adipose tissue analysis for a DEXA result.","operationId":"_get_dexa_visceral_fat_api_v1_users_me_results__result_id__dexa_visceral_fat_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaVisceralFatResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found or section not available","content":{"application/json":{"example":{"detail":"Result not found"}}}},"400":{"description":"Result is not a DEXA scan","content":{"application/json":{"example":{"detail":"Result is not a DEXA scan"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me/results/{result_id}/dexa/rmr":{"get":{"tags":["Results"],"summary":"DEXA - RMR","description":"Get resting metabolic rate estimates for a DEXA result.","operationId":"_get_dexa_rmr_api_v1_users_me_results__result_id__dexa_rmr_get","security":[{"HTTPBearer":[]},{"OAuth2AuthorizationCodeBearer":[]}],"parameters":[{"name":"result_id","in":"path","required":true,"schema":{"type":"string","title":"Result Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaRmrResponse"}}}},"401":{"description":"Not authenticated","content":{"application/json":{"example":{"detail":"Not authenticated"}}}},"403":{"description":"Insufficient permissions","content":{"application/json":{"example":{"detail":"Insufficient permissions"}}}},"404":{"description":"Result not found or section not available","content":{"application/json":{"example":{"detail":"Result not found"}}}},"400":{"description":"Result is not a DEXA scan","content":{"application/json":{"example":{"detail":"Result is not a DEXA scan"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/locations/{location_id}/availability":{"get":{"tags":["Availability"],"summary":"Get availability at a location","description":"Get available appointment slots at a specific location. Use start_time filters to narrow results. Availability is limited to 24 hours through 120 days in the future.","operationId":"_get_availability_api_v1_locations__location_id__availability_get","parameters":[{"name":"location_id","in":"path","required":true,"schema":{"type":"string","title":"Location Id"}},{"name":"start_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime: start_time = value. Uses location timezone if not specified.","title":"Start Time[Eq]"},"description":"ISO 8601 datetime: start_time = value. Uses location timezone if not specified."},{"name":"start_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime: start_time > value. Uses location timezone if not specified.","title":"Start Time[Gt]"},"description":"ISO 8601 datetime: start_time > value. Uses location timezone if not specified."},{"name":"start_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime: start_time >= value. Uses location timezone if not specified.","title":"Start Time[Gte]"},"description":"ISO 8601 datetime: start_time >= value. Uses location timezone if not specified."},{"name":"start_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime: start_time < value. Uses location timezone if not specified.","title":"Start Time[Lt]"},"description":"ISO 8601 datetime: start_time < value. Uses location timezone if not specified."},{"name":"start_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime: start_time <= value. Uses location timezone if not specified.","title":"Start Time[Lte]"},"description":"ISO 8601 datetime: start_time <= value. Uses location timezone if not specified."},{"name":"service_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional service/product ID to filter by","title":"Service Id"},"description":"Optional service/product ID to filter by"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":100,"title":"Page Size"},"description":"Items per page (max 100)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityResponse"},"example":{"slots":[{"appt_id":"appt123","location_id":"loc456","service_id":"svc789","start_time":"2025-01-15T14:00:00-08:00","duration_minutes":12,"timezone":"America/Los_Angeles","is_available":true}],"pagination":{"page":1,"page_size":100,"has_more":true},"max_availability_days":120}}}},"400":{"description":"Invalid request data","content":{"application/json":{"example":{"detail":[{"loc":["body","phone"],"msg":"string does not match regex","type":"value_error.str.regex"}]}}}},"404":{"description":"Resource not found","content":{"application/json":{"example":{"detail":"Location not found"}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Service type does not have a configured duration"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/api/v1/locations/availability":{"get":{"tags":["Availability"],"summary":"Get availability across multiple locations","description":"Get available appointment slots across multiple locations in a single request. Slots are sorted by start time globally across all locations.","operationId":"_get_multi_availability_api_v1_locations_availability_get","parameters":[{"name":"location_ids","in":"query","required":true,"schema":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Location IDs to search (1-10, repeated query param)","title":"Location Ids"},"description":"Location IDs to search (1-10, repeated query param)"},{"name":"start_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime with timezone: start_time = value","title":"Start Time[Eq]"},"description":"ISO 8601 datetime with timezone: start_time = value"},{"name":"start_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime with timezone: start_time > value","title":"Start Time[Gt]"},"description":"ISO 8601 datetime with timezone: start_time > value"},{"name":"start_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime with timezone: start_time >= value","title":"Start Time[Gte]"},"description":"ISO 8601 datetime with timezone: start_time >= value"},{"name":"start_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime with timezone: start_time < value","title":"Start Time[Lt]"},"description":"ISO 8601 datetime with timezone: start_time < value"},{"name":"start_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 datetime with timezone: start_time <= value","title":"Start Time[Lte]"},"description":"ISO 8601 datetime with timezone: start_time <= value"},{"name":"service_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional service/product ID to filter by","title":"Service Id"},"description":"Optional service/product ID to filter by"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":100,"title":"Page Size"},"description":"Items per page (max 100)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultiLocationAvailabilityResponse"},"example":{"slots":[{"appt_id":"appt123","location_id":"loc456","service_id":"svc789","start_time":"2025-01-15T14:00:00-08:00","duration_minutes":12,"timezone":"America/Los_Angeles","is_available":true},{"appt_id":"appt456","location_id":"loc789","service_id":"svc789","start_time":"2025-01-15T15:00:00-06:00","duration_minutes":12,"timezone":"America/Chicago","is_available":true}],"pagination":{"page":1,"page_size":100,"results":2,"has_more":false},"max_availability_days":120,"skipped_location_ids":["bad_loc"]}}}},"400":{"description":"Invalid request data","content":{"application/json":{"example":{"detail":[{"loc":["body","phone"],"msg":"string does not match regex","type":"value_error.str.regex"}]}}}},"500":{"description":"Internal server error","content":{"application/json":{"example":{"detail":"Service type does not have a configured duration"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/api/v1/locations":{"get":{"tags":["Locations"],"summary":"List locations","description":"Get a list of BodySpec scan locations with optional filtering by type and geographic proximity.","operationId":"_list_locations_api_v1_locations_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"},{"name":"location_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by type: 'mobile' or 'storefront'","title":"Location Type"},"description":"Filter by type: 'mobile' or 'storefront'"},{"name":"lat","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Latitude for geographic search","title":"Lat"},"description":"Latitude for geographic search"},{"name":"lng","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Longitude for geographic search","title":"Lng"},"description":"Longitude for geographic search"},{"name":"radius_miles","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":500,"minimum":0},{"type":"null"}],"description":"Search radius in miles (max 500)","title":"Radius Miles"},"description":"Search radius in miles (max 500)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationsListResponse"},"example":{"locations":[{"location_id":"abc123","name":"BodySpec - San Francisco","location_type":"storefront","address":{"address_line1":"295 Broadway","city":"San Francisco","state":"CA","postal_code":"94108","country":"US"},"coordinates":{"latitude":37.7986,"longitude":-122.4063},"timezone":"America/Los_Angeles","distance_miles":1.23}],"pagination":{"page":1,"page_size":20,"has_more":true}}}}},"400":{"description":"Invalid request data","content":{"application/json":{"example":{"detail":[{"loc":["body","phone"],"msg":"string does not match regex","type":"value_error.str.regex"}]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/api/v1/locations/{location_id}":{"get":{"tags":["Locations"],"summary":"Get location details","description":"Get detailed information about a specific location.","operationId":"_get_location_api_v1_locations__location_id__get","parameters":[{"name":"location_id","in":"path","required":true,"schema":{"type":"string","title":"Location Id"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationResponse"},"example":{"location":{"location_id":"abc123","name":"BodySpec - San Francisco","location_type":"storefront","address":{"address_line1":"295 Broadway","city":"San Francisco","state":"CA","country":"US"},"coordinates":{"latitude":37.7986,"longitude":-122.4063},"timezone":"America/Los_Angeles"}}}}},"404":{"description":"Resource not found","content":{"application/json":{"example":{"detail":"Location not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/api/v1/services":{"get":{"tags":["Services"],"summary":"List available services","description":"Get a list of available scan/test services that can be booked. Currently returns DEXA scans only.","operationId":"_list_services_api_v1_services_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServicesListResponse"},"example":{"services":[{"name":"DEXA","description":"Dual-energy X-ray Absorptiometry","service_id":"8d0e0a1f561f4ec69dbca50ea14336d","service_code":"DXA","duration_minutes":15}],"pagination":{"page":1,"page_size":20,"has_more":false}}}}},"400":{"description":"Invalid request data","content":{"application/json":{"example":{"detail":[{"loc":["body","phone"],"msg":"string does not match regex","type":"value_error.str.regex"}]}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/api/v1/services/{service_id}":{"get":{"tags":["Services"],"summary":"Get service details","description":"Get detailed information about a specific service.","operationId":"_get_service_api_v1_services__service_id__get","parameters":[{"name":"service_id","in":"path","required":true,"schema":{"type":"string","title":"Service Id"}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceResponse"},"example":{"service":{"name":"DEXA","description":"Dual-energy X-ray Absorptiometry","service_id":"8d0e0a1f561f4ec69dbca50ea14336d","service_code":"DXA","duration_minutes":15}}}}},"404":{"description":"Resource not found","content":{"application/json":{"example":{"detail":"Service not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[]}},"/api/v1/partners/{partner_id}/reservations":{"get":{"tags":["Reservations"],"summary":"List partner reservations","description":"Retrieve paginated list of reservations for the authenticated partner.","operationId":"_list_partner_reservations_api_v1_partners__partner_id__reservations_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ApptStatus"},{"type":"null"}],"description":"Filter by appointment outcome: 'scheduled', 'pending_scan' (started but results not published yet, or no evidence the location was operating), 'completed', or 'no_show'. A reservation_no_show webhook is emitted when an appointment becomes 'no_show'","title":"Status"},"description":"Filter by appointment outcome: 'scheduled', 'pending_scan' (started but results not published yet, or no evidence the location was operating), 'completed', or 'no_show'. A reservation_no_show webhook is emitted when an appointment becomes 'no_show'"},{"name":"sort_order","in":"query","required":false,"schema":{"$ref":"#/components/schemas/SortOrder","description":"Sort order: 'newest_first' or 'oldest_first'","default":"newest_first"},"description":"Sort order: 'newest_first' or 'oldest_first'"},{"name":"start_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time = value","title":"Start Time[Eq]"},"description":"ISO 8601 datetime: start_time = value"},{"name":"start_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time > value","title":"Start Time[Gt]"},"description":"ISO 8601 datetime: start_time > value"},{"name":"start_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time >= value","title":"Start Time[Gte]"},"description":"ISO 8601 datetime: start_time >= value"},{"name":"start_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time < value","title":"Start Time[Lt]"},"description":"ISO 8601 datetime: start_time < value"},{"name":"start_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time <= value","title":"Start Time[Lte]"},"description":"ISO 8601 datetime: start_time <= value"},{"name":"reserve_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time = value","title":"Reserve Time[Eq]"},"description":"ISO 8601 datetime: reserve_time = value"},{"name":"reserve_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time > value","title":"Reserve Time[Gt]"},"description":"ISO 8601 datetime: reserve_time > value"},{"name":"reserve_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time >= value","title":"Reserve Time[Gte]"},"description":"ISO 8601 datetime: reserve_time >= value"},{"name":"reserve_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time < value","title":"Reserve Time[Lt]"},"description":"ISO 8601 datetime: reserve_time < value"},{"name":"reserve_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time <= value","title":"Reserve Time[Lte]"},"description":"ISO 8601 datetime: reserve_time <= value"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerReservationsListResponse"},"example":{"reservations":[{"user":{"user_id":"a5950beea2a7487fb79cfdbeb77fa555","email":"user@example.com","first_name":"Jane","last_name":"Smith","phone":"+14155551234"},"appt":{"appt_id":"d7ecde6e9518008607e0e1e3b4b960d9","start_time":"2024-03-15T10:30:00-07:00","duration_minutes":12,"location":{"location_id":"loc123","name":"BodySpec - Culver City","location_type":"storefront"},"service":{"name":"DEXA","description":"DEXA Scan"},"status":"scheduled","reserve_time":"2024-03-01T15:30:00Z"}}],"pagination":{"page":1,"page_size":20,"results":1,"has_more":false}}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Reservations"],"summary":"Create reservation","description":"Create an appointment reservation for a partner integration.","operationId":"_create_reservation_api_v1_partners__partner_id__reservations_post","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationCreateRequest"}}}},"responses":{"201":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationResponse"},"example":{"appt_id":"12345","start_time":"2024-03-15T10:30:00-07:00","location":{"location_id":"a5950beea2a7487fb79cfdbeb77fa555","name":"BodySpec - Culver City","location_type":"storefront","address":{"address_line1":"5847 Uplander Way","city":"Culver City","state":"CA","postal_code":"90230","country":"US"},"coordinates":{"latitude":33.9874,"longitude":-118.3878},"timezone":"US/Pacific","description":"Free parking available in the lot behind the building","last_updated":"2024-02-01T18:00:00Z"},"user_id":"a5950beea2a7487fb79cfdbeb77fa555","reserve_time":"2024-03-15T10:30:00Z","external":{"user_id":"partner_user_456","appt_id":"partner_booking_123"}}}}},"400":{"description":"Invalid request or reservation failed"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"404":{"description":"Appointment not found"},"409":{"description":"Appointment already reserved"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/reservations/{appt_id}":{"get":{"tags":["Reservations"],"summary":"Get reservation","description":"Retrieve a single reservation and its current outcome. Use this to check what happened at one appointment without paging the reservation list.","operationId":"_get_partner_reservation_api_v1_partners__partner_id__reservations__appt_id__get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"appt_id","in":"path","required":true,"schema":{"type":"string","description":"BodySpec appointment ID","title":"Appt Id"},"description":"BodySpec appointment ID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerReservation"},"example":{"user":{"user_id":"a5950beea2a7487fb79cfdbeb77fa555","email":"jane@example.com","first_name":"Jane","last_name":"Doe","phone":"+15551234567"},"appt":{"appt_id":"d7ecde6e9518008607e0e1e3b4b960d9","start_time":"2024-03-15T10:30:00-07:00","duration_minutes":12,"location":{"location_id":"a5950beea2a7487fb79cfdbeb77fa555","name":"BodySpec - Culver City","address":"5847 Uplander Way","city_state":"Culver City, CA","timezone":"US/Pacific"},"service":{"name":"DEXA","description":"Dual-energy X-ray Absorptiometry scan for body composition"},"status":"completed","reserve_time":"2024-03-01T15:00:00Z"}}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"404":{"description":"Reservation not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Reservations"],"summary":"Cancel reservation","description":"Cancel an existing reservation.","operationId":"_cancel_reservation_api_v1_partners__partner_id__reservations__appt_id__delete","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"appt_id","in":"path","required":true,"schema":{"type":"string","description":"BodySpec appointment ID","title":"Appt Id"},"description":"BodySpec appointment ID"}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReservationCancelRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"204":{"description":"Reservation cancelled successfully"},"400":{"description":"Cannot cancel reservation"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"404":{"description":"Appointment not found"},"405":{"description":"Appointment cannot be cancelled"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users":{"get":{"tags":["Partner Users"],"summary":"List partner users","description":"Retrieve paginated list of users linked to the authenticated partner.","operationId":"_list_partner_users_api_v1_partners__partner_id__users_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"},{"name":"link_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: link_time = value","title":"Link Time[Eq]"},"description":"ISO 8601 datetime: link_time = value"},{"name":"link_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: link_time > value","title":"Link Time[Gt]"},"description":"ISO 8601 datetime: link_time > value"},{"name":"link_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: link_time >= value","title":"Link Time[Gte]"},"description":"ISO 8601 datetime: link_time >= value"},{"name":"link_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: link_time < value","title":"Link Time[Lt]"},"description":"ISO 8601 datetime: link_time < value"},{"name":"link_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: link_time <= value","title":"Link Time[Lte]"},"description":"ISO 8601 datetime: link_time <= value"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerUsersListResponse"},"example":{"users":[{"user_id":"a5950beea2a7487fb79cfdbeb77fa555","email":"user@example.com","first_name":"Jane","last_name":"Smith","phone":"+14155551234","link_time":"2024-01-15T10:30:00Z"}],"pagination":{"page":1,"page_size":20,"results":1,"has_more":false}}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/appts":{"get":{"tags":["Partner Appointments"],"summary":"List partner user appts","description":"Retrieve paginated list of appointments for a specific user linked to the partner.","operationId":"_list_partner_user_appointments_api_v1_partners__partner_id__users__user_id__appts_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ApptStatus"},{"type":"null"}],"description":"Filter by appointment outcome: 'scheduled', 'pending_scan' (started but results not published yet, or no evidence the location was operating), 'completed', or 'no_show'","title":"Status"},"description":"Filter by appointment outcome: 'scheduled', 'pending_scan' (started but results not published yet, or no evidence the location was operating), 'completed', or 'no_show'"},{"name":"sort_order","in":"query","required":false,"schema":{"$ref":"#/components/schemas/SortOrder","description":"Sort order: 'newest_first' or 'oldest_first'","default":"newest_first"},"description":"Sort order: 'newest_first' or 'oldest_first'"},{"name":"start_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time = value","title":"Start Time[Eq]"},"description":"ISO 8601 datetime: start_time = value"},{"name":"start_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time > value","title":"Start Time[Gt]"},"description":"ISO 8601 datetime: start_time > value"},{"name":"start_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time >= value","title":"Start Time[Gte]"},"description":"ISO 8601 datetime: start_time >= value"},{"name":"start_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time < value","title":"Start Time[Lt]"},"description":"ISO 8601 datetime: start_time < value"},{"name":"start_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: start_time <= value","title":"Start Time[Lte]"},"description":"ISO 8601 datetime: start_time <= value"},{"name":"reserve_time[eq]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time = value","title":"Reserve Time[Eq]"},"description":"ISO 8601 datetime: reserve_time = value"},{"name":"reserve_time[gt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time > value","title":"Reserve Time[Gt]"},"description":"ISO 8601 datetime: reserve_time > value"},{"name":"reserve_time[gte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time >= value","title":"Reserve Time[Gte]"},"description":"ISO 8601 datetime: reserve_time >= value"},{"name":"reserve_time[lt]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time < value","title":"Reserve Time[Lt]"},"description":"ISO 8601 datetime: reserve_time < value"},{"name":"reserve_time[lte]","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 datetime: reserve_time <= value","title":"Reserve Time[Lte]"},"description":"ISO 8601 datetime: reserve_time <= value"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApptListResponse"},"example":{"appts":[{"appt_id":"a5950beea2a7487fb79cfdbeb77fa555","start_time":"2024-03-15T10:30:00-07:00","duration_minutes":12,"location":{"location_id":"loc123","name":"BodySpec - Culver City","location_type":"storefront"},"service":{"name":"DEXA","description":"DEXA Scan"},"status":"completed","reserve_time":"2024-03-01T15:00:00Z"}],"pagination":{"page":1,"page_size":20,"results":1,"has_more":false}}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"User not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results":{"get":{"tags":["Partner Results"],"summary":"List partner user results","description":"Retrieve paginated list of results for a specific user linked to the partner.","operationId":"_list_partner_user_results_api_v1_partners__partner_id__users__user_id__results_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number (starts at 1)","default":1,"title":"Page"},"description":"Page number (starts at 1)"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Items per page (max 100)","default":20,"title":"Page Size"},"description":"Items per page (max 100)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultsListResponse"},"example":{"results":[{"result_id":"a5950beea2a7487fb79cfdbeb77fa555","start_time":"2024-03-15T10:30:00-07:00","location":{"location_id":"loc123","name":"BodySpec - Culver City","location_type":"storefront"},"service":{"name":"DEXA","description":"DEXA Scan"},"create_time":"2024-03-15T10:30:00-07:00","update_time":"2024-03-15T11:00:00-07:00"}],"pagination":{"page":1,"page_size":20,"results":1,"has_more":false}}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}":{"get":{"tags":["Partner Results"],"summary":"Get partner user result details","description":"Get detailed information about a specific result including available sections.","operationId":"_get_partner_user_result_detail_api_v1_partners__partner_id__users__user_id__results__result_id__get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultDetailResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/export":{"post":{"tags":["Partner Results"],"summary":"Create result export (Partner)","description":"Create a downloadable export in the specified format for a partner user's result.","operationId":"_create_partner_user_result_export_api_v1_partners__partner_id__users__user_id__results__result_id__export_post","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/InteractiveFormatRequest"},{"$ref":"#/components/schemas/PdfFormatRequest"},{"$ref":"#/components/schemas/ClassicPdfFormatRequest"}],"discriminator":{"propertyName":"format","mapping":{"bodyspec_interactive":"#/components/schemas/InteractiveFormatRequest","bodyspec_pdf":"#/components/schemas/PdfFormatRequest","classic_pdf":"#/components/schemas/ClassicPdfFormatRequest"}},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResultExportResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or result not accessible"},"404":{"description":"Result not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/dexa/composition":{"get":{"tags":["Partner Results"],"summary":"DEXA - Body comp (Partner)","description":"Get body composition data by region for a partner user's DEXA result.","operationId":"_get_partner_dexa_composition_api_v1_partners__partner_id__users__user_id__results__result_id__dexa_composition_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaCompositionResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found or section not available"},"400":{"description":"Result is not a DEXA scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/dexa/bone-density":{"get":{"tags":["Partner Results"],"summary":"DEXA - Bone density (Partner)","description":"Get bone mineral density measurements for a partner user's DEXA result.","operationId":"_get_partner_dexa_bone_density_api_v1_partners__partner_id__users__user_id__results__result_id__dexa_bone_density_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaBoneDensityResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found or section not available"},"400":{"description":"Result is not a DEXA scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/dexa/percentiles":{"get":{"tags":["Partner Results"],"summary":"DEXA - Percentiles (Partner)","description":"Get age and gender-matched percentile rankings for a partner user's DEXA result.","operationId":"_get_partner_dexa_percentiles_api_v1_partners__partner_id__users__user_id__results__result_id__dexa_percentiles_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"},{"name":"min_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Minimum age for reference range (optional)","title":"Min Age"},"description":"Minimum age for reference range (optional)"},{"name":"max_age","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Maximum age for reference range (optional)","title":"Max Age"},"description":"Maximum age for reference range (optional)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaPercentilesResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found or section not available"},"400":{"description":"Result is not a DEXA scan or invalid age range"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/dexa/visceral-fat":{"get":{"tags":["Partner Results"],"summary":"DEXA - Visceral fat (Partner)","description":"Get visceral adipose tissue analysis for a partner user's DEXA result.","operationId":"_get_partner_dexa_visceral_fat_api_v1_partners__partner_id__users__user_id__results__result_id__dexa_visceral_fat_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaVisceralFatResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found or section not available"},"400":{"description":"Result is not a DEXA scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/dexa/rmr":{"get":{"tags":["Partner Results"],"summary":"DEXA - RMR (Partner)","description":"Get resting metabolic rate estimates for a partner user's DEXA result.","operationId":"_get_partner_dexa_rmr_api_v1_partners__partner_id__users__user_id__results__result_id__dexa_rmr_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaRmrResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found or section not available"},"400":{"description":"Result is not a DEXA scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/results/{result_id}/dexa/scan-info":{"get":{"tags":["Partner Results"],"summary":"DEXA - Scan info (Partner)","description":"Get basic scan metadata and information for a partner user's DEXA result.","operationId":"_get_partner_dexa_scan_info_api_v1_partners__partner_id__users__user_id__results__result_id__dexa_scan_info_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"},{"name":"result_id","in":"path","required":true,"schema":{"type":"string","description":"Result ID","title":"Result Id"},"description":"Result ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DexaScanInfoResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"Result not found or section not available"},"400":{"description":"Result is not a DEXA scan"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/users/{user_id}/intake":{"post":{"tags":["Partner Intake"],"summary":"Create intake","description":"Create an intake form for a user linked to the partner.","operationId":"_create_intake_api_v1_partners__partner_id__users__user_id__intake_post","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","description":"User ID","title":"User Id"},"description":"User ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntakeCreateRequest"}}}},"responses":{"201":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntakeResponse"},"example":{"intake_id":"b8a5859c778f48c491eff5a294c67ab9"}}}},"400":{"description":"Invalid request or intake creation failed"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"404":{"description":"User not found"},"422":{"description":"Validation error"}}}},"/api/v1/partners/{partner_id}/orders":{"post":{"tags":["Partner Orders"],"summary":"Create order","description":"Upload a partner-generated medical order (metadata + PDF) for a user. The patient demographics on the order are used to create the DEXA intake for the scan, and the uploaded PDF is used as the medical order for the appointment. The three exclusion screens are optional and default to false; a true answer is rejected because screened-out patients cannot be scanned.","operationId":"_create_order_api_v1_partners__partner_id__orders_post","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body__create_order_api_v1_partners__partner_id__orders_post"}}}},"responses":{"201":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderCreateResponse"},"example":{"order_id":"ord_abc123"}}}},"400":{"description":"Invalid PDF or request"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or user not linked to partner"},"422":{"description":"Validation error in order metadata or patient demographics"}}},"get":{"tags":["Partner Orders"],"summary":"List orders","description":"List partner-generated orders with optional filtering and pagination.","operationId":"_list_orders_api_v1_partners__partner_id__orders_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page","default":20,"title":"Page Size"},"description":"Results per page"},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by user ID","title":"User Id"},"description":"Filter by user ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrdersListResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/orders/{order_id}":{"get":{"tags":["Partner Orders"],"summary":"Get order","description":"Get details of a specific partner order.","operationId":"_get_order_api_v1_partners__partner_id__orders__order_id__get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"order_id","in":"path","required":true,"schema":{"type":"string","description":"Order ID","title":"Order Id"},"description":"Order ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"404":{"description":"Resource not found","content":{"application/json":{"example":{"detail":"Order not found"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Partner Orders"],"summary":"Update order","description":"Update a partner order's provider metadata and/or replace the PDF. Patient demographics are fixed at creation. Revoked orders cannot be updated.","operationId":"_update_order_api_v1_partners__partner_id__orders__order_id__patch","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"order_id","in":"path","required":true,"schema":{"type":"string","description":"Order ID","title":"Order Id"},"description":"Order ID"}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body__update_order_api_v1_partners__partner_id__orders__order_id__patch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"400":{"description":"No update data provided or invalid PDF"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"404":{"description":"Resource not found","content":{"application/json":{"example":{"detail":"Order not found"}}}},"409":{"description":"Order has been revoked"},"422":{"description":"Validation error in order metadata"}}}},"/api/v1/partners/{partner_id}/webhooks":{"get":{"tags":["Partner Webhooks"],"summary":"List webhooks","description":"Retrieve all webhook configurations for the authenticated partner.","operationId":"list_webhooks_api_v1_partners__partner_id__webhooks_get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhooksListResponse"},"example":{"webhooks":[{"webhook_id":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4","url":"https://your-server.example.com/webhooks/bodyspec","event_type":"results_ready","enabled":true,"has_secret":true,"create_time":"2024-03-15T10:30:00Z"}]}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Partner Webhooks"],"summary":"Create webhook","description":"Create a new webhook configuration for the authenticated partner.","operationId":"create_webhook_api_v1_partners__partner_id__webhooks_post","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreateRequest"}}}},"responses":{"201":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"},"example":{"webhook_id":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4","url":"https://partner.example.com/webhooks","event_type":"results_ready","enabled":true,"has_secret":true,"create_time":"2024-03-15T10:30:00Z"}}}},"400":{"description":"Invalid webhook configuration"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch"},"503":{"description":"Webhook system not configured"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/partners/{partner_id}/webhooks/{webhook_id}":{"get":{"tags":["Partner Webhooks"],"summary":"Get webhook","description":"Retrieve details for a specific webhook configuration.","operationId":"get_webhook_api_v1_partners__partner_id__webhooks__webhook_id__get","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","description":"Webhook ID (32-character hex string)","title":"Webhook Id"},"description":"Webhook ID (32-character hex string)"}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"},"example":{"webhook_id":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4","url":"https://partner.example.com/webhooks","event_type":"results_ready","enabled":true,"has_secret":true,"create_time":"2024-03-15T10:30:00Z"}}}},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or webhook belongs to different partner"},"404":{"description":"Webhook not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Partner Webhooks"],"summary":"Update webhook","description":"Update an existing webhook configuration (partial update).","operationId":"update_webhook_api_v1_partners__partner_id__webhooks__webhook_id__patch","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","description":"Webhook ID (32-character hex string)","title":"Webhook Id"},"description":"Webhook ID (32-character hex string)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdateRequest"}}}},"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponse"},"example":{"webhook_id":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4","url":"https://partner.example.com/webhooks","event_type":"results_ready","enabled":false,"has_secret":true,"create_time":"2024-03-15T10:30:00Z"}}}},"400":{"description":"Invalid webhook configuration"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or webhook belongs to different partner"},"404":{"description":"Webhook not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Partner Webhooks"],"summary":"Delete webhook","description":"Delete a webhook configuration.","operationId":"delete_webhook_api_v1_partners__partner_id__webhooks__webhook_id__delete","security":[{"HTTPBasic":[]},{"PartnerAuth":[]}],"parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"string","description":"Partner ID","title":"Partner Id"},"description":"Partner ID"},{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","description":"Webhook ID (32-character hex string)","title":"Webhook Id"},"description":"Webhook ID (32-character hex string)"}],"responses":{"204":{"description":"Webhook deleted successfully"},"401":{"description":"Invalid partner authentication"},"403":{"description":"Partner ID mismatch or webhook belongs to different partner"},"404":{"description":"Webhook not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"webhooks":{"results_ready":{"post":{"summary":"Webhook Results Ready","description":"When a user's scan results are ready to pull, BodySpec sends a POST request with\nthis payload to your registered webhook URL. The event fires once the underlying\nanalysis has landed in BodySpec's database, so the results are queryable by the time\nyou receive it.\n\nDelivery is at-least-once: if a scan is re-analyzed, another results_ready is sent\nfor the same result. Treat repeats as expected and deduplicate on `data.result_id`.\n`event_id` is unique per delivery and differs across repeats, so do not dedupe on it.","operationId":"webhook_results_readyresults_ready_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResultsReadyPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"reservation_created":{"post":{"summary":"Webhook Reservation Created","description":"When a new reservation is created for a user, BodySpec sends a POST request\nwith this payload to your registered webhook URL.","operationId":"webhook_reservation_createdreservation_created_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReservationCreatedPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"reservation_canceled":{"post":{"summary":"Webhook Reservation Canceled","description":"When a reservation is cancelled, BodySpec sends a POST request with this\npayload to your registered webhook URL. The `canceled_by` field identifies\nwho initiated the cancellation.","operationId":"webhook_reservation_canceledreservation_canceled_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReservationCanceledPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"reservation_no_show":{"post":{"summary":"Webhook Reservation No Show","description":"When BodySpec determines that a reserved appointment was a no-show, it sends a POST\nrequest with this payload to your registered webhook URL. BodySpec does not record\nattendance, so this is inferred and deliberately conservative: it fires only once the\nappointment time has passed with no published results, the location has finished its\nday, and a later appointment at the same location that day produced results. An\nappointment that never meets that bar stays `pending_scan` and no event is sent. If\nresults are published later, a `results_ready` event follows and supersedes this one.","operationId":"webhook_reservation_no_showreservation_no_show_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookReservationNoShowPayload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Address":{"properties":{"address_line1":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line1","description":"Primary address line"},"address_line2":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address Line2","description":"Secondary address line (e.g., suite number)"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City","description":"City name"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State","description":"State or province"},"postal_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Postal Code","description":"Postal/ZIP code"},"country":{"type":"string","title":"Country","description":"Two-letter ISO country code","default":"US"}},"type":"object","title":"Address","description":"Address information for a location.","x-internal":true},"Appointment":{"properties":{"appt_id":{"type":"string","title":"Appt Id","description":"Unique identifier for the appointment"},"start_time":{"type":"string","format":"date-time","title":"Start Time","description":"Appointment start time in location timezone"},"duration_minutes":{"type":"integer","title":"Duration Minutes","description":"Duration of the appointment in minutes"},"location":{"$ref":"#/components/schemas/Location","description":"Location details"},"service":{"$ref":"#/components/schemas/Service","description":"Service information"},"status":{"$ref":"#/components/schemas/ApptStatus","description":"Appointment outcome. `scheduled` — upcoming, results not expected yet. `pending_scan` — the appointment time has passed but results are not published yet, or no other scan at that location later that day produced results either (which reads as a site problem rather than a missed appointment). `completed` — results are published and retrievable. `no_show` — the appointment time has passed with no results, and a later scan at the same location that day did produce results, so the location was operating normally. Note that `pending_scan` can be a lasting state: BodySpec does not record attendance, so an appointment is only reported as `no_show` when there is positive evidence the location was operating. When BodySpec makes the `no_show` determination for a partner appointment, a `reservation_no_show` webhook is emitted."},"reserve_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reserve Time","description":"UTC timestamp when the appointment was reserved, if applicable"}},"type":"object","required":["appt_id","start_time","duration_minutes","location","service","status"],"title":"Appointment","description":"Appointment information."},"ApptListResponse":{"properties":{"appts":{"items":{"$ref":"#/components/schemas/Appointment"},"type":"array","title":"Appts","description":"List of appointments"},"pagination":{"$ref":"#/components/schemas/Pagination","description":"Pagination information"}},"type":"object","required":["appts","pagination"],"title":"ApptListResponse","description":"Paginated appointment list response.","x-internal":true},"ApptStatus":{"type":"string","enum":["scheduled","pending_scan","completed","no_show"],"title":"ApptStatus","description":"Derived appointment outcome.\n\nBodySpec does not record attendance, so the outcome is inferred from whether\npublished results exist and from what happened at the same location that day.","x-internal":true},"AvailabilityResponse":{"properties":{"slots":{"items":{"$ref":"#/components/schemas/AvailabilitySlot"},"type":"array","title":"Slots","description":"List of available appointment slots"},"pagination":{"$ref":"#/components/schemas/Pagination"},"max_availability_days":{"type":"integer","title":"Max Availability Days","description":"Maximum number of days in the future availability can be queried"}},"type":"object","required":["slots","pagination","max_availability_days"],"title":"AvailabilityResponse","description":"Response model for availability endpoint.","x-internal":true},"AvailabilitySlot":{"properties":{"appt_id":{"type":"string","title":"Appt Id","description":"Unique identifier for the appointment slot"},"location_id":{"type":"string","title":"Location Id","description":"Location identifier where the appointment is available"},"service_id":{"type":"string","title":"Service Id","description":"Service/product identifier for this appointment"},"start_time":{"type":"string","format":"date-time","title":"Start Time","description":"Appointment start time in location's local timezone (ISO 8601 with offset)"},"duration_minutes":{"type":"integer","title":"Duration Minutes","description":"Duration of the appointment in minutes"},"timezone":{"type":"string","title":"Timezone","description":"IANA timezone identifier for the location (e.g., 'America/Los_Angeles')"},"is_available":{"type":"boolean","title":"Is Available","description":"Whether this slot is currently available for booking","default":true}},"type":"object","required":["appt_id","location_id","service_id","start_time","duration_minutes","timezone"],"title":"AvailabilitySlot","description":"Represents an available appointment slot."},"BodyRegion":{"properties":{"fat_mass_kg":{"type":"number","title":"Fat Mass Kg","description":"Fat mass in kilograms"},"lean_mass_kg":{"type":"number","title":"Lean Mass Kg","description":"Lean mass in kilograms"},"bone_mass_kg":{"type":"number","title":"Bone Mass Kg","description":"Bone mass in kilograms"},"total_mass_kg":{"type":"number","title":"Total Mass Kg","description":"Total mass in kilograms"},"tissue_fat_pct":{"type":"number","title":"Tissue Fat Pct","description":"Fat percentage of soft tissue (excludes bone)"},"region_fat_pct":{"type":"number","title":"Region Fat Pct","description":"Fat percentage of total region (includes bone)"}},"type":"object","required":["fat_mass_kg","lean_mass_kg","bone_mass_kg","total_mass_kg","tissue_fat_pct","region_fat_pct"],"title":"BodyRegion","description":"Body composition data for a specific region.","x-internal":true},"Body__create_order_api_v1_partners__partner_id__orders_post":{"properties":{"user_id":{"type":"string","minLength":1,"title":"User Id","description":"BodySpec user ID"},"provider_name":{"type":"string","minLength":1,"title":"Provider Name","description":"Ordering provider name"},"provider_npi":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider Npi","description":"Provider NPI number (10 digits)"},"order_date":{"type":"string","title":"Order Date","description":"Date order was written (YYYY-MM-DD)"},"expiration_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiration Date","description":"Order expiration date (YYYY-MM-DD)"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes","description":"Additional notes"},"first_name":{"type":"string","minLength":1,"title":"First Name","description":"Patient first name"},"last_name":{"type":"string","minLength":1,"title":"Last Name","description":"Patient last name"},"birth_date":{"type":"string","title":"Birth Date","description":"Patient birth date (YYYY-MM-DD)"},"gender":{"type":"string","title":"Gender","description":"Patient sex for the scan (Male, Female)"},"height_inches":{"type":"integer","maximum":96.0,"minimum":30.0,"title":"Height Inches","description":"Height in inches (30-96)"},"weight_pounds":{"type":"integer","maximum":600.0,"minimum":30.0,"title":"Weight Pounds","description":"Weight in pounds (30-600)"},"ethnicity":{"type":"string","title":"Ethnicity","description":"Ethnicity (Asian, Black, Hispanic, White, Other)"},"neg_q1_pregnant":{"type":"boolean","title":"Neg Q1 Pregnant","description":"Pregnant or possibly pregnant (optional, default false; true is rejected)","default":false},"neg_q2_barium_xray":{"type":"boolean","title":"Neg Q2 Barium Xray","description":"Barium X-ray in last 2 weeks (optional, default false; true is rejected)","default":false},"neg_q3_nuclear":{"type":"boolean","title":"Neg Q3 Nuclear","description":"Nuclear medicine test in last 2 weeks (optional, default false; true is rejected)","default":false},"file":{"type":"string","format":"binary","title":"File","description":"PDF file of the medical order"}},"type":"object","required":["user_id","provider_name","order_date","first_name","last_name","birth_date","gender","height_inches","weight_pounds","ethnicity","file"],"title":"Body__create_order_api_v1_partners__partner_id__orders_post","x-internal":true},"Body__update_order_api_v1_partners__partner_id__orders__order_id__patch":{"properties":{"provider_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider Name","description":"Ordering provider name"},"provider_npi":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider Npi","description":"Provider NPI number (10 digits)"},"order_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order Date","description":"Date order was written (YYYY-MM-DD)"},"expiration_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiration Date","description":"Order expiration date (YYYY-MM-DD)"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes","description":"Additional notes"},"file":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"File","description":"Replacement PDF file"}},"type":"object","title":"Body__update_order_api_v1_partners__partner_id__orders__order_id__patch","x-internal":true},"BoneDensity":{"properties":{"bone_mineral_density":{"type":"number","title":"Bone Mineral Density","description":"Bone Mineral Density (g/cm²)"},"bone_area_cm2":{"type":"number","title":"Bone Area Cm2","description":"Bone area in cm²"},"bone_mineral_content_g":{"type":"number","title":"Bone Mineral Content G","description":"Bone mineral content in grams"},"age_sex_z_percentile":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Age Sex Z Percentile","description":"Age and sex-matched percentile (1-99)"},"peak_sex_t_percentile":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Peak Sex T Percentile","description":"Sex-matched percentile compared to 30-year-olds (1-99)"}},"type":"object","required":["bone_mineral_density","bone_area_cm2","bone_mineral_content_g"],"title":"BoneDensity","description":"Bone density measurements for a specific site.","x-internal":true},"ClassicPdfFormatRequest":{"properties":{"format":{"type":"string","const":"classic_pdf","title":"Format"}},"type":"object","required":["format"],"title":"ClassicPdfFormatRequest","description":"Request body for classic_pdf export format.","examples":[{"format":"classic_pdf"}],"x-internal":true},"Coordinates":{"properties":{"latitude":{"type":"number","title":"Latitude","description":"Latitude in decimal degrees"},"longitude":{"type":"number","title":"Longitude","description":"Longitude in decimal degrees"}},"type":"object","required":["latitude","longitude"],"title":"Coordinates","description":"Geographic coordinates for a location.","x-internal":true},"DexaBoneDensityResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"section_name":{"type":"string","title":"Section Name","description":"Name of this section","default":"bone-density"},"total":{"$ref":"#/components/schemas/BoneDensity","description":"Total body bone density"},"regions":{"additionalProperties":{"$ref":"#/components/schemas/BoneDensity"},"type":"object","title":"Regions","description":"Regional bone density data","examples":[{"left_arm":{"bone_area_cm2":170.17,"bone_mineral_content_g":170.9,"bone_mineral_density":1.004},"left_leg":{"bone_area_cm2":376.24,"bone_mineral_content_g":542.5,"bone_mineral_density":1.442},"right_arm":{"bone_area_cm2":180.66,"bone_mineral_content_g":175.5,"bone_mineral_density":0.971},"right_leg":{"bone_area_cm2":376.32,"bone_mineral_content_g":533.4,"bone_mineral_density":1.417},"trunk":{"bone_area_cm2":782.47,"bone_mineral_content_g":1005.8,"bone_mineral_density":1.285}}]}},"type":"object","required":["result_id","total","regions"],"title":"DexaBoneDensity","description":"DEXA bone density results by region."},"DexaCompositionResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"section_name":{"type":"string","title":"Section Name","description":"Name of this section","default":"composition"},"total":{"$ref":"#/components/schemas/BodyRegion","description":"Total body composition"},"regions":{"additionalProperties":{"$ref":"#/components/schemas/BodyRegion"},"type":"object","title":"Regions","description":"Regional composition data including standard regions and special regions","examples":[{"android":{"bone_mass_kg":0.04,"fat_mass_kg":1.07,"lean_mass_kg":2.42,"region_fat_pct":30.31,"tissue_fat_pct":30.67,"total_mass_kg":3.53},"gynoid":{"bone_mass_kg":0.2,"fat_mass_kg":3.29,"lean_mass_kg":6.39,"region_fat_pct":33.3,"tissue_fat_pct":33.98,"total_mass_kg":9.88},"left_arm":{"bone_mass_kg":0.13,"fat_mass_kg":1.14,"lean_mass_kg":2.04,"region_fat_pct":34.5,"tissue_fat_pct":35.92,"total_mass_kg":3.31},"left_leg":{"bone_mass_kg":0.4,"fat_mass_kg":3.27,"lean_mass_kg":7.0,"region_fat_pct":30.64,"tissue_fat_pct":31.83,"total_mass_kg":10.67},"right_arm":{"bone_mass_kg":0.13,"fat_mass_kg":1.11,"lean_mass_kg":2.18,"region_fat_pct":32.48,"tissue_fat_pct":33.79,"total_mass_kg":3.43},"right_leg":{"bone_mass_kg":0.38,"fat_mass_kg":3.03,"lean_mass_kg":7.11,"region_fat_pct":28.81,"tissue_fat_pct":29.89,"total_mass_kg":10.52},"trunk":{"bone_mass_kg":0.57,"fat_mass_kg":7.22,"lean_mass_kg":17.32,"region_fat_pct":28.75,"tissue_fat_pct":29.42,"total_mass_kg":25.11}}]},"android_gynoid_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Android Gynoid Ratio","description":"Android/Gynoid fat ratio"}},"type":"object","required":["result_id","total","regions"],"title":"DexaComposition","description":"DEXA body composition results by region."},"DexaPercentilesResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"section_name":{"type":"string","title":"Section Name","description":"Name of this section","default":"percentiles"},"params":{"$ref":"#/components/schemas/PercentileParams","description":"Parameters used for percentile calculations"},"metrics":{"additionalProperties":{"$ref":"#/components/schemas/PercentileMetric"},"type":"object","title":"Metrics","description":"Percentile data for various metrics","examples":[{"bone_density_g_cm2":{"percentile":82,"value":1.25},"limb_lmi_kg_m2":{"percentile":85,"value":8.5},"total_body_fat_pct":{"percentile":45,"value":25.5},"total_lmi_kg_m2":{"percentile":68,"value":22.5},"vat_mass_kg":{"percentile":72,"value":2.8}}]}},"type":"object","required":["result_id","params","metrics"],"title":"DexaPercentiles","description":"Age and gender-matched percentile rankings."},"DexaRmrResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"section_name":{"type":"string","title":"Section Name","description":"Name of this section","default":"rmr"},"estimates":{"items":{"$ref":"#/components/schemas/RmrEstimate"},"type":"array","title":"Estimates","description":"RMR estimates calculated using different scientific formulas","examples":[[{"formula":"ten Haaf (2014)","kcal_per_day":1850},{"formula":"Cunningham (1980)","kcal_per_day":1798},{"formula":"De Lorenzo (1999)","kcal_per_day":1920},{"formula":"Mifflin-St. Jeor (1990)","kcal_per_day":1780}]]}},"type":"object","required":["result_id","estimates"],"title":"DexaRmr","description":"Resting metabolic rate (RMR) estimates derived from DEXA body composition."},"DexaScanInfoResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"section_name":{"type":"string","title":"Section Name","description":"Name of this section","default":"scan-info"},"scanner_model":{"type":"string","title":"Scanner Model","description":"Model of the DEXA scanner (e.g., 'GE Lunar iDXA', 'GE Lunar Prodigy')"},"acquire_time":{"type":"string","format":"date-time","title":"Acquire Time","description":"When the scan was acquired in location timezone"},"analyze_time":{"type":"string","format":"date-time","title":"Analyze Time","description":"When the scan was analyzed in location timezone"},"patient_intake":{"$ref":"#/components/schemas/PatientInfo","description":"Patient information at time of scan (from intake)"}},"type":"object","required":["result_id","scanner_model","acquire_time","analyze_time","patient_intake"],"title":"DexaScanInfo","description":"DEXA scan metadata and information."},"DexaVisceralFatResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"section_name":{"type":"string","title":"Section Name","description":"Name of this section","default":"visceral-fat"},"vat_mass_kg":{"type":"number","title":"Vat Mass Kg","description":"Visceral adipose tissue mass in kilograms"},"vat_volume_cm3":{"type":"number","title":"Vat Volume Cm3","description":"Visceral adipose tissue volume in cubic centimeters"}},"type":"object","required":["result_id","vat_mass_kg","vat_volume_cm3"],"title":"DexaVisceralFat","description":"Visceral adipose tissue (VAT) analysis results."},"ExternalIds":{"properties":{"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"Partner's external user ID"},"appt_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Appt Id","description":"Partner's external appointment/reservation ID"}},"type":"object","title":"ExternalIds","description":"Optional external IDs for partner tracking.","examples":[{"appt_id":"partner_booking_123","user_id":"partner_user_456"}],"x-internal":true},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError","x-internal":true},"IntakeAdditionalContact":{"properties":{"name":{"type":"string","minLength":1,"title":"Name","description":"Contact name"},"relationship":{"type":"string","minLength":1,"title":"Relationship","description":"Relationship to user"},"email":{"type":"string","format":"email","title":"Email","description":"Contact email"},"phone_number":{"type":"string","title":"Phone Number","description":"Contact phone number"}},"type":"object","required":["name","relationship","email","phone_number"],"title":"IntakeAdditionalContact","description":"Optional additional contact information.","x-internal":true},"IntakeCreateRequest":{"properties":{"user":{"$ref":"#/components/schemas/IntakeUser","description":"User information"},"qualifiers":{"$ref":"#/components/schemas/IntakeQualifiers","description":"Qualification questions"},"disqualifiers":{"$ref":"#/components/schemas/IntakeDisqualifiers","description":"Disqualification questions"},"addl_contact":{"anyOf":[{"$ref":"#/components/schemas/IntakeAdditionalContact"},{"type":"null"}],"description":"Optional additional contact"}},"type":"object","required":["user","qualifiers","disqualifiers"],"title":"IntakeCreateRequest","description":"Request to create an intake for a user.","examples":[{"disqualifiers":{"neg_q1_pregnant":false,"neg_q2_barium_xray":false,"neg_q3_nuclear":false},"qualifiers":{"qual_q1_weight":true,"qual_q2_body_fat":false,"qual_q3_athlete":false,"qual_q4_heart_disease":false,"qual_q5_high_blood_pressure":false,"qual_q6_injuries":false,"qual_q7_research_study":false},"user":{"birth_date":"1990-05-15","email":"jane@example.com","ethnicity":"White","first_name":"Jane","gender":"Female","height_inches":65,"last_name":"Smith","phone_number":"+14155551234","weight_pounds":140}}],"x-internal":true},"IntakeDisqualifiers":{"properties":{"neg_q1_pregnant":{"type":"boolean","title":"Neg Q1 Pregnant","description":"Pregnant or possibly pregnant","default":false},"neg_q2_barium_xray":{"type":"boolean","title":"Neg Q2 Barium Xray","description":"Barium X-ray in last 2 weeks","default":false},"neg_q3_nuclear":{"type":"boolean","title":"Neg Q3 Nuclear","description":"Nuclear medicine test in last 2 weeks","default":false}},"type":"object","title":"IntakeDisqualifiers","description":"Disqualifier questions - all must be false.","x-internal":true},"IntakeQualifiers":{"properties":{"qual_q1_weight":{"type":"boolean","title":"Qual Q1 Weight","description":"Weight management qualification","default":false},"qual_q2_body_fat":{"type":"boolean","title":"Qual Q2 Body Fat","description":"Body fat tracking qualification","default":false},"qual_q3_athlete":{"type":"boolean","title":"Qual Q3 Athlete","description":"Athletic performance qualification","default":false},"qual_q4_heart_disease":{"type":"boolean","title":"Qual Q4 Heart Disease","description":"Heart disease risk qualification","default":false},"qual_q5_high_blood_pressure":{"type":"boolean","title":"Qual Q5 High Blood Pressure","description":"High blood pressure qualification","default":false},"qual_q6_injuries":{"type":"boolean","title":"Qual Q6 Injuries","description":"Injury recovery qualification","default":false},"qual_q7_research_study":{"type":"boolean","title":"Qual Q7 Research Study","description":"Research study qualification","default":false}},"type":"object","title":"IntakeQualifiers","description":"Qualifier questions - at least one must be true.","x-internal":true},"IntakeResponse":{"properties":{"intake_id":{"type":"string","title":"Intake Id","description":"Created intake ID"}},"type":"object","required":["intake_id"],"title":"Intake","description":"Response from intake creation."},"IntakeUser":{"properties":{"first_name":{"type":"string","minLength":1,"title":"First Name","description":"User's first name"},"last_name":{"type":"string","minLength":1,"title":"Last Name","description":"User's last name"},"email":{"type":"string","format":"email","title":"Email","description":"User's email address"},"phone_number":{"type":"string","title":"Phone Number","description":"Phone number"},"birth_date":{"type":"string","title":"Birth Date","description":"Birth date in ISO format (YYYY-MM-DD)"},"gender":{"type":"string","title":"Gender","description":"Gender (Male, Female)"},"height_inches":{"type":"integer","maximum":96.0,"minimum":30.0,"title":"Height Inches","description":"Height in inches (30-96)"},"weight_pounds":{"type":"integer","maximum":600.0,"minimum":30.0,"title":"Weight Pounds","description":"Weight in pounds (30-600)"},"ethnicity":{"type":"string","title":"Ethnicity","description":"Ethnicity (Asian, Black, Hispanic, White, Other)"},"signature":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signature","description":"PNG signature image, max 500KB. Provide as raw base64 or data URL (data:image/png;base64,...)."}},"type":"object","required":["first_name","last_name","email","phone_number","birth_date","gender","height_inches","weight_pounds","ethnicity"],"title":"IntakeUser","description":"User information for intake.","x-internal":true},"InteractiveFormatRequest":{"properties":{"min_age":{"anyOf":[{"type":"integer","maximum":75.0,"minimum":20.0},{"type":"null"}],"title":"Min Age","description":"Minimum age for percentile comparison range. Defaults to patient age minus 3, clamped to 20."},"max_age":{"anyOf":[{"type":"integer","maximum":80.0,"minimum":20.0},{"type":"null"}],"title":"Max Age","description":"Maximum age for percentile comparison range. Defaults to patient age plus 3, clamped to 80."},"gender":{"anyOf":[{"type":"string","enum":["Male","Female"]},{"type":"null"}],"title":"Gender","description":"Patient gender for percentile comparison. Auto-detected from scan data if omitted."},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle","description":"Display name on the report. Defaults to the user's first name if omitted."},"appts":{"anyOf":[{"items":{"type":"string","pattern":"^[0-9a-f]{32}$"},"type":"array"},{"type":"null"}],"title":"Appts","description":"Appointment IDs to include as historical comparisons. Each ID must be a 32-character hex string. The current result is always included automatically. Defaults to all of the user's DEXA scans if omitted."},"format":{"type":"string","const":"bodyspec_interactive","title":"Format"}},"type":"object","required":["format"],"title":"InteractiveFormatRequest","description":"Request body for bodyspec_interactive export format.","examples":[{"format":"bodyspec_interactive"}],"x-internal":true},"Location":{"properties":{"location_id":{"type":"string","title":"Location Id","description":"Unique identifier for the location"},"name":{"type":"string","title":"Name","description":"Location name"},"location_type":{"type":"string","title":"Location Type","description":"Type of location: 'mobile' or 'storefront'"},"address":{"$ref":"#/components/schemas/Address","description":"Address information"},"coordinates":{"anyOf":[{"$ref":"#/components/schemas/Coordinates"},{"type":"null"}],"description":"Geographic coordinates"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone","description":"IANA timezone identifier (e.g., 'America/Los_Angeles')"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Public notes or description"},"last_updated":{"type":"string","format":"date-time","title":"Last Updated","description":"Last update timestamp in ISO 8601 UTC format"},"distance_miles":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Distance Miles","description":"Distance from search coordinates in miles. Only present when lat/lng search parameters are provided."}},"type":"object","required":["location_id","name","location_type","address","last_updated"],"title":"Location","description":"Location model representing a scan location with full details."},"LocationResponse":{"properties":{"location":{"$ref":"#/components/schemas/Location"}},"type":"object","required":["location"],"title":"LocationResponse","description":"Response model for getting a single location.","x-internal":true},"LocationsListResponse":{"properties":{"locations":{"items":{"$ref":"#/components/schemas/Location"},"type":"array","title":"Locations"},"pagination":{"$ref":"#/components/schemas/Pagination"}},"type":"object","required":["locations","pagination"],"title":"LocationsListResponse","description":"Response model for listing locations.","x-internal":true},"MultiLocationAvailabilityResponse":{"properties":{"slots":{"items":{"$ref":"#/components/schemas/AvailabilitySlot"},"type":"array","title":"Slots","description":"List of available appointment slots"},"pagination":{"$ref":"#/components/schemas/Pagination"},"max_availability_days":{"type":"integer","title":"Max Availability Days","description":"Maximum number of days in the future availability can be queried"},"skipped_location_ids":{"items":{"type":"string"},"type":"array","title":"Skipped Location Ids","description":"Location IDs that were skipped (not found or private)"}},"type":"object","required":["slots","pagination","max_availability_days"],"title":"MultiLocationAvailabilityResponse","description":"Response model for multi-location availability endpoint.","x-internal":true},"OrderCreateResponse":{"properties":{"order_id":{"type":"string","title":"Order Id","description":"Created order ID"}},"type":"object","required":["order_id"],"title":"OrderCreateResponse","description":"Response from order creation."},"OrderResponse":{"properties":{"order_id":{"type":"string","title":"Order Id","description":"Order ID"},"user_id":{"type":"string","title":"User Id","description":"User ID"},"intake_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Intake Id","description":"Intake created from the order's demographics"},"provider_name":{"type":"string","title":"Provider Name","description":"Ordering provider name"},"provider_npi":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider Npi","description":"Provider NPI number"},"order_date":{"type":"string","title":"Order Date","description":"Date order was written (YYYY-MM-DD)"},"expiration_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiration Date","description":"Order expiration date (YYYY-MM-DD)"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Additional notes"},"status":{"type":"string","title":"Status","description":"Order status: active, or revoked once BodySpec has voided the order"},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Creation timestamp (ISO 8601 UTC)"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"Last update timestamp (ISO 8601 UTC)"}},"type":"object","required":["order_id","user_id","provider_name","order_date","status","created_at","updated_at"],"title":"OrderResponse","description":"Response for a single order."},"OrdersListResponse":{"properties":{"orders":{"items":{"$ref":"#/components/schemas/OrderResponse"},"type":"array","title":"Orders","description":"List of orders"},"pagination":{"$ref":"#/components/schemas/Pagination","description":"Pagination info"}},"type":"object","required":["orders","pagination"],"title":"OrdersListResponse","description":"Paginated list of orders.","x-internal":true},"Pagination":{"properties":{"page":{"type":"integer","title":"Page","description":"Current page number"},"page_size":{"type":"integer","title":"Page Size","description":"Number of items per page"},"results":{"type":"integer","title":"Results","description":"Number of results in this page"},"has_more":{"type":"boolean","title":"Has More","description":"Whether more results exist after this page"}},"type":"object","required":["page","page_size","results","has_more"],"title":"Pagination","description":"Pagination information for list responses.","x-internal":true},"PartnerReservation":{"properties":{"user":{"$ref":"#/components/schemas/UserResponse","description":"User information"},"appt":{"$ref":"#/components/schemas/Appointment","description":"Appointment details"}},"type":"object","required":["user","appt"],"title":"PartnerReservation","description":"Combined user and appointment for a partner reservation."},"PartnerReservationsListResponse":{"properties":{"reservations":{"items":{"$ref":"#/components/schemas/PartnerReservation"},"type":"array","title":"Reservations","description":"List of reservations with user and appointment data"},"pagination":{"$ref":"#/components/schemas/Pagination","description":"Pagination information"}},"type":"object","required":["reservations","pagination"],"title":"PartnerReservationsListResponse","description":"Paginated partner reservations list response.","x-internal":true},"PartnerUser":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"User email address"},"first_name":{"type":"string","title":"First Name","description":"User first name"},"last_name":{"type":"string","title":"Last Name","description":"User last name"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Phone number in E.164 format"}},"type":"object","required":["email","first_name","last_name"],"title":"PartnerUser","description":"User information provided by integration partner.","examples":[{"email":"user@example.com","first_name":"Jane","last_name":"Smith","phone":"+14155551234"}],"x-internal":true},"PartnerUserResponse":{"properties":{"user_id":{"type":"string","title":"User Id","description":"Unique identifier for the user"},"email":{"type":"string","format":"email","title":"Email","description":"User's email address"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"User's first name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"User's last name"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Phone number in E.164 format","examples":["+14155551234"]},"link_time":{"type":"string","format":"date-time","title":"Link Time","description":"UTC timestamp when user was linked to partner"}},"type":"object","required":["user_id","email","link_time"],"title":"PartnerUser","description":"User model for partner API responses, includes link_time."},"PartnerUsersListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/PartnerUserResponse"},"type":"array","title":"Users","description":"List of users linked to the partner"},"pagination":{"$ref":"#/components/schemas/Pagination","description":"Pagination information"}},"type":"object","required":["users","pagination"],"title":"PartnerUsersListResponse","description":"Paginated partner users list response.","x-internal":true},"PatientInfo":{"properties":{"age_years":{"type":"number","title":"Age Years","description":"Patient's age at time of scan with one decimal precision"},"height_cm":{"type":"number","title":"Height Cm","description":"Patient's height in centimeters"},"weight_kg":{"type":"number","title":"Weight Kg","description":"Patient's weight in kilograms"}},"type":"object","required":["age_years","height_cm","weight_kg"],"title":"PatientInfo","description":"Patient information at time of scan.","x-internal":true},"PdfFormatRequest":{"properties":{"min_age":{"anyOf":[{"type":"integer","maximum":75.0,"minimum":20.0},{"type":"null"}],"title":"Min Age","description":"Minimum age for percentile comparison range. Defaults to patient age minus 3, clamped to 20."},"max_age":{"anyOf":[{"type":"integer","maximum":80.0,"minimum":20.0},{"type":"null"}],"title":"Max Age","description":"Maximum age for percentile comparison range. Defaults to patient age plus 3, clamped to 80."},"gender":{"anyOf":[{"type":"string","enum":["Male","Female"]},{"type":"null"}],"title":"Gender","description":"Patient gender for percentile comparison. Auto-detected from scan data if omitted."},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle","description":"Display name on the report. Defaults to the user's first name if omitted."},"appts":{"anyOf":[{"items":{"type":"string","pattern":"^[0-9a-f]{32}$"},"type":"array"},{"type":"null"}],"title":"Appts","description":"Appointment IDs to include as historical comparisons. Each ID must be a 32-character hex string. The current result is always included automatically. Defaults to all of the user's DEXA scans if omitted."},"format":{"type":"string","const":"bodyspec_pdf","title":"Format"}},"type":"object","required":["format"],"title":"PdfFormatRequest","description":"Request body for bodyspec_pdf export format.","examples":[{"format":"bodyspec_pdf"}],"x-internal":true},"PercentileMetric":{"properties":{"value":{"type":"number","title":"Value","description":"The measured value"},"percentile":{"type":"integer","title":"Percentile","description":"Percentile rank (1-99)"}},"type":"object","required":["value","percentile"],"title":"PercentileMetric","description":"Individual percentile metric data.","x-internal":true},"PercentileParams":{"properties":{"gender":{"type":"string","title":"Gender","description":"Gender used for comparison (male/female)"},"reference_age_range":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reference Age Range","description":"Age range used for comparison","examples":[{"max_years":45,"min_years":35}]},"reference_dataset_size":{"type":"integer","title":"Reference Dataset Size","description":"Population size of reference dataset (rounded to nearest thousand)"}},"type":"object","required":["gender","reference_age_range","reference_dataset_size"],"title":"PercentileParams","description":"Parameters used for percentile calculation.","x-internal":true},"ReservationCancelRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Cancellation reason"}},"type":"object","title":"ReservationCancelRequest","description":"Request to cancel a reservation (optional body).","examples":[{"reason":"User requested cancellation"}],"x-internal":true},"ReservationCreateRequest":{"properties":{"appt_id":{"type":"string","title":"Appt Id","description":"BodySpec appointment ID"},"user":{"$ref":"#/components/schemas/PartnerUser","description":"User information"},"external":{"anyOf":[{"$ref":"#/components/schemas/ExternalIds"},{"type":"null"}],"description":"Optional external IDs for partner tracking"}},"type":"object","required":["appt_id","user"],"title":"ReservationCreateRequest","description":"Request to create an appointment reservation.","examples":[{"appt_id":"12345","external":{"appt_id":"partner_booking_123","user_id":"partner_user_456"},"user":{"email":"user@example.com","first_name":"Jane","last_name":"Smith"}}],"x-internal":true},"ReservationResponse":{"properties":{"appt_id":{"type":"string","title":"Appt Id","description":"BodySpec appointment ID"},"start_time":{"type":"string","format":"date-time","title":"Start Time","description":"Appointment start time in location timezone"},"location":{"$ref":"#/components/schemas/Location","description":"Location details for the appointment"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"BodySpec user ID"},"external":{"anyOf":[{"$ref":"#/components/schemas/ExternalIds"},{"type":"null"}],"description":"External IDs (echoed back if provided)"},"reserve_time":{"type":"string","format":"date-time","title":"Reserve Time","description":"UTC timestamp when reservation was created"}},"type":"object","required":["appt_id","start_time","location","reserve_time"],"title":"Reservation","description":"Response for reservation creation."},"ResultDetailResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"start_time":{"type":"string","format":"date-time","title":"Start Time","description":"Result date/time in location timezone"},"location":{"$ref":"#/components/schemas/Location","description":"Location where the result was obtained"},"service":{"$ref":"#/components/schemas/Service","description":"Service information"},"create_time":{"type":"string","format":"date-time","title":"Create Time","description":"When the result was created in location timezone"},"update_time":{"type":"string","format":"date-time","title":"Update Time","description":"When the result was last updated in location timezone"},"sections":{"items":{"type":"string"},"type":"array","title":"Sections","description":"List of available section names (only present in detail response, not in list response)"}},"type":"object","required":["result_id","start_time","location","service","create_time","update_time","sections"],"title":"Result","description":"Response for getting result details."},"ResultExportResponse":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"format":{"$ref":"#/components/schemas/ResultFormatType","description":"The export format type"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url","description":"URL to access this export (null when unavailable)"},"status":{"type":"string","title":"Status","description":"Availability status: 'available', 'unavailable', or 'pending'"}},"type":"object","required":["result_id","format","status"],"title":"ResultExportResponse","description":"Response for result export endpoint."},"ResultFormatType":{"type":"string","enum":["bodyspec_interactive","bodyspec_pdf","classic_pdf"],"title":"ResultFormatType","description":"Available result output format types.","x-internal":true},"ResultSummary":{"properties":{"result_id":{"type":"string","title":"Result Id","description":"Unique identifier for the result"},"start_time":{"type":"string","format":"date-time","title":"Start Time","description":"Result date/time in location timezone"},"location":{"$ref":"#/components/schemas/Location","description":"Location where the result was obtained"},"service":{"$ref":"#/components/schemas/Service","description":"Service information"},"create_time":{"type":"string","format":"date-time","title":"Create Time","description":"When the result was created in location timezone"},"update_time":{"type":"string","format":"date-time","title":"Update Time","description":"When the result was last updated in location timezone"}},"type":"object","required":["result_id","start_time","location","service","create_time","update_time"],"title":"ResultSummary","description":"Summary information for a single result.","x-internal":true},"ResultsListResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/ResultSummary"},"type":"array","title":"Results","description":"List of result summaries"},"pagination":{"$ref":"#/components/schemas/Pagination","description":"Pagination information"}},"type":"object","required":["results","pagination"],"title":"ResultsListResponse","description":"Response for listing user results.","x-internal":true},"RmrEstimate":{"properties":{"formula":{"type":"string","title":"Formula","description":"Scientific formula name and publication year"},"kcal_per_day":{"type":"integer","title":"Kcal Per Day","description":"Estimated resting metabolic rate in kilocalories per day"}},"type":"object","required":["formula","kcal_per_day"],"title":"RmrEstimate","description":"Individual RMR estimate from a scientific formula.","x-internal":true},"Service":{"properties":{"name":{"type":"string","title":"Name","description":"Short name of the service (e.g., 'DEXA')"},"description":{"type":"string","title":"Description","description":"Full descriptive name of the service"},"service_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service Id","description":"Unique identifier for the service"},"service_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service Code","description":"Service code (e.g., 'DXA', 'RMR', 'VO2')"},"duration_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Minutes","description":"Typical duration in minutes"},"last_updated":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Updated","description":"Last update timestamp in ISO 8601 UTC format"}},"type":"object","required":["name","description"],"title":"Service","description":"Service model representing a bookable scan or test type."},"ServiceResponse":{"properties":{"service":{"$ref":"#/components/schemas/Service"}},"type":"object","required":["service"],"title":"ServiceResponse","description":"Response model for getting a single service.","x-internal":true},"ServicesListResponse":{"properties":{"services":{"items":{"$ref":"#/components/schemas/Service"},"type":"array","title":"Services"},"pagination":{"$ref":"#/components/schemas/Pagination"}},"type":"object","required":["services","pagination"],"title":"ServicesListResponse","description":"Response model for listing services.","x-internal":true},"SortOrder":{"type":"string","enum":["newest_first","oldest_first"],"title":"SortOrder","description":"Sort order options for appointment lists.","x-internal":true},"UserResponse":{"properties":{"user_id":{"type":"string","title":"User Id","description":"Unique identifier for the user"},"email":{"type":"string","format":"email","title":"Email","description":"User's email address"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"User's first name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"User's last name"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Phone number in E.164 format","examples":["+14155551234"]}},"type":"object","required":["user_id","email"],"title":"User","description":"User model for API responses.\n\nNote: This model intentionally excludes sensitive or internal fields like:\n- is_admin: Admin status is determined from token claims, not exposed in responses\n- created_at/updated_at: Internal timestamps not needed in public API"},"UserUpdateRequest":{"properties":{"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name","description":"User's first name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name","description":"User's last name"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone","description":"Phone number in E.164 format","examples":["+14155551234"]}},"type":"object","title":"UserUpdateRequest","description":"User update request model.","examples":[{"first_name":"Jane","last_name":"Smith","phone":"+14155559999"}],"x-internal":true},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError","x-internal":true},"WebhookCreateRequest":{"properties":{"url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Url","description":"HTTPS URL to receive webhook events"},"event_type":{"$ref":"#/components/schemas/WebhookEventType","description":"Event type to subscribe to"},"secret":{"anyOf":[{"type":"string","maxLength":256,"minLength":8},{"type":"null"}],"title":"Secret","description":"Optional secret for webhook authentication. BodySpec base64-encodes this value and sends it as a Bearer token in the Authorization header of every webhook delivery — i.e. `Authorization: Bearer <base64(secret)>` (standard base64, with padding)."},"enabled":{"type":"boolean","title":"Enabled","description":"Whether the webhook is enabled","default":true}},"type":"object","required":["url","event_type"],"title":"WebhookCreateRequest","description":"Request model for creating a webhook.","examples":[{"enabled":true,"event_type":"results_ready","secret":"my-webhook-secret","url":"https://your-server.example.com/webhooks/bodyspec"}],"x-internal":true},"WebhookEventType":{"type":"string","enum":["results_ready","reservation_created","reservation_canceled","reservation_no_show"],"title":"WebhookEventType","description":"Supported webhook event types.","x-internal":true},"WebhookReservationCanceledData":{"properties":{"user_id":{"type":"string","title":"User Id","description":"BodySpec user ID"},"appt_id":{"type":"string","title":"Appt Id","description":"Appointment ID"},"service_id":{"type":"string","title":"Service Id","description":"Service identifier"},"start_time":{"type":"string","title":"Start Time","description":"Start time of the cancelled appointment, in ISO 8601 format with timezone"},"canceled_by":{"type":"string","enum":["user","bodyspec","partner","other"],"title":"Canceled By","description":"Who initiated the cancellation. 'user' means the customer cancelled it themselves, 'bodyspec' means BodySpec staff cancelled it, 'partner' means it was cancelled through the partner API, and 'other' covers system or automated cancellations."},"cancel_time":{"type":"string","title":"Cancel Time","description":"When the cancellation occurred, in ISO 8601 format with timezone"}},"type":"object","required":["user_id","appt_id","service_id","start_time","canceled_by","cancel_time"],"title":"WebhookReservationCanceledData","description":"Data payload for reservation_canceled webhook event.","x-internal":true},"WebhookReservationCanceledPayload":{"properties":{"event_type":{"type":"string","const":"reservation_canceled","title":"Event Type","description":"Type of event (always 'reservation_canceled' for this payload)"},"event_id":{"type":"string","title":"Event Id","description":"Unique identifier for this event"},"partner_id":{"type":"string","title":"Partner Id","description":"Partner ID this event is for"},"data":{"$ref":"#/components/schemas/WebhookReservationCanceledData","description":"Event data containing user, appointment, and cancellation information"}},"type":"object","required":["event_type","event_id","partner_id","data"],"title":"WebhookReservationCanceledPayload","description":"Webhook payload for reservation_canceled events.\n\nSent when a reservation is cancelled, regardless of who initiated it.","examples":[{"data":{"appt_id":"e8c91d4a2b7f3e6c8d0a5b9f1c3e7d2a","cancel_time":"2024-03-18T09:12:44-07:00","canceled_by":"partner","service_id":"8d0e0a1f561f4ec69dbca50ea14336d","start_time":"2024-03-20T14:00:00-07:00","user_id":"a5950beea2a7487fb79cfdbeb77fa555"},"event_id":"3b7d2e91c4a85f60b2d9e8a1f7c4b3d6","event_type":"reservation_canceled","partner_id":"pk_acme"}],"x-internal":true},"WebhookReservationCreatedData":{"properties":{"user_id":{"type":"string","title":"User Id","description":"BodySpec user ID"},"appt_id":{"type":"string","title":"Appt Id","description":"Appointment ID"},"service_id":{"type":"string","title":"Service Id","description":"Service identifier"},"start_time":{"type":"string","title":"Start Time","description":"Appointment start time in ISO 8601 format with timezone"}},"type":"object","required":["user_id","appt_id","service_id","start_time"],"title":"WebhookReservationCreatedData","description":"Data payload for reservation_created webhook event.","x-internal":true},"WebhookReservationCreatedPayload":{"properties":{"event_type":{"type":"string","const":"reservation_created","title":"Event Type","description":"Type of event (always 'reservation_created' for this payload)"},"event_id":{"type":"string","title":"Event Id","description":"Unique identifier for this event"},"partner_id":{"type":"string","title":"Partner Id","description":"Partner ID this event is for"},"data":{"$ref":"#/components/schemas/WebhookReservationCreatedData","description":"Event data containing user and appointment information"}},"type":"object","required":["event_type","event_id","partner_id","data"],"title":"WebhookReservationCreatedPayload","description":"Webhook payload for reservation_created events.\n\nSent when a new reservation is created for a user.","examples":[{"data":{"appt_id":"e8c91d4a2b7f3e6c8d0a5b9f1c3e7d2a","service_id":"8d0e0a1f561f4ec69dbca50ea14336d","start_time":"2024-03-20T14:00:00-07:00","user_id":"a5950beea2a7487fb79cfdbeb77fa555"},"event_id":"7f3a9c12b8e64d21f9a8b7c6d5e4f3a2","event_type":"reservation_created","partner_id":"pk_acme"}],"x-internal":true},"WebhookReservationNoShowData":{"properties":{"user_id":{"type":"string","title":"User Id","description":"BodySpec user ID"},"appt_id":{"type":"string","title":"Appt Id","description":"Appointment ID"},"service_id":{"type":"string","title":"Service Id","description":"Service identifier"},"start_time":{"type":"string","title":"Start Time","description":"Scheduled start time of the missed appointment, in ISO 8601 format with timezone"},"no_show_time":{"type":"string","title":"No Show Time","description":"When BodySpec concluded the appointment was a no-show, in ISO 8601 format with timezone. BodySpec does not record attendance, so this is inferred: the appointment time passed with no published results, the location finished its day, and a later appointment at the same location that day did produce results (evidence the location was operating). If results for this appointment are published later, a `results_ready` event follows and supersedes this one."}},"type":"object","required":["user_id","appt_id","service_id","start_time","no_show_time"],"title":"WebhookReservationNoShowData","description":"Data payload for reservation_no_show webhook event.","x-internal":true},"WebhookReservationNoShowPayload":{"properties":{"event_type":{"type":"string","const":"reservation_no_show","title":"Event Type","description":"Type of event (always 'reservation_no_show' for this payload)"},"event_id":{"type":"string","title":"Event Id","description":"Unique identifier for this event"},"partner_id":{"type":"string","title":"Partner Id","description":"Partner ID this event is for"},"data":{"$ref":"#/components/schemas/WebhookReservationNoShowData","description":"Event data containing user, appointment, and no-show determination information"}},"type":"object","required":["event_type","event_id","partner_id","data"],"title":"WebhookReservationNoShowPayload","description":"Webhook payload for reservation_no_show events.\n\nSent when BodySpec determines that a reserved appointment was a no-show. BodySpec does not\nrecord attendance, so the determination is inferred and deliberately conservative; a later\n`results_ready` event for the same appointment supersedes it.","examples":[{"data":{"appt_id":"e8c91d4a2b7f3e6c8d0a5b9f1c3e7d2a","no_show_time":"2024-03-20T19:05:12-07:00","service_id":"8d0e0a1f561f4ec69dbca50ea14336d","start_time":"2024-03-20T14:00:00-07:00","user_id":"a5950beea2a7487fb79cfdbeb77fa555"},"event_id":"9c1f4e27ab8d43e6b5f2a0c7d8e91b34","event_type":"reservation_no_show","partner_id":"pk_acme"}],"x-internal":true},"WebhookResponse":{"properties":{"webhook_id":{"type":"string","title":"Webhook Id","description":"Unique identifier for the webhook (Airtable record ID)"},"url":{"type":"string","title":"Url","description":"URL configured to receive webhook events"},"event_type":{"$ref":"#/components/schemas/WebhookEventType","description":"Event type this webhook is subscribed to"},"enabled":{"type":"boolean","title":"Enabled","description":"Whether the webhook is currently enabled"},"has_secret":{"type":"boolean","title":"Has Secret","description":"Whether a secret is configured for this webhook"},"create_time":{"type":"string","format":"date-time","title":"Create Time","description":"When the webhook was created (UTC)"}},"type":"object","required":["webhook_id","url","event_type","enabled","has_secret","create_time"],"title":"WebhookResponse","description":"Response model for a webhook configuration.","x-internal":true},"WebhookResultsReadyData":{"properties":{"user_id":{"type":"string","title":"User Id","description":"BodySpec user ID"},"result_id":{"type":"string","title":"Result Id","description":"Result/appointment ID"},"appt_id":{"type":"string","title":"Appt Id","description":"Appointment ID. For results_ready events this is the same value as result_id; it is echoed under its own key so it can be correlated with the appt_id from the reservation_created event."},"service_id":{"type":"string","title":"Service Id","description":"Service identifier"},"start_time":{"type":"string","title":"Start Time","description":"Result time in ISO 8601 format with timezone"}},"type":"object","required":["user_id","result_id","appt_id","service_id","start_time"],"title":"WebhookResultsReadyData","description":"Data payload for results_ready webhook event.","x-internal":true},"WebhookResultsReadyPayload":{"properties":{"event_type":{"type":"string","const":"results_ready","title":"Event Type","description":"Type of event (always 'results_ready' for this payload)"},"event_id":{"type":"string","title":"Event Id","description":"Unique identifier for this event"},"partner_id":{"type":"string","title":"Partner Id","description":"Partner ID this event is for"},"data":{"$ref":"#/components/schemas/WebhookResultsReadyData","description":"Event data containing user and result information"}},"type":"object","required":["event_type","event_id","partner_id","data"],"title":"WebhookResultsReadyPayload","description":"Webhook payload for results_ready events.\n\nSent when scan results are ready for a user.","examples":[{"data":{"appt_id":"d7ecde6e9518008607e0e1e3b4b960d9","result_id":"d7ecde6e9518008607e0e1e3b4b960d9","service_id":"8d0e0a1f561f4ec69dbca50ea14336d","start_time":"2024-03-15T10:30:00-07:00","user_id":"a5950beea2a7487fb79cfdbeb77fa555"},"event_id":"550e8400e29b41d4a716446655440000","event_type":"results_ready","partner_id":"pk_acme"}],"x-internal":true},"WebhookUpdateRequest":{"properties":{"url":{"anyOf":[{"type":"string","maxLength":2083,"minLength":1,"format":"uri"},{"type":"null"}],"title":"Url","description":"HTTPS URL to receive webhook events"},"secret":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"title":"Secret","description":"New secret for webhook authentication. Pass empty string to remove."},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled","description":"Whether the webhook is enabled"}},"type":"object","title":"WebhookUpdateRequest","description":"Request model for updating a webhook (partial update).\n\nNote: event_type cannot be changed after creation.","examples":[{"enabled":false},{"secret":"new-secret-value","url":"https://your-server.example.com/webhooks/v2"}],"x-internal":true},"WebhooksListResponse":{"properties":{"webhooks":{"items":{"$ref":"#/components/schemas/WebhookResponse"},"type":"array","title":"Webhooks","description":"List of webhook configurations"}},"type":"object","required":["webhooks"],"title":"WebhooksListResponse","description":"Response model for listing webhooks.","x-internal":true}},"securitySchemes":{"OAuth2":{"type":"oauth2","description":"OAuth2 authentication via Keycloak with PKCE","flows":{"authorizationCode":{"authorizationUrl":"https://auth.bodyspec.com/realms/bodyspec/protocol/openid-connect/auth","tokenUrl":"https://auth.bodyspec.com/realms/bodyspec/protocol/openid-connect/token","scopes":{"openid":"OpenID Connect scope","profile":"Access to user profile","email":"Access to user email"},"x-usePkce":"SHA-256","x-scalar-client-id":"bodyspec-api-ext-v1"}}},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT Bearer token for authentication"},"PartnerAuth":{"type":"http","scheme":"basic","description":"For partner integrations only. Contact BodySpec to obtain credentials."}}},"tags":[{"name":"API Status","description":"Health check and monitoring endpoints"},{"name":"Users","description":"Operations related to user management and profiles"},{"name":"Appointments","description":"Appointments scheduling and management"},{"name":"Results","description":"Test results and analysis data"},{"name":"Locations","description":"Browse and search scan locations"},{"name":"Services","description":"Available scan types and services"},{"name":"Availability","description":"Check appointment availability at locations"},{"name":"Reservations","description":"Partner integration endpoints for reservations"},{"name":"Partner Users","description":"Partner integration endpoints for user management"},{"name":"Partner Appointments","description":"Partner integration endpoints for user appointments"},{"name":"Partner Results","description":"Partner integration endpoints for user results"},{"name":"Partner Intake","description":"Partner integration endpoints for intake form submission"},{"name":"Partner Orders","description":"Partner integration endpoints for medical order upload and management"},{"name":"Partner Webhooks","description":"Partner integration endpoints for webhook configuration"}],"security":[{"OAuth2":["openid","profile","email"]},{"BearerAuth":[]}],"x-tagGroups":[{"name":"👤 User Data","tags":["Users","Appointments","Results"]},{"name":"📅 Availability","tags":["Locations","Services","Availability"]},{"name":"🤝 Partners","tags":["Reservations","Partner Users","Partner Appointments","Partner Results","Partner Intake","Partner Orders","Partner Webhooks"]},{"name":"🏥 API Status","tags":["API Status"]}]}