> ## Documentation Index
> Fetch the complete documentation index at: https://developers.soxai.co.jp/llms.txt
> Use this file to discover all available pages before exploring further.

# Get a family group's shared health data

> The single data-view endpoint for family groups (spec §3.5).
All masking goes through resolve_field_mask — no endpoint-local mask logic.

Every card is scoped to the days that member has been in the group (SOX-91):
nothing from before their `joinedAt` day is shared, whatever the requested
range says, and leaving and rejoining moves the floor to the rejoin day
(leaving deletes the membership, so a rejoin gets a fresh `joinedAt`). So a
long `start_day` does not reach further back than the join for a recent
member — their `history` simply begins on their join day — and a member who
joined today shows only today. Days are the owner's local days, and each card
reports its own floor as `sharedFrom` so a short `history` reads as a recent
join rather than as missing data.

`data` holds the latest visible value per catalog metric; `last_sync_time`
(UTC ISO 8601) is a profile snapshot, so it appears there only and never in
the per-day `history`.

Sleep duration (睡眠時間) is `sleep_total_time_exclude_awake`: minutes of true
sleep (純睡眠時間), that is sleep with mid-sleep waking (中途覚醒) excluded - the
same number the owner's own sleep screen shows (SOX-95). It used to be
`sleep_total_time` (total sleep time, 総睡眠時間), which reads longer by
exactly the night's awake time, so one label answered two different values
depending on the screen. BREAKING for clients on the old key — both the
response key and the `sharedFields` opt-in key are renamed, because a
GroupView catalog key is always the stored InfluxDB `_field` name. DailyInfo
v2 serves this same field to the personal screen as `sleep_total_sleep_time`.
A night with no true sleep time (純睡眠時間) recorded omits the field rather
than reporting 0 — the same "--" the owner sees for that night, since there is
no total to fall back to that would still agree with their own screen.

Beside `displayName` and `role`, each card carries `joinedAt` (SOX-92) — when
that person's membership started, as offset-carrying ISO 8601 like the group's
own `createdAt`. It sits outside `data` and outside `visibleFields` because it
is membership metadata rather than a health value: sharing settings do not hide
who is in the group. Same key, same value as the `members` rows of
`GET /api/groups`.

`joinedAt` and `sharedFrom` are not duplicates: the first is the exact instant
the membership began, the second the owner's LOCAL day that instant falls in.
They disagree whenever the two calendars do — a JST member who joined at
02:00 local has `joinedAt` on the previous UTC day. Read `joinedAt` for the join
date (参加日) and ordering, `sharedFrom` for where that member's data starts.

`heat_stroke_severity` is the stored severity integer, passed through
unchanged (SOX-83): 0=NORMAL, 1=Low, 2=Medium, 3=High, 4=Critical — the same
scale the apps already render on their own heat-stroke screens. Per day it is
the HIGHEST severity that day reached, not the most recent one. It comes with
`heat_stroke_alert_time`, the last moment that peak was reached, as ISO 8601
in the OWNER's UTC offset (`2026-08-28T15:00:00+09:00`) so the wall clock
reads the same for a viewer in another timezone. A severity-0 day carries no
alert time: NORMAL is not an alert (警戒).

The pair is omitted entirely when there is no alert record in the window,
which is NOT the same statement as 0/NORMAL: soxai-app only writes a record
when it raises an alert, so an absent field means "no alert, or no ring, or
no sync". Pair it with `last_sync_time` to tell those apart. Its `history`
days are the OWNER's local days, since the underlying alerts are timestamped
instants rather than the day labels the daily metrics use.

Each card carries `reactions` — the emoji the rest of the group is currently
holding on that member (SOX-116), with a derived `count` and `mine` for the
caller. They are **per day** and reset daily: the cards carry today's (JST)
reactions, or those of the day named by `day_str`, and the response reports
which day as `reactionDay`. `day_str` is independent of `start_day`/`end_day`,
which govern health data only. They sit outside `data` and outside the
mask for the same reason `joinedAt` does — a reaction is a message between
members, not a health value — so a `masterHidden` member shows an empty `data`
and still carries reactions, and reading them writes no audit entry. `[]` when
nobody has reacted. Send and take back with
`PUT`/`DELETE /api/groups/{group_id}/reactions/{target_uid}/{emoji_id}`, or read
them on their own with `GET /api/groups/{group_id}/reactions` when the full
InfluxDB fan-out this endpoint pays for is not needed.

`visibleFields` is the viewer's mask. Since SOX-83 that INCLUDES the caller's
own card: your own card shows exactly what the rest of the group sees of you,
so the family screen doubles as a preview of your sharing settings. An owner
with `masterHidden` therefore sees their own card empty too. The self card
also carries `sharedFields` / `masterHidden` / `sharedFieldNames` — the raw
toggles the edit screen needs (SOX-67);
`GET /api/groups/{group_id}/members/me` returns the same.

**Parameters**

- `group_id` (path, required) - the group whose members' cards are returned.
- `start_day` / `end_day` (query, optional) - `YYYY-MM-DD`, both inclusive. Give
  either one and every card also carries a per-day `history`; omit both for latest
  values only.
- `day_str` (query, optional) - `YYYY-MM-DD`, the day whose `reactions` the cards
  carry. Defaults to today (JST). Reactions only: it does not affect `data` or
  `history`.

**Response**

- `200` returns one card per active member, each holding `data` - the latest visible
  value per catalog metric - and `visibleFields`, what that member shares with the
  caller. A member sharing nothing still appears, with an empty `data`.
- The caller's own card carries the whole catalog regardless of what they share.

**Errors**

| Status | Condition |
| --- | --- |
| 400 | `start_day`, `end_day` or `day_str` is not `YYYY-MM-DD`, `start_day` is after `end_day`, or the range exceeds 366 days |
| 404 | The group does not exist |

**Authorization**

- Scheme: `Authorization: Bearer <idToken>` or `soxai-api-key: <api key>`.
- Authority inside a group comes from the caller's own membership record, never from a SOXAI
  role: an `Admin`, `PartnerAdmin` or `OrgAdmin` claim grants nothing here.
- Required: an active member of the group. A caller who is not one receives
  `403 not a member of this group`, whether or not the group exists.

**Response fields**

**GroupView**

| Field | Unit | Description |
| --- | --- | --- |
| `sleep_score` | pt | Sleep Score: Overall index of sleep quality |
| `health_score` | pt | Health Score: Overall index of physical health, ranging from 0 to 100 |
| `activity_score` | pt | Activity Score: Index of physical activity |
| `sleep_total_time_exclude_awake` | mins | Sleep - Sleep Time Excluding Awake Time: Sleep duration excluding time spent awake |
| `activity_steps` | step | Activity - Steps: Number of steps in a day |
| `heat_stroke_severity` | code | Heat Stroke Alert - Severity: Integer from 0 to 4, passed through from SX_HeatStroke_Alert. Absent when no alert record exists for the day, which covers no alert, no ring and no sync alike |
| `heat_stroke_alert_time` | datetime | Heat Stroke Alert - Alert Time: ISO 8601 instant the alert fired, carrying the local UTC offset of the owner. Shares the heat_stroke_severity sharing setting |
| `last_sync_time` | datetime | Last Sync Time: UTC ISO 8601 timestamp of the last recorded ring sync, to second precision. Read from the Firestore user profile rather than a time series, and absent when the user has never synced |



## OpenAPI

````yaml /openapi-public.json get /api/v2/GroupView/{group_id}
openapi: 3.1.0
info:
  title: SOXAI API
  description: >


    ## SOXAI API helps you get data stored in SOXAI database.

    Every endpoint below documents its own parameters, responses and
    authorization rule.


    ## How to use

    1. All data-related endpoints need you to sign in first. Use the account you
    registered in the SOXAI app.


    2. You can get a Bearer token and localID like this:
        ```
        {
        "kind": "identitytoolkit#VerifyPasswordResponse",
        "registered": true,
        "localId": "[YOUR UID]",
        "email": "[YOUR EMAIL]",
        "idToken": "[YOUR BEARER TOKEN]",
        "refreshToken": [YOUR Refresh TOKEN]"
        }
        ```
        use this `idToken` to fill it into the value field after you click **Authorize** button, or attach it to header in your request. For more info, search for "Bearer token".
        (ps. the difference between idToken and refreshToken: idToken will expire in 1 hour, refreshToken will expire in one month, you can use refreshToken to get new idToken when idToken expired.)
    3. All users can access to his/her personal data with `localId` as user's
    uid.


    4. If you are an organization admin, contact SOXAI first to create an
    organization.


    5. After the organization is created, you can invite other users to join it.
    They receive an email to accept the invitation.


    6. As an organization user, you can read your own data, plus the roster and
    profiles of your organization. Other members' measurement data is not yours
    to read - that is what an organization admin does. See "Who can read whose
    data" below.


    ## Quick start note for personal user

    1. (Only once) Access `/api/login` to get your uid and OAuthToken by api,
    please memo OAuthToken, you can use it to access api without login again.

    2. Access `/api/v2/DailyInfoData/{query_uid}` for day-level scores, or
       `/api/v2/DailyDetailData/{query_uid}` for the finer series. Both take a date range and
       return JSON, or a csv download when `format` is `csv`.
    3. The v1 forms `/api/RawData/{query_uid}`, `/api/DailyInfoData/{query_uid}`
    and
       `/api/DailyDetailData/{query_uid}` return the same measurements under the raw database
       field names. Prefer the `/api/v2/` endpoints for a new integration.

    ## Quick start note for organization user

    1. You will receive an email to accept the invitation from your organization
    admin. After you accept it, your data is shared with that admin.

    2. If you want to query your own data, you can refer to the note for
    personal user.

    3. You can query other orguser's info(email, uid, etc) after obtain your
    org_id from `/api/myOrg` and access `/api/orgs/{org_id}/orgUsers`



    ## Quick start note for organization admin

    1. Contact SOXAI (info@soxai.co.jp) to create an organization for you.

    2. Access `/api/login` to get your uid and OAuthToken by api.

    3. Access `/api/myOrg` to get your organization id.

    4. Access `/api/orgs/{org_id}/orgUsers` to create, read, update and delete
    organization users. When you post a new user, they receive an email to
    accept the invitation.

    5. Access `/api/v2/DailyInfoDataForOrg` or `/api/v2/DailyDetailDataForOrg`
    to read your
       organization's members in one request. **You reach the SOXAI app data of the users who
       have agreed to share it**, and the endpoint narrows the answer to that set for you.


    ## Authentication and errors common to every endpoint


    Unless an endpoint says otherwise, it requires `Authorization: Bearer
    <idToken>` and answers:


    | Status | Condition |

    | --- | --- |

    | 401 | The `Authorization` header is missing, or the token is invalid or
    expired |

    | 403 | The caller is authenticated but not allowed to make this request.
    Each endpoint's **Authorization** block lists the bodies it uses |

    | 422 | The request did not match the schema. An **authorized** caller sees
    this instead of a 403; an unauthorized caller is refused before its body is
    read |

    | 429 | The rate limit was exceeded. Wait for `Retry-After` seconds before
    retrying |

    | 500 | Unexpected server-side failure |


    Each endpoint documents only the statuses that are specific to it.


    The **Authorization** block is the last section of every endpoint's
    description. For the

    endpoints that declare their rule on the route it is generated from that
    declaration, so it

    cannot disagree with what is enforced.


    ### The second credential


    `soxai-api-key: <key>` is accepted wherever `Authorization: Bearer
    <idToken>` is. The

    difference is lifetime and who issues it: an ID token expires in an hour and
    you mint it

    yourself from `POST /api/login`, while an API key is long-lived and read
    back from

    `GET /api-key/secret`. A request carries one or the other, not both.
    Server-to-server

    integrations should hold the key; anything acting on behalf of a signed-in
    person should

    hold the token, so that revoking the person's access is enough to stop it.


    ### Who can read whose data


    Which role you hold decides how far past your own uid you can read. A role
    is a claim on

    your credential, not something you pass as a parameter.


    | Role | Reaches |

    | --- | --- |

    | (none) | Only your own uid. Every user has this much |

    | `OrgUser` | Your own uid, plus your organization's roster and member
    profiles (email, uid, profile) - not other members' measurement data |

    | `OrgAdmin` | Every member of your own organization, including their
    measurement data |

    | `PartnerAdmin` | Every organization owned by your own company, and every
    member of those organizations |

    | `Admin` | Every user, with no company or organization scoping. Held by
    SOXAI only |


    One consequence catches integrators out: the roster an admin reaches is the
    whole roster,

    not only its active part. A membership's `status` records where a member
    stands in the

    invitation lifecycle; it does not by itself narrow what an admin can read.
    Do not infer a

    member's data scope from their `status` - read the endpoint's
    **Authorization** block,

    which states the rule that is actually enforced.


    ### v1 and v2


    The data endpoints come in two generations. `/api/v2/...` returns
    standardized field

    names and is the interface to build against. The v1 endpoints
    (`/api/RawData`,

    `/api/DailyInfoData`, `/api/DailyDetailData`) return the raw database field
    names, which

    change when the database does. They are not deprecated and they are not
    going away

    without notice, but they will not gain fields that v2 has; a new integration
    should use

    v2.
  contact:
    name: SOXAI
    url: https://www.soxai.co.jp/contact
    email: info@soxai.co.jp
  version: 1.0.0
servers:
  - url: https://web-api.soxai.site
    description: Production
security: []
tags:
  - name: Authentication
    description: >-
      Obtain and hold the credential every other endpoint asks for. `POST
      /api/login` returns the `idToken` you send as `Authorization: Bearer
      <idToken>` and the `refreshToken` that renews it; the password reset pair
      is here because a caller who has lost their password cannot hold a token
      either. `/app/me` reports what the credential you are holding actually
      carries, which is the first thing to check when an endpoint answers 403.
      The long-lived alternative to an ID token is under `API Keys`.
  - name: API Keys
    description: >-
      Issue and read back the caller's own long-lived API key, which is accepted
      as `soxai-api-key: <key>` wherever an ID token is. A caller holds one key
      at a time and reads it back rather than being shown it once. Hold a key
      for a server-to-server integration, where there is no signed-in person
      whose session could expire; hold an ID token from `Authentication` for
      anything acting on a person's behalf, so that revoking the person's access
      is enough to stop it.
  - name: Data (v2)
    description: >-
      Read measurement data with standardized field names. One endpoint per data
      shape for a single user (`DailyInfoData`, `DailyDetailData`,
      `DailyTrendData`) and the bulk forms for a whole organization or company
      (`...ForOrg`). This is the interface to build against.
  - name: Data (v1)
    description: >-
      Read measurement data with the raw database field names, one user at a
      time, as CSV or JSON. These endpoints are not deprecated and will not
      disappear without notice, but the names they return are the ones the
      database uses and change when it does, and they will not gain the fields
      `Data (v2)` has. **A new integration should use `Data (v2)`.**
  - name: Sync Status
    description: >-
      Whether a user's ring data arrived, rather than what it says.
      `ProcessLog/{query_uid}` returns one user's sync history and
      `LastProcessLog` the most recent entry. Read these before concluding that
      a missing day is missing rather than not yet uploaded - the data endpoints
      answer with what was received and cannot tell the two apart.
  - name: Organizations
    description: >-
      Create and manage organizations. An organization owns a member roster and
      is the unit every org-scoped endpoint addresses. A `PartnerAdmin` sees
      only the organizations its own company owns.
  - name: Organization Members
    description: >-
      Manage an organization's member roster: enrol members, invite them, edit
      their teams and admin flag, and remove them. A member reaches these
      endpoints only for the organization their own token names.
      `AcceptEmailVerification` is the other end of that flow - the link in the
      invitation email, which the invited user opens without being signed in.
  - name: Family Groups
    description: >-
      Small self-service groups where members choose, per metric, what they
      share with each other. Authority here comes from the caller's membership
      record and its `admin` / `member` role **inside the group** - a SOXAI
      `Admin`, `PartnerAdmin` or `OrgAdmin` claim grants nothing. `GroupView` is
      the single endpoint that returns members' health data.
  - name: Partner Companies
    description: >-
      Partner companies register and sign in **their own** users with
      certificates rather than through the SOXAI app. These are the endpoints
      the partner's own backend calls, authenticating with a signed certificate
      instead of a SOXAI token. The certificate itself is issued and revoked by
      SOXAI rather than through this API; contact SOXAI to obtain one.
  - name: Deep Links
    description: >-
      Deep links that carry a one-time token into the SOXAI app: group
      invitations, and sign-up links that prefill an email address. The token is
      the credential, so the endpoints that exchange one need no signed-in
      caller.
  - name: OAuth2
    description: >-
      Sign a SOXAI user in to your own application with the OAuth2 authorization
      code flow. Your code reaches four of these: the discovery document your
      OIDC library reads, the URL you send the user's browser to, the token
      endpoint your backend calls with the authorization code, and the record of
      what the user consented to. The screens in between - the login form, the
      consent page and the two-factor prompts - are SOXAI's own pages, posted to
      by the browser, and your application never calls them. The remaining
      endpoint sets whether two-factor is required for a list of users, which an
      `OrgAdmin` or `PartnerAdmin` does for its own members.
  - name: Users
    description: >-
      Endpoints keyed on a user rather than on an organization: ring serial
      number lookup, and the caller's own ring sync history.
  - name: System
    description: >-
      Liveness and health checks for monitoring. `ping` answers from the process
      alone; `health` probes Firestore and both InfluxDB clients and answers
      `500` when one of them is unreachable; `health_check` reports the same
      dependencies one by one inside a `200`. None of these require
      authentication, and none of them read or write a user's data.
paths:
  /api/v2/GroupView/{group_id}:
    get:
      tags:
        - Family Groups
      summary: Get a family group's shared health data
      description: >-
        The single data-view endpoint for family groups (spec §3.5).

        All masking goes through resolve_field_mask — no endpoint-local mask
        logic.


        Every card is scoped to the days that member has been in the group
        (SOX-91):

        nothing from before their `joinedAt` day is shared, whatever the
        requested

        range says, and leaving and rejoining moves the floor to the rejoin day

        (leaving deletes the membership, so a rejoin gets a fresh `joinedAt`).
        So a

        long `start_day` does not reach further back than the join for a recent

        member — their `history` simply begins on their join day — and a member
        who

        joined today shows only today. Days are the owner's local days, and each
        card

        reports its own floor as `sharedFrom` so a short `history` reads as a
        recent

        join rather than as missing data.


        `data` holds the latest visible value per catalog metric;
        `last_sync_time`

        (UTC ISO 8601) is a profile snapshot, so it appears there only and never
        in

        the per-day `history`.


        Sleep duration (睡眠時間) is `sleep_total_time_exclude_awake`: minutes of
        true

        sleep (純睡眠時間), that is sleep with mid-sleep waking (中途覚醒) excluded - the

        same number the owner's own sleep screen shows (SOX-95). It used to be

        `sleep_total_time` (total sleep time, 総睡眠時間), which reads longer by

        exactly the night's awake time, so one label answered two different
        values

        depending on the screen. BREAKING for clients on the old key — both the

        response key and the `sharedFields` opt-in key are renamed, because a

        GroupView catalog key is always the stored InfluxDB `_field` name.
        DailyInfo

        v2 serves this same field to the personal screen as
        `sleep_total_sleep_time`.

        A night with no true sleep time (純睡眠時間) recorded omits the field rather

        than reporting 0 — the same "--" the owner sees for that night, since
        there is

        no total to fall back to that would still agree with their own screen.


        Beside `displayName` and `role`, each card carries `joinedAt` (SOX-92) —
        when

        that person's membership started, as offset-carrying ISO 8601 like the
        group's

        own `createdAt`. It sits outside `data` and outside `visibleFields`
        because it

        is membership metadata rather than a health value: sharing settings do
        not hide

        who is in the group. Same key, same value as the `members` rows of

        `GET /api/groups`.


        `joinedAt` and `sharedFrom` are not duplicates: the first is the exact
        instant

        the membership began, the second the owner's LOCAL day that instant
        falls in.

        They disagree whenever the two calendars do — a JST member who joined at

        02:00 local has `joinedAt` on the previous UTC day. Read `joinedAt` for
        the join

        date (参加日) and ordering, `sharedFrom` for where that member's data
        starts.


        `heat_stroke_severity` is the stored severity integer, passed through

        unchanged (SOX-83): 0=NORMAL, 1=Low, 2=Medium, 3=High, 4=Critical — the
        same

        scale the apps already render on their own heat-stroke screens. Per day
        it is

        the HIGHEST severity that day reached, not the most recent one. It comes
        with

        `heat_stroke_alert_time`, the last moment that peak was reached, as ISO
        8601

        in the OWNER's UTC offset (`2026-08-28T15:00:00+09:00`) so the wall
        clock

        reads the same for a viewer in another timezone. A severity-0 day
        carries no

        alert time: NORMAL is not an alert (警戒).


        The pair is omitted entirely when there is no alert record in the
        window,

        which is NOT the same statement as 0/NORMAL: soxai-app only writes a
        record

        when it raises an alert, so an absent field means "no alert, or no ring,
        or

        no sync". Pair it with `last_sync_time` to tell those apart. Its
        `history`

        days are the OWNER's local days, since the underlying alerts are
        timestamped

        instants rather than the day labels the daily metrics use.


        Each card carries `reactions` — the emoji the rest of the group is
        currently

        holding on that member (SOX-116), with a derived `count` and `mine` for
        the

        caller. They are **per day** and reset daily: the cards carry today's
        (JST)

        reactions, or those of the day named by `day_str`, and the response
        reports

        which day as `reactionDay`. `day_str` is independent of
        `start_day`/`end_day`,

        which govern health data only. They sit outside `data` and outside the

        mask for the same reason `joinedAt` does — a reaction is a message
        between

        members, not a health value — so a `masterHidden` member shows an empty
        `data`

        and still carries reactions, and reading them writes no audit entry.
        `[]` when

        nobody has reacted. Send and take back with

        `PUT`/`DELETE /api/groups/{group_id}/reactions/{target_uid}/{emoji_id}`,
        or read

        them on their own with `GET /api/groups/{group_id}/reactions` when the
        full

        InfluxDB fan-out this endpoint pays for is not needed.


        `visibleFields` is the viewer's mask. Since SOX-83 that INCLUDES the
        caller's

        own card: your own card shows exactly what the rest of the group sees of
        you,

        so the family screen doubles as a preview of your sharing settings. An
        owner

        with `masterHidden` therefore sees their own card empty too. The self
        card

        also carries `sharedFields` / `masterHidden` / `sharedFieldNames` — the
        raw

        toggles the edit screen needs (SOX-67);

        `GET /api/groups/{group_id}/members/me` returns the same.


        **Parameters**


        - `group_id` (path, required) - the group whose members' cards are
        returned.

        - `start_day` / `end_day` (query, optional) - `YYYY-MM-DD`, both
        inclusive. Give
          either one and every card also carries a per-day `history`; omit both for latest
          values only.
        - `day_str` (query, optional) - `YYYY-MM-DD`, the day whose `reactions`
        the cards
          carry. Defaults to today (JST). Reactions only: it does not affect `data` or
          `history`.

        **Response**


        - `200` returns one card per active member, each holding `data` - the
        latest visible
          value per catalog metric - and `visibleFields`, what that member shares with the
          caller. A member sharing nothing still appears, with an empty `data`.
        - The caller's own card carries the whole catalog regardless of what
        they share.


        **Errors**


        | Status | Condition |

        | --- | --- |

        | 400 | `start_day`, `end_day` or `day_str` is not `YYYY-MM-DD`,
        `start_day` is after `end_day`, or the range exceeds 366 days |

        | 404 | The group does not exist |


        **Authorization**


        - Scheme: `Authorization: Bearer <idToken>` or `soxai-api-key: <api
        key>`.

        - Authority inside a group comes from the caller's own membership
        record, never from a SOXAI
          role: an `Admin`, `PartnerAdmin` or `OrgAdmin` claim grants nothing here.
        - Required: an active member of the group. A caller who is not one
        receives
          `403 not a member of this group`, whether or not the group exists.

        **Response fields**


        **GroupView**


        | Field | Unit | Description |

        | --- | --- | --- |

        | `sleep_score` | pt | Sleep Score: Overall index of sleep quality |

        | `health_score` | pt | Health Score: Overall index of physical health,
        ranging from 0 to 100 |

        | `activity_score` | pt | Activity Score: Index of physical activity |

        | `sleep_total_time_exclude_awake` | mins | Sleep - Sleep Time Excluding
        Awake Time: Sleep duration excluding time spent awake |

        | `activity_steps` | step | Activity - Steps: Number of steps in a day |

        | `heat_stroke_severity` | code | Heat Stroke Alert - Severity: Integer
        from 0 to 4, passed through from SX_HeatStroke_Alert. Absent when no
        alert record exists for the day, which covers no alert, no ring and no
        sync alike |

        | `heat_stroke_alert_time` | datetime | Heat Stroke Alert - Alert Time:
        ISO 8601 instant the alert fired, carrying the local UTC offset of the
        owner. Shares the heat_stroke_severity sharing setting |

        | `last_sync_time` | datetime | Last Sync Time: UTC ISO 8601 timestamp
        of the last recorded ring sync, to second precision. Read from the
        Firestore user profile rather than a time series, and absent when the
        user has never synced |
      operationId: get_group_view
      parameters:
        - description: The group whose members' cards are returned.
          required: true
          schema:
            type: string
            title: Group Id
            description: The group whose members' cards are returned.
            examples:
              - group_123
          name: group_id
          in: path
        - description: >-
            Start date inclusive (YYYY-MM-DD). Given either date, every card
            also carries a per-day `history` list; omit both for latest values
            only.
          required: false
          schema:
            type: string
            title: Start Day
            description: >-
              Start date inclusive (YYYY-MM-DD). Given either date, every card
              also carries a per-day `history` list; omit both for latest values
              only.
            examples:
              - '2026-01-01'
          name: start_day
          in: query
        - description: >-
            End date inclusive (YYYY-MM-DD). Defaults to today when only
            start_day is given.
          required: false
          schema:
            type: string
            title: End Day
            description: >-
              End date inclusive (YYYY-MM-DD). Defaults to today when only
              start_day is given.
            examples:
              - '2026-01-31'
          name: end_day
          in: query
        - description: >-
            The day whose reactions the cards carry (YYYY-MM-DD). Defaults to
            today in JST (UTC+09:00). Independent of start_day/end_day.
          required: false
          schema:
            type: string
            title: Day Str
            description: >-
              The day whose reactions the cards carry (YYYY-MM-DD). Defaults to
              today in JST (UTC+09:00). Independent of start_day/end_day.
            examples:
              - '2026-10-14'
          name: day_str
          in: query
      responses:
        '200':
          description: >-
            One card per active member. A member sharing nothing still appears,
            with an empty data. Cards carry history and the response carries
            range only when a date is given.
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
          content:
            application/json:
              schema: {}
              example:
                group:
                  groupId: b7d1f0c9a2e4d6f8b0c1
                  groupName: Shibata family
                  status: active
                members:
                  - uid: user123
                    displayName: Mom
                    role: admin
                    joinedAt: '2026-08-03T09:30:00+00:00'
                    isSelf: true
                    data:
                      sleep_score: 82
                      health_score: 76
                      activity_steps: 8421
                      last_sync_time: '2026-08-28T22:10:00Z'
                    visibleFields:
                      - activity_steps
                      - health_score
                      - last_sync_time
                      - sleep_score
                    sharedFrom: '2026-08-03'
                    reactions:
                      - id: muscle
                        emoji: 👍
                        count: 1
                        mine: false
                        uids:
                          - user456
                    sharedFields:
                      sleep_score: true
                      health_score: true
                      activity_score: false
                      sleep_total_time_exclude_awake: false
                      activity_steps: true
                      heat_stroke_severity: false
                      last_sync_time: true
                    masterHidden: false
                    sharedFieldNames:
                      - activity_steps
                      - health_score
                      - last_sync_time
                      - sleep_score
                  - uid: user456
                    displayName: Dad
                    role: member
                    joinedAt: '2026-08-05T02:10:00+00:00'
                    isSelf: false
                    data:
                      sleep_score: 68
                      heat_stroke_severity: 2
                      heat_stroke_alert_time: '2026-08-28T15:00:00+09:00'
                    visibleFields:
                      - heat_stroke_severity
                      - sleep_score
                    sharedFrom: '2026-08-05'
                    reactions: []
        '400':
          description: >-
            Bad Request - start_day or end_day is not YYYY-MM-DD, the range is
            inverted, or it is longer than 366 days
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: bad-request
                error_msg: date range must not exceed 366 days
        '401':
          description: Unauthorized - the credentials are missing, expired or invalid
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: http-error
                error_msg: Invalid authentication credentials. <reason>
        '403':
          description: Forbidden - Not enough permission
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: forbidden
                error_msg: Not enough permission
        '404':
          description: Not Found - no group holds that group_id
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: not-found
                error_msg: group not found
        '422':
          description: >-
            Unprocessable Entity - a request field does not meet the endpoint's
            validation rules
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: unprocessable-entity
                error_msg: 'body.<field>: <validation message>'
        '429':
          description: Too Many Requests - the rate limit was exceeded
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
            Retry-After:
              description: >-
                Whole seconds to wait until both applicable buckets can accept
                one request. Returned only with a 429 response.
              schema:
                type: integer
                minimum: 1
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: rate-limited
                error_msg: Rate limit exceeded. Retry after <n> seconds.
        '500':
          description: Internal Server Error - Failed to fetch data
          headers:
            X-RateLimit-Limit:
              description: Maximum capacity of the continuously refilled minute bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Whole tokens remaining in the minute bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: >-
                Unix timestamp in seconds when the minute bucket is expected to
                be full. This is not a calendar reset time.
              schema:
                type: integer
                minimum: 0
                format: int64
            X-RateLimit-Limit-Day:
              description: Maximum capacity of the continuously refilled daily bucket.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Remaining-Day:
              description: Whole tokens remaining in the daily bucket after this request.
              schema:
                type: integer
                minimum: 0
            X-RateLimit-Reset-Day:
              description: >-
                Unix timestamp in seconds when the daily bucket is expected to
                be full. The bucket refills continuously and does not reset at
                midnight.
              schema:
                type: integer
                minimum: 0
                format: int64
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: internal-server-error
                error_msg: 'Failed to fetch data: <error_message>'
      security:
        - HTTPBearer: []
        - SoxaiApiKey: []
components:
  schemas:
    ErrorResponse:
      properties:
        error:
          type: string
          title: Error
          description: Machine-readable error category.
          examples:
            - bad-request
        error_msg:
          type: string
          title: Error Msg
          description: Human-readable explanation of the error; clients must not parse it.
          examples:
            - 'Invalid input: start_time must use YYYY-MM-DD.'
      type: object
      required:
        - error
        - error_msg
      title: ErrorResponse
      description: >-
        The body of every error answer: a machine-readable type and a
        human-readable message.
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer
    SoxaiApiKey:
      type: apiKey
      description: >-
        API key issued by `POST /api-key/secret`, sent in the `soxai-api-key`
        header. It is an alternative to the Bearer token, not an addition to it:
        when both are sent the Bearer token is used. The key carries the same
        roles as the user it was issued to, and expires after the number of days
        it was issued for.
      in: header
      name: soxai-api-key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.