openapi: 3.0.3 info: title: Ultrahuman Partner (UltraSignal) API description: >- The Ultrahuman Partner API gives approved partners read access to user-consented health metrics from the Ultrahuman Ring AIR / Ring Pro and, for users with an active M1 patch, the Ultrahuman CGM. Access is secured with OAuth 2.0 (Authorization Code Grant plus Refresh Token flow) across three scopes: profile, ring_data, and cgm_data. Access is not self-serve - partners apply to the developer program and are issued a Client ID, Client Secret, and a registered redirect URI during onboarding; end users authorize data sharing from the Ultrahuman app (Profile -> Settings -> Partner ID). Access tokens expire in ~1 day (86400 seconds); refresh tokens rotate on each exchange. IMPORTANT: This document is MODELED by API Evangelist from Ultrahuman's public partner developer documentation. Endpoint paths, scopes, OAuth flows, query parameters, and the documented metric families are drawn from the docs; exact request/response schemas are approximations and should be verified against the live developer portal. Ultrahuman also documents a personal-token variant at GET /api/v1/partner/daily_metrics for a developer's own ring data. version: '1.0' contact: name: Ultrahuman Partnerships url: https://partnerships.ultrahuman.com/ x-endpointsModeled: true servers: - url: https://partner.ultrahuman.com description: Ultrahuman Partner API tags: - name: OAuth description: OAuth 2.0 authorization, token exchange, and revocation. - name: Metrics description: Ring and CGM daily metrics for consented users. - name: User description: Basic authorized-user profile information. paths: /authorize: get: operationId: authorize tags: - OAuth summary: Authorization endpoint (Authorization Code Grant) description: >- Redirects the user to Ultrahuman to consent to the requested scopes. On approval, Ultrahuman redirects back to the registered redirect_uri with an authorization code. parameters: - name: response_type in: query required: true schema: type: string enum: [code] - name: client_id in: query required: true schema: type: string - name: redirect_uri in: query required: true schema: type: string format: uri - name: scope in: query required: true description: Space-delimited scopes - profile, ring_data, cgm_data. schema: type: string example: profile ring_data cgm_data - name: state in: query required: false schema: type: string responses: '302': description: Redirect to redirect_uri with an authorization code (or an error). /api/partners/oauth/token: post: operationId: token tags: - OAuth summary: Token endpoint (code exchange and refresh) description: >- Exchanges an authorization code for an access token, or exchanges a refresh token for a new access token. Perform token exchanges server-side. Refresh tokens rotate on each exchange. requestBody: required: true content: application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/TokenRequest' responses: '200': description: A new access token (and rotated refresh token). content: application/json: schema: $ref: '#/components/schemas/TokenResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /api/partners/oauth/revoke: post: operationId: revoke tags: - OAuth summary: Revoke a token description: Revokes an access or refresh token. requestBody: required: true content: application/x-www-form-urlencoded: schema: type: object required: [token, client_id, client_secret] properties: token: type: string client_id: type: string client_secret: type: string responses: '200': description: Token revoked. '400': $ref: '#/components/responses/BadRequest' /api/partners/v1/user_data/metrics: get: operationId: getUserMetrics tags: - Metrics summary: Get a user's daily health metrics description: >- Returns consented daily metrics for the authorized user. Ring metrics require the ring_data scope; glucose/CGM metrics require the cgm_data scope and an active M1 patch. Query by a single date (YYYY-MM-DD) or by a start_epoch/end_epoch window (max range 7 days). date and the epoch pair are mutually exclusive. security: - bearerAuth: [] parameters: - name: date in: query required: false description: Target day in YYYY-MM-DD. Mutually exclusive with the epoch pair. schema: type: string format: date - name: start_epoch in: query required: false description: Window start, epoch seconds. Use with end_epoch (max 7-day range). schema: type: integer format: int64 - name: end_epoch in: query required: false description: Window end, epoch seconds. schema: type: integer format: int64 - name: email in: query required: false description: Optional target user's email for authorized multi-user access. schema: type: string format: email responses: '200': description: The user's metrics for the requested period. content: application/json: schema: $ref: '#/components/schemas/MetricsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /api/partners/v1/user_data/user_info: get: operationId: getUserInfo tags: - User summary: Get the authorized user's basic profile description: >- Returns basic profile info (user ID, time zone, etc.) for the authorized user. Requires the profile scope. security: - bearerAuth: [] responses: '200': description: Basic user profile. content: application/json: schema: $ref: '#/components/schemas/UserInfo' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' components: securitySchemes: bearerAuth: type: http scheme: bearer description: OAuth 2.0 access token in the Authorization header. responses: BadRequest: description: Invalid parameters, or the epoch window exceeded the 7-day limit. Unauthorized: description: Missing, invalid, or expired access token. NotFound: description: User not found, or the partner lacks permission for the requested data. schemas: TokenRequest: type: object required: [grant_type, client_id, client_secret] properties: grant_type: type: string enum: [authorization_code, refresh_token] client_id: type: string client_secret: type: string code: type: string description: Required for grant_type=authorization_code. redirect_uri: type: string format: uri refresh_token: type: string description: Required for grant_type=refresh_token. TokenResponse: type: object properties: access_token: type: string token_type: type: string example: Bearer expires_in: type: integer example: 86400 refresh_token: type: string scope: type: string created_at: type: integer format: int64 UserInfo: type: object properties: id: type: string timezone: type: string example: Asia/Kolkata MetricsResponse: type: object description: >- Modeled envelope of daily metrics. The docs describe roughly 40+ metric fields spanning cardiovascular, sleep, glucose, activity/recovery, and temperature families; representative fields are shown here. properties: date: type: string format: date cardiovascular: type: object properties: heart_rate: type: number hrv_rmssd: type: number night_resting_heart_rate: type: number sleep_hrv: type: number spo2: type: number sleep: type: object properties: total_duration_minutes: type: number efficiency: type: number rem_minutes: type: number deep_minutes: type: number light_minutes: type: number sleep_score: type: number temperature_deviation: type: number activity_recovery: type: object properties: steps: type: integer active_minutes: type: number movement_index: type: number recovery_index: type: number recovery_score: type: number metabolic_score: type: number vo2_max: type: number temperature: type: object properties: skin_temperature_celsius: type: number avg_body_temperature_sleep: type: number glucose: type: object description: Present only with the cgm_data scope and an active M1 patch. properties: readings: type: array items: type: object properties: epoch: type: integer format: int64 value_mgdl: type: number average_mgdl: type: number variability: type: number estimated_hba1c: type: number time_in_target_percent: type: number