> ## Documentation Index
> Fetch the complete documentation index at: https://docs.junction.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API

> Changelog of Junction Wearables API updates including Sense readiness scores, WHERE clause support, and derived readiness query tables.

### Junction Sense: Menstrual Cycle resource (April 2026)

Junction Sense now supports the `menstrual_cycle` query table, covering cycle-level scalars and the nested daily logs.

Check out the updated [Column expressions](/sense/query-dsl/column-expressions#menstrualcycle) reference for the full field mapping.

<Accordion icon="droplet" iconType="duotone" title="Details" defaultOpen>
  * New query table: `menstrual_cycle`, with scalar columns (`period_start`, `period_end`, `cycle_end`, `is_predicted`, `source_*`) and list-of-struct columns (`menstrual_flow`, `cervical_mucus`, `basal_body_temperature`, `intermenstrual_bleeding`, `contraceptive`, `detected_deviations`, `ovulation_test`, `home_pregnancy_test`, `home_progesterone_test`, `sexual_activity`).
  * New index expression: `{ "index": "menstrual_cycle" }` / `MenstrualCycle.index()`.
  * Aggregate the nested daily logs with the [list-column aggregation](/sense/query-dsl/list-column-aggregation)

  Example — period end, cycle end, and mean basal body temperature, one row per cycle:

  <CodeGroup>
    ```python Python DSL theme={null}
    import vitalx.aggregation as va

    va.select(
        va.group_key("*"),
        va.MenstrualCycle.col("period_end").newest(),
        va.MenstrualCycle.col("cycle_end").newest(),
        va.MenstrualCycle.col("basal_body_temperature")
            .unnest_and_select(lambda col: col.field("value").mean())
            .mean(),
    ).group_by(
        va.date_trunc(va.MenstrualCycle.index(), 1, "day")
    )
    ```

    ```jsonc JSON DSL theme={null}
    {
      "select": [
        { "group_key": "*" },
        { "func": "newest", "arg": { "menstrual_cycle": "period_end" } },
        { "func": "newest", "arg": { "menstrual_cycle": "cycle_end" } },
        {
          "func": "mean",
          "arg": {
            "select": {
              "func": "mean",
              "arg": { "field_for": "menstrual_cycle", "basal_body_temperature": "value" }
            },
            "from": { "unnest": { "menstrual_cycle": "basal_body_temperature" } }
          }
        }
      ],
      "group_by": [
        {
          "date_trunc": { "value": 1, "unit": "day" },
          "arg": { "index": "menstrual_cycle" }
        }
      ]
    }
    ```
  </CodeGroup>
</Accordion>

### Junction Sense: list-column aggregation (April 2026)

Junction Sense now supports aggregating the elements of list columns via a scalar-output subquery - for example, counting meaningful flow days per cycle or summing elements of a scalar-element list.

Check out [List-column aggregation](/sense/query-dsl/list-column-aggregation) for the full reference.

<Accordion icon="list-tree" iconType="duotone" title="Details" defaultOpen>
  * New select-expression variant for list columns: a scalar-output subquery with `select` / `from` (UNNEST) / optional `where`, producing one scalar per outer row. Wrap the subquery in an outer aggregate (e.g. `.mean()`) to use it inside a GROUP BY.
  * `arg` in the subquery's `select` has three shapes: `null` (SQL `COUNT(*)`), a struct-field reference (struct-element lists), or `{ "element": true }` (scalar-element lists).
  * Inside an UNNEST `where` over a scalar-element list (e.g. a list of string tags), the reserved identifier `element` refers to the element value itself.

  Example — count only the meaningful flow entries within each menstrual cycle:

  <CodeGroup>
    ```python Python DSL theme={null}
    import vitalx.aggregation as va

    va.select(
        va.MenstrualCycle.index(),
        va.MenstrualCycle.col("menstrual_flow")
            .unnest_and_select(lambda col: col.count())
            .where("flow != 'none'")
    )
    ```

    ```jsonc JSON DSL theme={null}
    {
      "select": [
        { "index": "menstrual_cycle" },
        {
          "select": { "func": "count", "arg": null },
          "from": { "unnest": { "menstrual_cycle": "menstrual_flow" } },
          "where": "flow != 'none'"
        }
      ]
    }
    ```
  </CodeGroup>
</Accordion>

### Junction Sense `awakenings` macro (April 2026)

Junction Sense now exposes a new Sleep Macro that counts the number of awakenings during a sleep session.
Check out [Sleep Analysis](/sense/query-dsl/sleep-analysis) for more details.

<Accordion icon="moon" iconType="duotone" title="Details" defaultOpen>
  * `awakenings` counts transitions from a sleeping phase (deep, light, or REM) to an awake phase, computed from sleep cycle hypnogram data.
  * Use this macro in the Select clause of Junction Sense queries to surface consistent awakening counts across providers.

  Syntax:

  <CodeGroup>
    ```python Python DSL theme={null}
    import vitalx.aggregation as va

    va.select(
        va.Sleep.awakenings()
    )
    ```

    ```jsonc JSON DSL theme={null}
    {
        "select": [
            { "value_macro": "awakenings" }
        ]
    }
    ```
  </CodeGroup>
</Accordion>

### Junction Sense Readiness Scores (March 2026)

Junction Sense now supports Readiness Scores, a new query table that provides daily readiness insights derived from sleep.
Use it to query sleep, recovery, stress and strain scores, their zones, and chronotype without building custom scoring logic.

Check out the [Readiness Scores](/sense/query-dsl/readiness-scores) documentation for full details.

<Accordion icon="bed" iconType="duotone" title="Details" defaultOpen>
  * New query table: `derived_readiness`
  * New index expression: `{ "index": "derived_readiness" }` / `DerivedReadiness.index()`
  * Works with standard Query DSL patterns (`select`, `where`, `group_by`, and aggregations).

  <CodeGroup>
    ```python Python DSL theme={null}
    import vitalx.aggregation as va

    va.select(
        va.group_key("*"),
        va.DerivedReadiness.col("recovery_score").mean(),
        va.DerivedReadiness.col("strain_score").max(),
    ).group_by(
        va.date_trunc(va.DerivedReadiness.index(), 1, "week")
    )
    ```

    ```jsonc JSON DSL theme={null}
    {
      "select": [
        { "group_key": "*" },
        { "func": "mean", "arg": { "derived_readiness": "recovery_score" } },
        { "func": "max", "arg": { "derived_readiness": "strain_score" } }
      ],
      "group_by": [
        {
          "date_trunc": { "value": 1, "unit": "week" },
          "arg": { "index": "derived_readiness" }
        }
      ]
    }
    ```
  </CodeGroup>
</Accordion>

### Junction Sense WHERE clause (September 2025)

Junction Sense now accepts a `where` clause so you can filter input rows before aggregation.

<Accordion icon="filter" iconType="duotone" title="Details" defaultOpen>
  * Compose SQL-style predicates with `>`, `>=`, `<`, `<=`, `=`, `!=`, `NOT`, `AND`, `OR`, and parentheses grouping.
  * Filters always run before `group_by` evaluation when both clauses are present.
  * Check out the [WHERE clause](/sense/query-dsl/where-clause) documentation for more details.

  <CodeGroup>
    ```python Python DSL theme={null}
    import vitalx.aggregation as va

    va.select(
        va.group_key("*"),
        va.Sleep.score().mean(),
        va.Sleep.col("stage_asleep_second").sum(),
    ).group_by(
        va.date_trunc(va.Sleep.index(), 1, "day"),
        va.Sleep.col("state"),
    ).where(
        "type = 'long_sleep'"
    )
    ```

    ```jsonc JSON DSL theme={null}
    {
        "select": [
            { "group_key": "*" },
            { "func": "mean", "arg": { "value_macro": "sleep_score" } },
            { "func": "sum", "arg": { "sleep": "stage_asleep_second" } }
        ],
        "group_by": [
            {
                "date_trunc": { "value": 1, "unit": "day" },
                "arg": { "index": "sleep" }
            },
            { "sleep": "state" }
        ],
        "where": "type = 'long_sleep'"
    }
    ```
  </CodeGroup>
</Accordion>

### Junction Sense sleep macros (September 2025)

Junction Sense now exposes two new Sleep Macros to help you pinpoint when users fall asleep and wake up within a session.
Check out [Sleep Analysis](/sense/query-dsl/sleep-analysis) for more details.

<Accordion icon="moon" iconType="duotone" title="Details" defaultOpen>
  * `asleep_at` returns the first instant at which the user is considered to be asleep, i.e., session start + sleep latency.
  * `awake_at` returns the time at which the user has stopped sleeping, sans any brief awakening.

  Use these macros in the Select clause of Junction Sense queries to surface consistent bedtime and wake-up timestamps across providers.

  Syntax:

  <CodeGroup>
    ```python Python DSL theme={null}
    import vitalx.aggregation as va

    va.select(
        va.Sleep.asleep_at(),
        va.Sleep.awake_at()
    )
    ```

    ```jsonc JSON DSL theme={null}
    {
        "select": [
            { "value_macro": "asleep_at" },
            { "value_macro": "awake_at" }
        ]
    }
    ```
  </CodeGroup>
</Accordion>

### Device metadata and source device tracking (August 2025)

We have added device tracking with new API endpoints and enhanced data attribution.
Eligible data resources now include `source.device_id` when the originating device can be determined, enabling better tracking and device-specific analytics.

Check out the [Device ID attribution documentation](/wearables/providers/data-attributions#device-id) on what data resources are eligible for Device ID attributions in this initial release.

<Accordion icon="devices" iconType="duotone" title="Details" defaultOpen>
  The new [Get User Devices](/api-reference/data/device/get-devices) and [Get Device Details](/api-reference/data/device/get-device) endpoints allow you to retrieve detailed device information for connected users.

  We've also added [Provider Device Created](/event-catalog/provider.device.created) and [Provider Device Updated](/event-catalog/provider.device.updated) webhook events to notify you when device information becomes available or changes.
</Accordion>

### Detecting password expiration in password-based connections (July 2025)

The [Link Password Provider endpoint](/api-reference/link/link-password-provider), the [Get User Connections endpoint](/api-reference/user/get-users-connected-providers)
and the [Provider Connection Error events](/event-catalog/provider.connection.error) now report password expiration using the `provider_password_expired` error type.

This mainly affects [Abbott LibreView patient-based connections](/wearables/guides/abbott-libreview#abbott-libreview-for-patient-based-connections).

### Introducing Universal Group By Support to Junction Sense (June 2025)

<Card horizontal icon="person-digging" color="#57164A">
  Junction Sense is in **closed beta**.

  Interested in Junction Sense? Get in touch with your Customer Success Manager.
</Card>

Junction Sense can now group and aggregate the data by any [Table Column expression](/sense/query-dsl/column-expressions#table-column-expression)
and/or any [Source Column expression](/sense/query-dsl/column-expressions#source-column-expression).

This gives you more options on how the data should be dissected — not just by calendar units, but now also by Data Source fields and categorical values from the
data itself.

<Accordion icon="link" iconType="duotone" title="Details" defaultOpen>
  Check out the [Group By clause](/sense/query-dsl/group-by-clause) documentation.

  <Note>
    If both the *Provider* and *Source Type* Source Columns are present in the [Group By clause](/sense/query-dsl/group-by-clause),
    the implicit [Data Prioritization](/sense/data-prioritization) behavior would be disabled.
  </Note>

  #### Example queries

  <CodeGroup>
    ```python Python DSL theme={null}
    # Group Summary Data by Week, Provider and Source Type

    va.select(...).group_by(
       va.date_trunc(va.Sleep.index(), 1, "week"),
       va.Source.col("source_provider"),
       va.Source.col("source_type"),
    )

    # Group Timeseries Data by Day, Provider and Source Type

    va.select(...).group_by(
       va.date_trunc(va.Timeseries.index(), 1, "day"),
       va.Source.col("source_provider"),
       va.Source.col("source_type"),
    )

    # Group Electrocardiogram Voltage Data by Hour and Lead Type

    va.select(...).group_by(
       va.date_trunc(va.Timeseries.index(), 1, "hour"),
       va.Timeseries.col("electrocardiogram_voltage").field("type"),
    )

    # Group Sleep Breathing Disturbance by Hour and Elevated vs. Not Elevated

    va.select(...).group_by(
       va.date_trunc(va.Timeseries.index(), 1, "hour"),
       va.Timeseries.col("sleep_breathing_disturbance").field("type"),
    )
    ```

    ```jsonc JSON DSL theme={null}
    # Group Summary Data by Week, Provider and Source Type

    {
        "group_by": [
            {
                "date_trunc": {
                    "value": 1,
                    "unit": "week"
                },
                "arg": { "index": "sleep" }
            },
            { "source": "source_provider" },
            { "source": "source_type" }
        ]
    }

    # Group Timeseries Data by Day, Provider and Source Type

    {
        "group_by": [
            {
                "date_trunc": {
                    "value": 1,
                    "unit": "day"
                },
                "arg": { "index": "timeseries" }
            },
            { "source": "source_provider" },
            { "source": "source_type" }
        ]
    }

    # Group Electrocardiogram Voltage Data by Hour and Lead Type

    {
        "group_by": [
            {
                "date_trunc": {
                    "value": 1,
                    "unit": "hour"
                },
                "arg": { "index": "timeseries" }
            },
            {
                "timeseries": "electrocardiogram_voltage",
                "field": "type"
            },
        ]
    }

    # Group Sleep Breathing Disturbance by Hour and Elevated vs. Not Elevated

    {
        "group_by": [
            {
                "date_trunc": {
                    "value": 1,
                    "unit": "hour"
                },
                "arg": { "index": "timeseries" }
            },
            {
                "timeseries": "sleep_breathing_disturbance",
                "field": "type"
            },
        ]
    }
    ```
  </CodeGroup>
</Accordion>

### Introducing Timeseries Data Support to Continuous Query (June 2025)

<Card horizontal icon="person-digging" color="#57164A">
  Junction Sense is in **closed beta**.

  Interested in Junction Sense? Get in touch with your Customer Success Manager.
</Card>

We have expanded Continuous Query to support **timeseries resources** in addition to the previously available summary resources.

With a few simple configuration steps, Continuous Query enables you to:

1. Extract aggregations of high‑volume timeseries data collected across all your active user device connections; and

2. Offload this concern to Junction, allowing you to simplify your data pipeline and focus on extracting actionable signals.

<Accordion icon="link" iconType="duotone" title="Details" defaultOpen>
  You can now select timeseries resources in a Query:

  * For each resource, you can select all specific fields of interest.
  * You can select multiple timeseries resources simultaneously, even though they are of different types (discrete, interval, or blood pressure).

  Check out the [Table Column expression](/sense/query-dsl/column-expressions) documentation.

  #### Examples of Timeseries Table Column expressions

  <CodeGroup>
    ```python Python DSL theme={null}
    select(
        # Blood Pressure
        Timeseries.col("blood_pressure").field("systolic"),
        Timeseries.col("blood_pressure").field("diastolic"),
        Timeseries.col("blood_pressure").field("timezone_offset"),

        # Discrete Samples
        Timeseries.col("heartrate").field("value"),
        Timeseries.col("heartrate").field("timezone_offset"),

        # Interval Samples
        Timeseries.col("body_temperature_delta").field("value"),
        Timeseries.col("body_temperature_delta").field("duration"),
        Timeseries.col("body_temperature_delta").field("sensor_location"),
        Timeseries.col("body_temperature_delta").field("timezone_offset"),

        # Workout Interval Samples
        Timeseries.col("workout_distance").field("value"),
        Timeseries.col("workout_distance").field("duration"),
        Timeseries.col("workout_distance").field("sport"),
        Timeseries.col("workout_distance").field("workout_id"),
    )
    ```

    ```jsonc JSON DSL theme={null}
    [
        # Blood Pressure
        {
          "timeseries": "blood_pressure",
          "field": "systolic"
        },
        {
          "timeseries": "blood_pressure",
          "field": "diastolic"
        },
        {
          "timeseries": "blood_pressure",
          "field": "timezone_offset"
        },
        # Discrete Samples
        {
          "timeseries": "heartrate",
          "field": "value"
        },
        {
          "timeseries": "heartrate",
          "field": "timezone_offset"
        },
        {
          "timeseries": "heartrate",
          "field": "type"
        },
        # Interval Samples
        {
          "timeseries": "body_temperature_delta",
          "field": "value"
        },
        {
          "timeseries": "body_temperature_delta",
          "field": "duration"
        },
        {
          "timeseries": "body_temperature_delta",
          "field": "sensor_location"
        },
        {
          "timeseries": "body_temperature_delta",
          "field": "timezone_offset"
        },
        # Workout Interval Samples
        {
          "timeseries": "workout_distance",
          "field": "value"
        },
        {
          "timeseries": "workout_distance",
          "field": "duration"
        },
        {
          "timeseries": "workout_distance",
          "field": "sport"
        },
        {
          "timeseries": "workout_distance",
          "field": "workout_id"
        },
    ]
    ```
  </CodeGroup>
</Accordion>

### External User ID of User Connections (April 2025)

We now expose the *External User ID* of User Connections in the [Provider Connection Created](/event-catalog/provider.connection.created) event
and [Get User Connections](/api-reference/user/get-users-connected-providers) endpoint.

The External User ID is the unique user identifier supplied by the provider, e.g., the Fitbit User ID for Fitbit connections.

| Connection type               | Content                                           |
| ----------------------------- | ------------------------------------------------- |
| OAuth Providers               | User unique identifier; provider-specific formats |
| Password Providers            | Username                                          |
| Email Providers               | Email                                             |
| Junction Mobile SDK Providers | null (not available at this time)                 |

<Accordion icon="link" iconType="duotone" title="Details" defaultOpen>
  Check out the [Provider Connection Created](/event-catalog/provider.connection.created) event and
  [Get User Connections](/api-reference/user/get-users-connected-providers) endpoint documentation.
</Accordion>

### Link API Improvements (Jan 2025)

We have increased Link Token expiry from 15 minutes to 60 minutes.

We have also added an `on_error=redirect` Link Token option, which forces any error scenario to redirect to your specified `redirect_url`, disabling any in-built error handling behavior in the Link Widget.

<Accordion icon="link" iconType="duotone" title="Details" defaultOpen>
  Check out the [Link Error](/wearables/connecting-providers/errors) format and the [Generate a Link Token](/api-reference/link/generate-link-token) endpoint documentation.
</Accordion>

### Extendable Historical Date Ranges (Jul 2024)

We now offer the ability to set custom historical data ranges for wearables providers, with resource-level granularity.

<Accordion icon="lightbulb" iconType="duotone" title="Details" defaultOpen>
  You can now extend the historical pull range for a number of supported providers using the [Org Management API](/api-reference/org-management/team-data-pull-preferences/upsert-team-data-pull-preferences).

  <Info>
    Org Management API is available for [the Scale plan](https://tryvital.io/pricing).
  </Info>

  Check out the [Set Data Pull Preferences](/api-reference/org-management/team-data-pull-preferences/upsert-team-data-pull-preferences),
  [Get Data Pull Preferences](/api-reference/org-management/team-data-pull-preferences/get-team-data-pull-preferences), [Delete Data Pull Preferences](/api-reference/org-management/team-data-pull-preferences/delete-team-data-pull-preferences) endpoint documentation for more details.
</Accordion>

### Introspection: Historical Pull Timeline (May 2024)

The Historical Pull Introspection endpoint now reports the execution timeline of historical pulls.

<Accordion icon="lightbulb" iconType="duotone" title="Details" defaultOpen>
  The timeline tracks when the historical pull was scheduled, started, and eventually ended (success or failure). This
  improves the visibility around the latency of historical pulls, as well as providing more indicators of an incomplete
  execution.

  Check out the [Historical Pull Introspection](/api-reference/data/introspection/historical-pulls) endpoint documentation.
</Accordion>

### Team Scope Requirements (Apr 2024)

You can now tailor what scopes Junction would request from your Users using the [Org Management API](/api-reference/org-management/team-scope-requirements/upsert-team-scope-requirements).
This applies when your user connects to an [OAuth provider](/wearables/connecting-providers/auth_types) which supports scopes.

<Accordion icon="lightbulb" iconType="duotone" title="Details" defaultOpen>
  Junction ensures that any new provider connection can be established only when the user has granted all the scopes specified in the `user_must_grant` scope list.

  The scopes specified as `user_may_grant` would be requested alongside the `user_must_grant` scopes. However, unlike `user_must_grant`, they do not prevent the connection from being established.

  <Info>
    Org Management API is available for [the Scale plan](https://tryvital.io/pricing).
  </Info>

  Check out the [Set Team Scope Requirements](/api-reference/org-management/team-scope-requirements/upsert-team-scope-requirements) and
  [Get Team Scope Requirements](/api-reference/org-management/team-scope-requirements/get-team-scope-requirements) endpoint documentation on how to
  enable the setting.
</Accordion>

### Reject Duplicate Connections (Apr 2024)

You can now configure your Junction Team through the [Org Management API](/api-reference/org-management/team/update-team)
to reject duplicate wearable connections.

<Accordion icon="person-military-pointing" iconType="duotone" title="Details" defaultOpen>
  When the `reject_duplicate_connection` setting is enabled on the Team, Junction checks whether or
  not the provider-reported user ID is already connected to an existing User in your Team.

  If it does, the Link API would report the [`duplicate_connection` error](/wearables/connecting-providers/errors).

  <Info>
    Org Management API is available for [the Scale plan](https://tryvital.io/pricing).
  </Info>

  Check out the [Update Team](/api-reference/org-management/team/update-team) and
  [Create Team](/api-reference/org-management/team/create-team) endpoint documentation on how to
  enable the setting.

  Check out the [Link Errors](/wearables/connecting-providers/errors) documentation on how to
  catch the `duplicate_connection` error.
</Accordion>

### Link Error reporting (Apr 2024)

The Link API now reports errors in terms of a predefined set of [Error Types](/wearables/connecting-providers/errors)
on which your application logic can depend.

<Accordion icon="circle-exclamation" iconType="duotone" title="Details" defaultOpen>
  We introduced this because there has not been a dependable way for your application logic
  to understand why a connection attempt has failed, and in turn this prevents your application
  from providing actionable messages to your end users.

  Depending on how you initiate the Link flow, the Link Error would be reported either as a
  URL query parameter, or as part of the JSON response.

  Check out the [Link Errors](/wearables/connecting-providers/errors) documentation for the detailed guidance.
</Accordion>

### Understanding Resource Availability (Apr 2024)

When a user connection to a provider is established, the webhook event now includes a resource availability
report of the connection.

<Accordion icon="signal-bars" iconType="duotone" title="Details" defaultOpen>
  We introduced this because it helps you understand what resources would and would not be available on
  a new connection. We also provide insights into how partial consents from users during the OAuth authentication
  flow can influence the resource availability, so that you can take actions accordingly.

  This resource availability report is based on the permissions *(also known as API access scopes)* the user has
  granted during the authentication process.

  In some cases, a provider resource may be available, but some information could be absent due to
  some optional scopes having been denied by the user. The availability report includes a full breakdown
  of granted and denied scopes by their optionality.

  <Note>
    If the provider has no concept of API access scopes, we report all resources as available.
  </Note>

  You can also query this information at any time through the
  [Get User Connections](/api-reference/user/get-users-connected-providers) endpoint.

  Check out the [Provider Connection Created](/event-catalog/provider.connection.created)
  (`provider.connection.created`) event schema and the [Get User Connections](/api-reference/user/get-users-connected-providers) endpoint documentation.
</Accordion>

### Fallback Birth Date for Heart Rate Zones (Feb 2024)

You can now set a Fallback Birth Date on a user.

<Accordion icon="cake-candles" iconType="duotone" title="Details" defaultOpen>
  Junction can use this to compute more accurate workout Heart Rate Zones, when the provider
  exposes neither heart rate zones nor user age to Junction.

  Check out the [Heart Rate Zones](/wearables/providers/heart-rate-zones#fallback-birth-date) documentation.
</Accordion>

### Grouped Timeseries (Feb 2024)

You can now get grouped timeseries data.

<Accordion icon="chart-scatter" iconType="duotone" title="Details" defaultOpen>
  This initial release groups data by [Source Type](/wearables/providers/data-attributions#source-type)
  from [supported providers](/wearables/providers/data-attributions#supported-providers).

  Check out the [Blood Oxygen](/api-reference/data/timeseries/blood-oxygen) endpoint documentation for an example.
</Accordion>

### Historical Pull Introspection (Dec 2023)

You can now introspect the status of all one-off user historical data pulls.

<Accordion icon="binoculars" iconType="duotone" title="Details" defaultOpen>
  It also provides the pulled date-time range, as well as a rough estimate of the amount of
  data ingested (in terms of "days with data").

  Check out the [Historical Pull Introspection](/api-reference/data/introspection/historical-pulls) endpoint documentation.
</Accordion>

### User Resources Introspection (Dec 2023)

You can now introspect user data ingestion statistics.

<Accordion icon="binoculars" iconType="duotone" title="Details" defaultOpen>
  For example, the endpoint provides:

  1. Oldest and newest data timestamp
  2. The number of objects sent in `*.created` events
  3. The status and time of the last ingestion attempt (polling or push)

  Check out the [User Resources Introspection](/api-reference/data/introspection/user-resources) endpoint documentation.
</Accordion>

### Junction Sign-In Token for Mobile SDKs (Nov 2023)

Junction Sign-In Token is a new, user-scoped Authentication scheme for Junction Mobile SDKs.

<Accordion icon="id-badge" iconType="duotone" title="Details" defaultOpen>
  It grants only user-scoped access to your mobile app sign-ins. This allows you to keep your Junction Team API Keys
  as server-side secrets.

  We encourage all customers using Junction Team API Keys in their production mobile apps to
  migrate to the Junction Sign-In Token scheme.

  Check out the [SDK Authentication](/wearables/sdks/authentication#junction-sign-in-token) guide on how to migrate to
  this scheme. Check out the [Create Sign-In Token](/api-reference/user/create-sign-in-token) endpoint documentation
  on how to generate tokens for your mobile app sign-ins.
</Accordion>
