# Junction API
Source: https://docs.junction.com/api-details/junction-api

Overview of the Junction API including production and sandbox environments, regional endpoints, authentication methods, and core API capabilities.

You use Junction API to:

* manage users and demographic information in your Team with the [Core API](/api-reference/user/create-user);
* manage user connections, pull ingested device data, and inspect connection backfill statuses through the [Devices API](/wearables/connecting-providers/introduction);
* order lab tests, manage appointments and pull the results through the [Lab Testing API](/lab/overview/introduction); and
* aggregate ingested device data with the [Junction Sense API](/sense/overview).

<Info>
  For managing Team configuration and Org resources programmatically, see [Junction Management API](/api-details/junction-management-api).
</Info>

## Environments

Junction provides two Sandbox environments and two Production environments today:

| Environment        | Base URL                               | Team API Key Prefix |
| ------------------ | -------------------------------------- | ------------------- |
| 🇺🇸 Production US | `https://api.us.junction.com/`         | `pk_us_*`           |
| 🇪🇺 Production EU | `https://api.eu.junction.com/`         | `pk_eu_*`           |
| 🇺🇸 Sandbox US    | `https://api.sandbox.us.junction.com/` | `sk_us_*`           |
| 🇪🇺 Sandbox EU    | `https://api.sandbox.eu.junction.com/` | `sk_eu_*`           |

<Note>
  If you are using a `*.tryvital.io` Base URL, they are still supported alongside `*.junction.com`.
</Note>

Sandbox environments are functionally identical to Production environments, except that:

1. Each Team can have only up to 50 Users in Sandbox;
2. You can create [Synthetic Device connections](/wearables/providers/test_data); and
3. You can [simulate and transition an order through its lifecycle](/lab/overview/sandbox).

## Authentication

Junction API accepts two team-scoped credential types — pick one per request.

| Credential                                                                                                | Lifetime                 | Header                          | Provisioned via                                                                                                                                                  |
| --------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Team API Key](/api-reference/org-management/team-api-keys/create-team-api-key)                           | Long-lived until revoked | `X-Vital-API-Key: <key>`        | Team Config page in the [Junction Dashboard](https://app.junction.com) or [Create Team API Key](/api-reference/org-management/team-api-keys/create-team-api-key) |
| [Team API Access Token](/api-reference/org-management/team-api-access-token/create-team-api-access-token) | Several minutes          | `Authorization: Bearer <token>` | Created programmatically via [Create Team API Access Token](/api-reference/org-management/team-api-access-token/create-team-api-access-token)                    |

### When to use a Team API Key

Choose a Team API Key when your system can safely store a pre-shared secret and you intend to issue separate team-scoped keys to your own divisions or environments. Keys are listed and rotated from the Team Config page in the [Junction Dashboard](https://app.junction.com), or programmatically through the Management API via [Create Team API Key](/api-reference/org-management/team-api-keys/create-team-api-key).

```bash cURL theme={null}
curl --request GET \
  --url 'https://api.us.junction.com/v2/providers' \
  --header 'X-Vital-API-Key: <YOUR-TEAM-API-KEY>'
```

### When to use a Team API Access Token

Choose a Team API Access Token when you are integrating Junction into a multi-tenant EMR or similar platform and need to act on behalf of many customer teams without provisioning and storing a Team API Key for each one. Mint a token on demand from your backend by calling the Management API endpoint [Create Team API Access Token](/api-reference/org-management/team-api-access-token/create-team-api-access-token) with your Management Key, then call Junction API with the returned token.

```bash cURL theme={null}
curl --request GET \
  --url 'https://api.us.junction.com/v2/providers' \
  --header 'Authorization: Bearer <YOUR-TEAM-API-ACCESS-TOKEN>'
```

<Warning>
  Tokens are short-lived (several minutes) and the Create Team API Access Token endpoint is rate limited. Cache and reuse each token until it expires rather than minting per request. When a Junction API call returns `401 Unauthorized`, mint a fresh token by calling the endpoint again, then retry the request.
</Warning>

<Note>
  A [Management Key](/api-details/junction-management-api) used by Junction Management API does not function as a Team API Key.
  However, you can provision Team API Keys and mint Team API Access Tokens through a Management Key.
</Note>

## Junction Mobile SDKs

[Junction Mobile SDKs](/wearables/sdks/) support two authentication methods:

| Scheme                                                                 | Authorization         | Recommended use case   |
| ---------------------------------------------------------------------- | --------------------- | ---------------------- |
| [Sign-In Token](/wearables/sdks/authentication#junction-sign-in-token) | User-scoped access    | Production mobile apps |
| [Team API Keys](/wearables/sdks/authentication#junction-team-api-keys) | Full Team data access | Proof-of-concept       |

Refer to the [Mobile SDK Authentication](/wearables/sdks/authentication) documentation for more information.


# Junction Management API
Source: https://docs.junction.com/api-details/junction-management-api

Use the Junction Management API to programmatically manage organization resources like teams, members, API keys, and ETL pipelines.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

You use Junction Management API to:

* manage Junction Org resources, such as [Teams](/api-reference/org-management/team/create-team) and [Members](/api-reference/org-management/member/list-members);
* manage Team configurations across both Sandbox and Production environments, such as:
  * [Team features and brand information](/api-reference/org-management/team/update-team);
  * [Team ETL Pipelines](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines);
  * [Team Custom Credentials](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials); and
  * [Team Continuous Query](/api-reference/sense/continuous-query/create);
* integrate Junction Dashboard experiences into your web application with [Junction Connect](/connect/overview) — see [Junction Connect in the Management API Reference](/api-reference/org-management/connect/junction-connect) for the endpoints involved.

Note that you can access a lot of Junction Management API functionalities through the [Junction Dashboard](https://app.junction.com/) as well.

<Info>
  For device and lab testing access, see [Junction API](/api-details/junction-api).
</Info>

## Environments

There is one production environment for Junction Management API:

| Environment   | Base URL                               | Management Key Prefix |
| ------------- | -------------------------------------- | --------------------- |
| 🤖 Production | `https://api.management.junction.com/` | `mg_*`                |

## Authentication

Junction Management API accepts a **Management Key**. Enterprise customers can request a Management Key
through your Junction Customer Success Manager.

Your API requests should present the Management Key under the `X-Management-Key` header. For example:

```bash cURL theme={null}
curl --request GET \
  --url "https://api.management.junction.com/v1/org/${ORG_ID}" \
  --header 'X-Management-Key: ${YOUR_MANAGEMENT_KEY}'
```

<Note>
  A [Team API Key](/api-details/junction-api#authentication) does not work with Junction Management API.

  Junction enforces the use of separate API credentials based on the principle of least privilege, especially
  considering the administrative power of Junction Management API.
</Note>


# Rate Limiting
Source: https://docs.junction.com/api-details/rate_limiting

Learn about Junction API rate limiting behavior, HTTP 429 and 503 responses under server load, and targeted limits on specific endpoints.

Junction does not impose a per-customer rate limit on server-to-server API calls at this time. We adjust the
elastic capacity and overprovisioning rate of our API servers regularly, in response to your usage pattern changes
and product launch plans.

However, when Junction API servers are under stress, there is a possibility that our infrastructure may abort a number
of your API requests with a **429 Too Many Requests** or **503 Service Unavailable** response.

For idempotent API requests, consider retrying the API request at least once, upon receiving a 429 or 503 response. Optionally,
if you are making the API request in an asynchronous job context, consider having more retry attempts with an
exponential backoff strategy.

## Targeted rate limits

You may encounter **429 Too Many Requests** in these specific scenarios:

| Endpoint                       | Authentication Scheme | Rate Limit          |
| ------------------------------ | --------------------- | ------------------- |
| `POST /user/refresh/{user_id}` | Junction API Key      | 8 per hour per user |

<Note>
  Junction may adjust rate limiting based on real-world usage patterns. We will give you sufficient notice
  if a new enforcement would impact your existing usage patterns.
</Note>


# Regions
Source: https://docs.junction.com/api-details/regions

Understand how Junction stores data regionally in either the US (HIPAA compliant) or EU (GDPR compliant), and how organizations span multiple regions.

Junction Team is a **regional** resource. All data of the Team is stored in the region you selected. This
cannot be changed afterwards. Make sure you choose a region that is suitable for your data residency requirements.

| Regions | Data Residency                                  |
| ------- | ----------------------------------------------- |
| 🇺🇸 US | Data stored in United States (Subject to HIPAA) |
| 🇪🇺 EU | Data stored in Belgium (Subject to GDPR)        |

Your Junction Org is a **global** resource — you can start with a Team in one region and create more in a different
region as you expand.


# Raw
Source: https://docs.junction.com/api-reference/data/activity/get-raw

GET /v2/summary/activity/{user_id}/raw
Retrieve raw activity data for a specific user as received from their connected wearable provider.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/activity/{user_id}/raw \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.activity.get_raw(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.activity.getRaw({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.activity.requests.GetRawActivityRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.activity().getRaw(
      "<user_id>",
      GetRawActivityRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Activity.GetRaw(context.TODO(), &junction.GetRawActivityRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/activity/get-summary

GET /v2/summary/activity/{user_id}
Retrieve processed activity summary data for a specific user, including steps, calories, and distance metrics.

<RequestExample>
  ```bash Shell theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/activity/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.activity.get(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.activity.get({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.activity.requests.GetActivityRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.activity().get(
      "<user_id>",
      GetActivityRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Activity.Get(context.TODO(), &junction.GetActivityRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Raw
Source: https://docs.junction.com/api-reference/data/body/get-raw

GET /v2/summary/body/{user_id}/raw
Retrieve raw body composition data for a specific user as received from their connected wearable provider.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/body/{user_id}/raw \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.body.get_raw(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.body.getRaw({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.body.requests.GetRawBodyRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.body().getRaw(
      "<user_id>",
      GetRawBodyRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Body.GetRaw(context.TODO(), &junction.GetRawBodyRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/body/get-summary

GET /v2/summary/body/{user_id}
Retrieve processed body summary data for a specific user, including weight, body fat, and BMI measurements.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/body/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.body.get(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.body.get({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.body.requests.GetBodyRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.body().get(
      "<user_id>",
      GetBodyRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Body.Get(context.TODO(), &junction.GetBodyRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get User Device
Source: https://docs.junction.com/api-reference/data/device/get-device

GET /v2/user/{user_id}/device/{device_id}
Retrieve user device via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash Shell theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/{user_id}/device/{device_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_device("<user_id>", "<device_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getDevice({
      userId: "<user_id>",
      deviceId: "<device_id>",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getDevice("<user_id>", "<device_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetDevice(context.TODO(), &junction.GetDeviceUserRequest{
      UserId:   "<user_id>",
      DeviceId: "<device_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get User Devices
Source: https://docs.junction.com/api-reference/data/device/get-devices

GET /v2/user/{user_id}/device
Retrieve user devices via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash Shell theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/{user_id}/device \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_devices("<user_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getDevices({ userId: "<user_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getDevices("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetDevices(context.TODO(), &junction.GetDevicesUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/electrocardiogram/get-summary

GET /v2/summary/electrocardiogram/{user_id}
Retrieve electrocardiogram summary data for a specific user from their connected wearable provider.

<Note>
  The Electrocardiogram summary type does not embed the voltage measurements.

  Use the [Get Electrocardiogram Voltage](/api-reference/data/timeseries/electrocardiogram-voltage) endpoint
  with the `session_start` and `session_end` timestamps to query the voltage measurements.
</Note>

<RequestExample>
  ```bash Shell theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/electrocardiogram/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.electrocardiogram.get(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.electrocardiogram.get({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.electrocardiogram.requests.GetElectrocardiogramRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.electrocardiogram().get(
      "<user_id>",
      GetElectrocardiogramRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Electrocardiogram.Get(context.TODO(), &junction.GetElectrocardiogramRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get historical pulls
Source: https://docs.junction.com/api-reference/data/introspection/historical-pulls

GET /v2/introspect/historical_pull
List historical data pull records to track the progress and status of initial data backfills for connected providers.

## Overview

Diagnose user historical data unavailability through the User Resources Introspection API.

This API offers detailed insights into all provider connections and collected historical resources of all users in your team,
empowering you to perform an initial diagnosis of any user connection issue with ease.

<Warning>
  Please note that `user_limit` is an upper bound and the endpoint can return data for fewer users.
  This would be the case if any of the top selected users had no available resource information.
</Warning>


# Get user resources
Source: https://docs.junction.com/api-reference/data/introspection/user-resources

GET /v2/introspect/resources
List available data resources for the authenticated user, showing which providers and data types are accessible.

## Overview

Diagnose user data unavailability through the User Resources Introspection API.

This API offers detailed insights into all provider connections and collected resources of all users in your team, empowering you to perform an initial diagnosis of any user connection issue with ease.

<Warning>
  Please note that `user_limit` is an upper bound and the endpoint can return data for fewer users.
  This would be the case if any of the top selected users had no available resource information.
</Warning>


# Summary
Source: https://docs.junction.com/api-reference/data/meal/get-summary

GET /v2/summary/meal/{user_id}
Retrieve meal and nutrition summary data for a specific user from their connected tracking provider.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/meal/{user_id}?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.meal.get(
      "<user_id>",
      start_date="2021-01-01",
      end_date="2021-01-02",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.meal.get({
      userId: "<user_id>",
      startDate: "2021-01-01",
      endDate: "2021-01-02",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.meal.requests.GetMealRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.meal().get(
      "<user_id>",
      GetMealRequest.builder()
          .startDate("2021-01-01")
          .endDate("2021-01-02")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2021-01-02"
  response, err := c.Meal.Get(context.TODO(), &junction.GetMealRequest{
      UserId:    "<user_id>",
      StartDate: "2021-01-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/menstrual-cycle/get-summary

GET /v2/summary/menstrual_cycle/{user_id}
Retrieve menstrual cycle tracking summary data for a specific user from their connected wearable provider.

<RequestExample>
  ```bash Shell theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/menstrual_cycle/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.menstrual_cycle.get(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.menstrualCycle.get({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.menstrualcycle.requests.GetMenstrualCycleRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.menstrualCycle().get(
      "<user_id>",
      GetMenstrualCycleRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.MenstrualCycle.Get(context.TODO(), &junction.GetMenstrualCycleRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Raw
Source: https://docs.junction.com/api-reference/data/profile/get-raw

GET /v2/summary/profile/{user_id}/raw
Retrieve raw user profile data as received from their connected wearable provider without processing.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/profile/{user_id}/raw \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.profile.get_raw("<user_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.profile.getRaw({ userId: "<user_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.profile().getRaw("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Profile.GetRaw(context.TODO(), &junction.GetRawProfileRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/profile/get-summary

GET /v2/summary/profile/{user_id}
Retrieve the processed user profile data from their connected wearable provider, including demographics and preferences.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/profile/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.profile.get("<user_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.profile.get({ userId: "<user_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.profile().get("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Profile.Get(context.TODO(), &junction.GetProfileRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/sleep-cycle/get-summary

GET /v2/summary/sleep_cycle/{user_id}
Retrieve sleep cycle analysis data for a specific user, including sleep stage breakdowns and cycle metrics.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/sleep_cycle/{user_id}?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.sleep_cycle.get(
      "<user_id>",
      start_date="2021-01-01",
      end_date="2021-01-02",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.sleepCycle.get({
      userId: "<user_id>",
      startDate: "2021-01-01",
      endDate: "2021-01-02",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.sleepcycle.requests.GetSleepCycleRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.sleepCycle().get(
      "<user_id>",
      GetSleepCycleRequest.builder()
          .startDate("2021-01-01")
          .endDate("2021-01-02")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2021-01-02"
  response, err := c.SleepCycle.Get(context.TODO(), &junction.GetSleepCycleRequest{
      UserId:    "<user_id>",
      StartDate: "2021-01-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Raw
Source: https://docs.junction.com/api-reference/data/sleep/get-raw

GET /v2/summary/sleep/{user_id}/raw
Retrieve raw sleep data for a specific user as received from their connected wearable provider.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/sleep/{user_id}/raw \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.sleep.get_raw(
      "<user_id>",
      start_date="2021-01-01",
      end_date="2021-01-02",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.sleep.getRaw({
      userId: "<user_id>",
      startDate: "2021-01-01",
      endDate: "2021-01-02",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.sleep.requests.GetRawSleepRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.sleep().getRaw(
      "<user_id>",
      GetRawSleepRequest.builder()
          .startDate("2021-01-01")
          .endDate("2021-01-02")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2021-01-02"
  response, err := c.Sleep.GetRaw(context.TODO(), &junction.GetRawSleepRequest{
      UserId:    "<user_id>",
      StartDate: "2021-01-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/sleep/get-summary

GET /v2/summary/sleep/{user_id}
Retrieve processed sleep summary data for a specific user, including duration, efficiency, and quality scores.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/sleep/{user_id}?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.sleep.get(
      "<user_id>",
      start_date="2021-01-01",
      end_date="2021-01-02",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.sleep.get({
      userId: "<user_id>",
      startDate: "2021-01-01",
      endDate: "2021-01-02",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.sleep.requests.GetSleepRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.sleep().get(
      "<user_id>",
      GetSleepRequest.builder()
          .startDate("2021-01-01")
          .endDate("2021-01-02")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2021-01-02"
  response, err := c.Sleep.Get(context.TODO(), &junction.GetSleepRequest{
      UserId:    "<user_id>",
      StartDate: "2021-01-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Afib Burden
Source: https://docs.junction.com/api-reference/data/timeseries/afib-burden

GET /v2/timeseries/{user_id}/afib_burden/grouped
Retrieve grouped atrial fibrillation burden time series data for a user, showing the percentage of time spent in AFib from connected wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_afib_burden_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/afib_burden/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.afib_burden_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsAfibBurdenGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsAfibBurdenGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.afibBurdenGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsAfibBurdenGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsAfibBurdenGroupedRequest request = VitalsAfibBurdenGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().afibBurdenGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsAfibBurdenGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.AfibBurdenGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:30:52+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "%",
              "value": 3
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Basal Body Temperature
Source: https://docs.junction.com/api-reference/data/timeseries/basal-body-temperature

GET /v2/timeseries/{user_id}/basal_body_temperature/grouped
Retrieve grouped basal body temperature time series data for a user, captured from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_basal_body_temperature_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/basal_body_temperature/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.basal_body_temperature_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBasalBodyTemperatureGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBasalBodyTemperatureGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.basalBodyTemperatureGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBasalBodyTemperatureGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBasalBodyTemperatureGroupedRequest request = VitalsBasalBodyTemperatureGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().basalBodyTemperatureGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBasalBodyTemperatureGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BasalBodyTemperatureGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "\u00b0C",
              "value": 36.7
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Blood Oxygen
Source: https://docs.junction.com/api-reference/data/timeseries/blood-oxygen

GET /v2/timeseries/{user_id}/blood_oxygen/grouped
Retrieve grouped blood oxygen saturation (SpO2) time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_blood_oxygen_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/blood_oxygen/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.blood_oxygen_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBloodOxygenGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBloodOxygenGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bloodOxygenGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBloodOxygenGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBloodOxygenGroupedRequest request = VitalsBloodOxygenGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bloodOxygenGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBloodOxygenGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BloodOxygenGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "%",
              "value": 98
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Blood Pressure
Source: https://docs.junction.com/api-reference/data/timeseries/blood-pressure

GET /v2/timeseries/{user_id}/blood_pressure/grouped
Retrieve grouped systolic and diastolic blood pressure time series readings for a user from connected wearables and health devices.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_blood_pressure_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/blood_pressure/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.blood_pressure_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBloodPressureGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBloodPressureGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bloodPressureGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBloodPressureGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBloodPressureGroupedRequest request = VitalsBloodPressureGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bloodPressureGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBloodPressureGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BloodPressureGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "diastolic": 75,
              "systolic": 125,
              "timestamp": "2026-08-14T07:32:31+00:00",
              "unit": "mmHg"
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Body Fat
Source: https://docs.junction.com/api-reference/data/timeseries/body-fat

GET /v2/timeseries/{user_id}/body_fat/grouped
Retrieve grouped body fat percentage time series data for a user from connected wearables, smart scales, and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_body_fat_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/body_fat/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.body_fat_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBodyFatGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBodyFatGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bodyFatGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBodyFatGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBodyFatGroupedRequest request = VitalsBodyFatGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bodyFatGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBodyFatGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BodyFatGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "%",
              "value": 50
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Body Mass Index
Source: https://docs.junction.com/api-reference/data/timeseries/body-mass-index

GET /v2/timeseries/{user_id}/body_mass_index/grouped
Retrieve grouped body mass index (BMI) time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_body_mass_index_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/body_mass_index/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.body_mass_index_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBodyMassIndexGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBodyMassIndexGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bodyMassIndexGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBodyMassIndexGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBodyMassIndexGroupedRequest request = VitalsBodyMassIndexGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bodyMassIndexGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBodyMassIndexGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BodyMassIndexGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "index",
              "value": 21
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Body Temperature
Source: https://docs.junction.com/api-reference/data/timeseries/body-temperature

GET /v2/timeseries/{user_id}/body_temperature/grouped
Retrieve grouped body temperature time series readings for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_body_temperature_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/body_temperature/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.body_temperature_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBodyTemperatureGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBodyTemperatureGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bodyTemperatureGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBodyTemperatureGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBodyTemperatureGroupedRequest request = VitalsBodyTemperatureGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bodyTemperatureGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBodyTemperatureGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BodyTemperatureGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:30:52+00:00",
              "sensor_location": "eardrum",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "\u00b0C",
              "value": 65
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Body Temperature Delta
Source: https://docs.junction.com/api-reference/data/timeseries/body-temperature-delta

GET /v2/timeseries/{user_id}/body_temperature_delta/grouped
Retrieve grouped body temperature delta time series data for a user, showing deviations from baseline captured by connected wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_body_temperature_delta_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/body_temperature_delta/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.body_temperature_delta_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBodyTemperatureDeltaGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBodyTemperatureDeltaGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bodyTemperatureDeltaGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBodyTemperatureDeltaGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBodyTemperatureDeltaGroupedRequest request = VitalsBodyTemperatureDeltaGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bodyTemperatureDeltaGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBodyTemperatureDeltaGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BodyTemperatureDeltaGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "sensor_location": "wrist",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "\u00b0C",
              "value": -1.0
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Body Weight
Source: https://docs.junction.com/api-reference/data/timeseries/body-weight

GET /v2/timeseries/{user_id}/body_weight/grouped
Retrieve grouped body weight time series data for a user from connected wearables, smart scales, and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_body_weight_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/body_weight/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.body_weight_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsBodyWeightGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsBodyWeightGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.bodyWeightGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsBodyWeightGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsBodyWeightGroupedRequest request = VitalsBodyWeightGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().bodyWeightGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsBodyWeightGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.BodyWeightGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "kg",
              "value": 65
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Caffeine
Source: https://docs.junction.com/api-reference/data/timeseries/caffeine

GET /v2/timeseries/{user_id}/caffeine/grouped
Retrieve grouped caffeine intake time series data logged by a user through connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_caffeine_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/caffeine/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.caffeine_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsCaffeineGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsCaffeineGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.caffeineGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsCaffeineGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsCaffeineGroupedRequest request = VitalsCaffeineGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().caffeineGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsCaffeineGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.CaffeineGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "g",
              "value": 42
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Calories Active
Source: https://docs.junction.com/api-reference/data/timeseries/calories-active

GET /v2/timeseries/{user_id}/calories_active/grouped
Retrieve grouped active calorie expenditure time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_calories_active_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/calories_active/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.calories_active_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsCaloriesActiveGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsCaloriesActiveGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.caloriesActiveGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsCaloriesActiveGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsCaloriesActiveGroupedRequest request = VitalsCaloriesActiveGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().caloriesActiveGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsCaloriesActiveGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.CaloriesActiveGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "kcal",
              "value": 184
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Calories Basal
Source: https://docs.junction.com/api-reference/data/timeseries/calories-basal

GET /v2/timeseries/{user_id}/calories_basal/grouped
Retrieve grouped basal calorie expenditure time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_calories_basal_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/calories_basal/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.calories_basal_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsCaloriesBasalGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsCaloriesBasalGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.caloriesBasalGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsCaloriesBasalGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsCaloriesBasalGroupedRequest request = VitalsCaloriesBasalGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().caloriesBasalGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsCaloriesBasalGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.CaloriesBasalGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "kcal",
              "value": 22.8
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Carbohydrates
Source: https://docs.junction.com/api-reference/data/timeseries/carbohydrates

GET /v2/timeseries/{user_id}/carbohydrates/grouped
Retrieve grouped carbohydrate intake time series data logged by a user through connected nutrition apps and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_carbohydrates_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/carbohydrates/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.carbohydrates_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsCarbohydratesGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsCarbohydratesGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.carbohydratesGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsCarbohydratesGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsCarbohydratesGroupedRequest request = VitalsCarbohydratesGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().carbohydratesGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsCarbohydratesGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.CarbohydratesGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:30:52+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "g",
              "value": 30
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Daylight Exposure
Source: https://docs.junction.com/api-reference/data/timeseries/daylight-exposure

GET /v2/timeseries/{user_id}/daylight_exposure/grouped
Retrieve grouped daylight exposure time series data for a user, measuring time spent in outdoor light from connected wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_daylight_exposure_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/daylight_exposure/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.daylight_exposure_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsDaylightExposureGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsDaylightExposureGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.daylightExposureGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsDaylightExposureGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsDaylightExposureGroupedRequest request = VitalsDaylightExposureGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().daylightExposureGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsDaylightExposureGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.DaylightExposureGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "min",
              "value": 45
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Distance
Source: https://docs.junction.com/api-reference/data/timeseries/distance

GET /v2/timeseries/{user_id}/distance/grouped
Retrieve grouped distance traveled time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_distance_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/distance/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.distance_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsDistanceGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsDistanceGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.distanceGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsDistanceGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsDistanceGroupedRequest request = VitalsDistanceGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().distanceGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsDistanceGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.DistanceGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "m",
              "value": 5.6
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Electrocardiogram Voltage
Source: https://docs.junction.com/api-reference/data/timeseries/electrocardiogram-voltage

GET /v2/timeseries/{user_id}/electrocardiogram_voltage/grouped
Retrieve grouped electrocardiogram (ECG) voltage time series readings for a user from connected wearables that support ECG recording.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_electrocardiogram_voltage_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/electrocardiogram_voltage/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.electrocardiogram_voltage_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsElectrocardiogramVoltageGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsElectrocardiogramVoltageGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.electrocardiogramVoltageGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsElectrocardiogramVoltageGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsElectrocardiogramVoltageGroupedRequest request = VitalsElectrocardiogramVoltageGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().electrocardiogramVoltageGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsElectrocardiogramVoltageGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.ElectrocardiogramVoltageGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "type": "lead_1",
              "unit": "mV",
              "value": -373
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Fall
Source: https://docs.junction.com/api-reference/data/timeseries/fall

GET /v2/timeseries/{user_id}/fall/grouped
Retrieve grouped fall detection time series events for a user, captured by connected wearables with fall detection enabled.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_fall_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/fall/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.fall_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsFallGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsFallGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.fallGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsFallGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsFallGroupedRequest request = VitalsFallGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().fallGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsFallGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.FallGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 3
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Floors Climbed
Source: https://docs.junction.com/api-reference/data/timeseries/floors-climbed

GET /v2/timeseries/{user_id}/floors_climbed/grouped
Retrieve grouped floors climbed time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_floors_climbed_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/floors_climbed/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.floors_climbed_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsFloorsClimbedGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsFloorsClimbedGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.floorsClimbedGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsFloorsClimbedGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsFloorsClimbedGroupedRequest request = VitalsFloorsClimbedGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().floorsClimbedGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsFloorsClimbedGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.FloorsClimbedGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 2
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Forced Expiratory Volume 1
Source: https://docs.junction.com/api-reference/data/timeseries/forced-expiratory-volume-1

GET /v2/timeseries/{user_id}/forced_expiratory_volume_1/grouped
Retrieve grouped forced expiratory volume in one second (FEV1) time series readings for a user from connected respiratory devices.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_forced_expiratory_volume_1_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/forced_expiratory_volume_1/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.forced_expiratory_volume_1_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsForcedExpiratoryVolume1GroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsForcedExpiratoryVolume1GroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.forcedExpiratoryVolume1Grouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsForcedExpiratoryVolume1GroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsForcedExpiratoryVolume1GroupedRequest request = VitalsForcedExpiratoryVolume1GroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().forcedExpiratoryVolume1Grouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsForcedExpiratoryVolume1GroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.ForcedExpiratoryVolume1Grouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "L",
              "value": 3.5
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Forced Vital Capacity
Source: https://docs.junction.com/api-reference/data/timeseries/forced-vital-capacity

GET /v2/timeseries/{user_id}/forced_vital_capacity/grouped
Retrieve grouped forced vital capacity (FVC) time series readings for a user from connected respiratory devices and health sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_forced_vital_capacity_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/forced_vital_capacity/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.forced_vital_capacity_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsForcedVitalCapacityGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsForcedVitalCapacityGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.forcedVitalCapacityGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsForcedVitalCapacityGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsForcedVitalCapacityGroupedRequest request = VitalsForcedVitalCapacityGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().forcedVitalCapacityGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsForcedVitalCapacityGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.ForcedVitalCapacityGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "L",
              "value": 4.2
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Glucose
Source: https://docs.junction.com/api-reference/data/timeseries/glucose

GET /v2/timeseries/{user_id}/glucose/grouped
Retrieve grouped blood glucose time series readings for a user from connected continuous glucose monitors and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_glucose_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/glucose/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.glucose_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsGlucoseGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsGlucoseGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.glucoseGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsGlucoseGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsGlucoseGroupedRequest request = VitalsGlucoseGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().glucoseGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsGlucoseGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.GlucoseGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "type": "automatic | manual_scan",
              "unit": "mmol/L",
              "value": 0.5
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Handwashing
Source: https://docs.junction.com/api-reference/data/timeseries/handwashing

GET /v2/timeseries/{user_id}/handwashing/grouped
Retrieve grouped handwashing event time series data for a user, captured by connected wearables that detect handwashing.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_handwashing_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/handwashing/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.handwashing_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsHandwashingGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsHandwashingGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.handwashingGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsHandwashingGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsHandwashingGroupedRequest request = VitalsHandwashingGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().handwashingGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsHandwashingGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.HandwashingGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 1
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Heart Rate Alert
Source: https://docs.junction.com/api-reference/data/timeseries/heart-rate-alert

GET /v2/timeseries/{user_id}/heart_rate_alert/grouped
Retrieve grouped heart rate alert time series events for a user, including high and low heart rate notifications from wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_heart_rate_alert_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/heart_rate_alert/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.heart_rate_alert_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsHeartRateAlertGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsHeartRateAlertGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.heartRateAlertGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsHeartRateAlertGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsHeartRateAlertGroupedRequest request = VitalsHeartRateAlertGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().heartRateAlertGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsHeartRateAlertGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.HeartRateAlertGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:30:52+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "type": "irregular_rhythm",
              "unit": "count",
              "value": 1
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Heart Rate Recovery One Minute
Source: https://docs.junction.com/api-reference/data/timeseries/heart-rate-recovery-one-minute

GET /v2/timeseries/{user_id}/heart_rate_recovery_one_minute/grouped
Retrieve grouped one-minute heart rate recovery time series data for a user from connected wearables after exercise sessions.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_heart_rate_recovery_one_minute_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/heart_rate_recovery_one_minute/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.heart_rate_recovery_one_minute_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsHeartRateRecoveryOneMinuteGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsHeartRateRecoveryOneMinuteGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.heartRateRecoveryOneMinuteGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsHeartRateRecoveryOneMinuteGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsHeartRateRecoveryOneMinuteGroupedRequest request = VitalsHeartRateRecoveryOneMinuteGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().heartRateRecoveryOneMinuteGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsHeartRateRecoveryOneMinuteGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.HeartRateRecoveryOneMinuteGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 37
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Heart Rate
Source: https://docs.junction.com/api-reference/data/timeseries/heartrate

GET /v2/timeseries/{user_id}/heartrate/grouped
Retrieve grouped heart rate time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_heartrate_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/heartrate/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.heartrate_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsHeartrateGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsHeartrateGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.heartrateGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsHeartrateGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsHeartrateGroupedRequest request = VitalsHeartrateGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().heartrateGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsHeartrateGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.HeartrateGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "bpm",
              "value": 70
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Heart Rate Variability
Source: https://docs.junction.com/api-reference/data/timeseries/hrv

GET /v2/timeseries/{user_id}/hrv/grouped
Retrieve grouped heart rate variability (HRV) time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_hrv_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/hrv/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.hrv_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsHrvGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsHrvGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.hrvGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsHrvGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsHrvGroupedRequest request = VitalsHrvGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().hrvGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsHrvGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.HrvGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "rmssd",
              "value": 48
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Inhaler Usage
Source: https://docs.junction.com/api-reference/data/timeseries/inhaler-usage

GET /v2/timeseries/{user_id}/inhaler_usage/grouped
Retrieve grouped inhaler usage time series events for a user from connected smart inhalers and respiratory health devices.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_inhaler_usage_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/inhaler_usage/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.inhaler_usage_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsInhalerUsageGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsInhalerUsageGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.inhalerUsageGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsInhalerUsageGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsInhalerUsageGroupedRequest request = VitalsInhalerUsageGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().inhalerUsageGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsInhalerUsageGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.InhalerUsageGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 2
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Insulin Injection
Source: https://docs.junction.com/api-reference/data/timeseries/insulin-injection

GET /v2/timeseries/{user_id}/insulin_injection/grouped
Retrieve grouped insulin injection time series events for a user from connected diabetes management devices and apps.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_insulin_injection_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/insulin_injection/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.insulin_injection_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsInsulinInjectionGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsInsulinInjectionGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.insulinInjectionGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsInsulinInjectionGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsInsulinInjectionGroupedRequest request = VitalsInsulinInjectionGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().insulinInjectionGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsInsulinInjectionGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.InsulinInjectionGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "bolus_purpose": "correction",
              "delivery_form": "extended",
              "delivery_mode": "bolus",
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "type": "rapid_acting",
              "unit": "unit",
              "value": 2.5
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Lean Body Mass
Source: https://docs.junction.com/api-reference/data/timeseries/lean-body-mass

GET /v2/timeseries/{user_id}/lean_body_mass/grouped
Retrieve grouped lean body mass time series data for a user from connected wearables, smart scales, and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_lean_body_mass_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/lean_body_mass/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.lean_body_mass_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsLeanBodyMassGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsLeanBodyMassGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.leanBodyMassGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsLeanBodyMassGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsLeanBodyMassGroupedRequest request = VitalsLeanBodyMassGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().leanBodyMassGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsLeanBodyMassGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.LeanBodyMassGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "kg",
              "value": 50
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Mindfulness Minutes
Source: https://docs.junction.com/api-reference/data/timeseries/mindfulness-minutes

GET /v2/timeseries/{user_id}/mindfulness_minutes/grouped
Retrieve grouped mindfulness session time series data for a user from connected wearables and meditation apps.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_mindfulness_minutes_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/mindfulness_minutes/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.mindfulness_minutes_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsMindfulnessMinutesGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsMindfulnessMinutesGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.mindfulnessMinutesGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsMindfulnessMinutesGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsMindfulnessMinutesGroupedRequest request = VitalsMindfulnessMinutesGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().mindfulnessMinutesGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsMindfulnessMinutesGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.MindfulnessMinutesGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2026-08-14T07:37:31.195512+00:00",
              "start": "2023-02-13T14:57:24+00:00",
              "unit": "min",
              "value": 42
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Note
Source: https://docs.junction.com/api-reference/data/timeseries/note

GET /v2/timeseries/{user_id}/note/grouped
Retrieve grouped user-logged note time series entries for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_note_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/note/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.note_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsNoteGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsNoteGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.noteGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsNoteGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsNoteGroupedRequest request = VitalsNoteGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().noteGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsNoteGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.NoteGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:30:52+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "tags": [
                "food",
                "exercise"
              ],
              "unit": "text",
              "value": "Lorem ipsum dolor sit amet"
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Peak Expiratory Flow Rate
Source: https://docs.junction.com/api-reference/data/timeseries/peak-expiratory-flow-rate

GET /v2/timeseries/{user_id}/peak_expiratory_flow_rate/grouped
Retrieve grouped peak expiratory flow rate time series readings for a user from connected respiratory devices and peak flow meters.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_peak_expiratory_flow_rate_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/peak_expiratory_flow_rate/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.peak_expiratory_flow_rate_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsPeakExpiratoryFlowRateGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsPeakExpiratoryFlowRateGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.peakExpiratoryFlowRateGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsPeakExpiratoryFlowRateGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsPeakExpiratoryFlowRateGroupedRequest request = VitalsPeakExpiratoryFlowRateGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().peakExpiratoryFlowRateGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsPeakExpiratoryFlowRateGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.PeakExpiratoryFlowRateGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "L/min",
              "value": 450
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Respiratory Rate
Source: https://docs.junction.com/api-reference/data/timeseries/respiratory-rate

GET /v2/timeseries/{user_id}/respiratory_rate/grouped
Retrieve grouped respiratory rate time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_respiratory_rate_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/respiratory_rate/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.respiratory_rate_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsRespiratoryRateGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsRespiratoryRateGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.respiratoryRateGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsRespiratoryRateGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsRespiratoryRateGroupedRequest request = VitalsRespiratoryRateGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().respiratoryRateGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsRespiratoryRateGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.RespiratoryRateGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "bpm",
              "value": 15.5
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Sleep Apnea Alert
Source: https://docs.junction.com/api-reference/data/timeseries/sleep-apnea-alert

GET /v2/timeseries/{user_id}/sleep_apnea_alert/grouped
Retrieve grouped sleep apnea alert time series events for a user from connected wearables that detect breathing disturbances.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_sleep_apnea_alert_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/sleep_apnea_alert/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.sleep_apnea_alert_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsSleepApneaAlertGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsSleepApneaAlertGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.sleepApneaAlertGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsSleepApneaAlertGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsSleepApneaAlertGroupedRequest request = VitalsSleepApneaAlertGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().sleepApneaAlertGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsSleepApneaAlertGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.SleepApneaAlertGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 1
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Sleep Breathing Disturbance
Source: https://docs.junction.com/api-reference/data/timeseries/sleep-breathing-disturbance

GET /v2/timeseries/{user_id}/sleep_breathing_disturbance/grouped
Retrieve grouped sleep breathing disturbance time series data for a user from connected wearables that monitor overnight breathing.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_sleep_breathing_disturbance_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/sleep_breathing_disturbance/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.sleep_breathing_disturbance_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsSleepBreathingDisturbanceGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsSleepBreathingDisturbanceGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.sleepBreathingDisturbanceGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsSleepBreathingDisturbanceGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsSleepBreathingDisturbanceGroupedRequest request = VitalsSleepBreathingDisturbanceGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().sleepBreathingDisturbanceGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsSleepBreathingDisturbanceGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.SleepBreathingDisturbanceGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "type": "elevated",
              "unit": "count",
              "value": 12
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Stand Duration
Source: https://docs.junction.com/api-reference/data/timeseries/stand-duration

GET /v2/timeseries/{user_id}/stand_duration/grouped
Retrieve grouped stand duration time series data for a user, measuring time spent standing from connected wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_stand_duration_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/stand_duration/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.stand_duration_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsStandDurationGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsStandDurationGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.standDurationGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsStandDurationGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsStandDurationGroupedRequest request = VitalsStandDurationGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().standDurationGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsStandDurationGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.StandDurationGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "min",
              "value": 15
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Stand Hour
Source: https://docs.junction.com/api-reference/data/timeseries/stand-hour

GET /v2/timeseries/{user_id}/stand_hour/grouped
Retrieve grouped stand hour time series data for a user, tracking hours with sufficient standing activity from connected wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_stand_hour_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/stand_hour/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.stand_hour_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsStandHourGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsStandHourGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.standHourGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsStandHourGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsStandHourGroupedRequest request = VitalsStandHourGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().standHourGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsStandHourGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.StandHourGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 1
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Steps
Source: https://docs.junction.com/api-reference/data/timeseries/steps

GET /v2/timeseries/{user_id}/steps/grouped
Retrieve grouped step count time series data for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_steps_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/steps/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.steps_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsStepsGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsStepsGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.stepsGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsStepsGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsStepsGroupedRequest request = VitalsStepsGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().stepsGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsStepsGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.StepsGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 123
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Stress Level
Source: https://docs.junction.com/api-reference/data/timeseries/stress-level

GET /v2/timeseries/{user_id}/stress_level/grouped
Retrieve grouped stress level time series data for a user from connected wearables that estimate stress from heart rate signals.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_stress_level_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/stress_level/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.stress_level_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsStressLevelGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsStressLevelGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.stressLevelGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsStressLevelGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsStressLevelGroupedRequest request = VitalsStressLevelGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().stressLevelGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsStressLevelGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.StressLevelGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "%",
              "value": 35
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Uv Exposure
Source: https://docs.junction.com/api-reference/data/timeseries/uv-exposure

GET /v2/timeseries/{user_id}/uv_exposure/grouped
Retrieve grouped ultraviolet (UV) exposure time series data for a user from connected wearables with UV sensors.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_uv_exposure_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/uv_exposure/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.uv_exposure_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsUvExposureGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsUvExposureGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.uvExposureGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsUvExposureGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsUvExposureGroupedRequest request = VitalsUvExposureGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().uvExposureGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsUvExposureGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.UvExposureGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "index",
              "value": 5
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Vo2 Max
Source: https://docs.junction.com/api-reference/data/timeseries/vo2-max

GET /v2/timeseries/{user_id}/vo2_max/grouped
Retrieve grouped VO2 max time series readings for a user, estimating cardiorespiratory fitness from connected wearables.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_vo2_max_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/vo2_max/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.vo2_max_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsVo2MaxGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsVo2MaxGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.vo2MaxGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsVo2MaxGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsVo2MaxGroupedRequest request = VitalsVo2MaxGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().vo2MaxGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsVo2MaxGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.Vo2MaxGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "mL/kg/min",
              "value": 48
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Waist Circumference
Source: https://docs.junction.com/api-reference/data/timeseries/waist-circumference

GET /v2/timeseries/{user_id}/waist_circumference/grouped
Retrieve grouped waist circumference time series measurements for a user from connected wearables and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_waist_circumference_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/waist_circumference/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.waist_circumference_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsWaistCircumferenceGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsWaistCircumferenceGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.waistCircumferenceGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsWaistCircumferenceGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsWaistCircumferenceGroupedRequest request = VitalsWaistCircumferenceGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().waistCircumferenceGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsWaistCircumferenceGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.WaistCircumferenceGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "cm",
              "value": 90
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Water
Source: https://docs.junction.com/api-reference/data/timeseries/water

GET /v2/timeseries/{user_id}/water/grouped
Retrieve grouped water intake time series data logged by a user through connected hydration apps and health data sources.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_water_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/water/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.water_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsWaterGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsWaterGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.waterGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsWaterGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsWaterGroupedRequest request = VitalsWaterGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().waterGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsWaterGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.WaterGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "ml",
              "value": 400
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Wheelchair Push
Source: https://docs.junction.com/api-reference/data/timeseries/wheelchair-push

GET /v2/timeseries/{user_id}/wheelchair_push/grouped
Retrieve grouped wheelchair push time series data for a user from connected wearables that track wheelchair activity.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_wheelchair_push_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/wheelchair_push/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.wheelchair_push_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsWheelchairPushGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsWheelchairPushGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.wheelchairPushGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsWheelchairPushGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsWheelchairPushGroupedRequest request = VitalsWheelchairPushGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().wheelchairPushGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsWheelchairPushGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.WheelchairPushGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 52
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Workout Distance
Source: https://docs.junction.com/api-reference/data/timeseries/workout-distance

GET /v2/timeseries/{user_id}/workout_distance/grouped
Retrieve grouped workout distance time series data for a user from connected wearables and fitness tracking apps.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_workout_distance_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/workout_distance/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.workout_distance_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsWorkoutDistanceGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsWorkoutDistanceGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.workoutDistanceGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsWorkoutDistanceGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsWorkoutDistanceGroupedRequest request = VitalsWorkoutDistanceGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().workoutDistanceGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsWorkoutDistanceGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.WorkoutDistanceGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "m",
              "value": 37
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Workout Duration
Source: https://docs.junction.com/api-reference/data/timeseries/workout-duration

GET /v2/timeseries/{user_id}/workout_duration/grouped
Retrieve grouped workout duration time series data for a user from connected wearables and fitness tracking apps.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_workout_duration_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/workout_duration/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.workout_duration_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsWorkoutDurationGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsWorkoutDurationGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.workoutDurationGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsWorkoutDurationGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsWorkoutDurationGroupedRequest request = VitalsWorkoutDurationGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().workoutDurationGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsWorkoutDurationGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.WorkoutDurationGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:30:52+00:00",
              "intensity": "medium",
              "start": "2023-02-13T14:30:52+00:00",
              "unit": "min",
              "value": 48
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Workout Swimming Stroke
Source: https://docs.junction.com/api-reference/data/timeseries/workout-swimming-stroke

GET /v2/timeseries/{user_id}/workout_swimming_stroke/grouped
Retrieve grouped swimming stroke count time series data for a user from connected wearables that track swim workouts.

<Card title="View Full Response Schema »" icon="diamond-turn-right" href="https://api.tryvital.io/redoc#tag/timeseries/operation/get_workout_swimming_stroke_grouped">
  The Response section on the page still needs work. Click here to check out the complete schema.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/timeseries/{user_id}/workout_swimming_stroke/grouped?start_date={{START_DATE}}&end_date={{END_DATE}} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from vital.client import Vital
  from vital.environment import VitalEnvironment

  client = Vital(
    api_key="YOUR_API_KEY",
    environment=VitalEnvironment.SANDBOX
  )

  data = client.vitals.workout_swimming_stroke_grouped(
    user_id="<user_id>", 
    start_date="2021-10-01", 
    end_date="2021-10-02"
  )
  ```

  ```javascript Node theme={null}
  import { VitalClient, VitalEnvironment } from '@tryvital/vital-node';
  import { VitalsWorkoutSwimmingStrokeGroupedRequest } from '@tryvital/vital-node/api/resources/vitals';

  const client = new VitalClient({
      apiKey: '<my_api_key>',
      environment: VitalEnvironment.Sandbox,
  });

  const request: VitalsWorkoutSwimmingStrokeGroupedRequest = {
      startDate: "2022-05-01",
      endDate: "2022-06-01"
  }

  const data = await client.vitals.workoutSwimmingStrokeGrouped(
      '<my_user_id>',
      request
  );
  ```

  ```java Java theme={null}
  import com.vital.api.Vital;
  import com.vital.api.core.Environment;
  import com.vital.api.resources.vitals.requests.VitalsWorkoutSwimmingStrokeGroupedRequest;

  Vital vital = Vital.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

  VitalsWorkoutSwimmingStrokeGroupedRequest request = VitalsWorkoutSwimmingStrokeGroupedRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build();

  var data = vital.vitals().workoutSwimmingStrokeGrouped("<user_id>", request);
  ```

  ```go Go theme={null}
  import (
    "context"

    vital "github.com/tryVital/vital-go"
    vitalclient "github.com/tryVital/vital-go/client"
  )

  client := vitalclient.NewClient(
    vitalclient.WithApiKey("<YOUR_API_KEY>"),
    vitalclient.WithBaseURL(vital.Environments.Sandbox),
  )

  EndDate := "2022-06-01"
  request := &vital.VitalsWorkoutSwimmingStrokeGroupedRequest{
    StartDate: "2022-05-01",
    EndDate:   &EndDate,
  }

  response, err := client.Vitals.WorkoutSwimmingStrokeGrouped(context.TODO(), "*", request)
  if err != nil {
    return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Example theme={null}
  {
    "groups": {
      "oura": [
        {
          "data": [
            {
              "end": "2023-02-13T14:57:24+00:00",
              "start": "2023-02-13T14:30:52+00:00",
              "timestamp": "2023-02-13T14:30:52+00:00",
              "unit": "count",
              "value": 37
            }
          ],
          "source": {
            "provider": "oura",
            "type": "ring"
          }
        }
      ]
    }
  }
  ```
</ResponseExample>


# Raw
Source: https://docs.junction.com/api-reference/data/workouts/get-raw

GET /v2/summary/workouts/{user_id}/raw
Retrieve raw workout data for a specific user as received from their connected wearable provider.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/workouts/{user_id}/raw \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.workouts.get_raw(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.workouts.getRaw({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.workouts.requests.GetRawWorkoutsRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.workouts().getRaw(
      "<user_id>",
      GetRawWorkoutsRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Workouts.GetRaw(context.TODO(), &junction.GetRawWorkoutsRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Stream
Source: https://docs.junction.com/api-reference/data/workouts/get-stream

GET /v2/timeseries/workouts/{workout_id}/stream
Retrieve timeseries workouts stream via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/workouts/{workout_id}/stream \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.workouts.get_by_workout_id("<workout_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.workouts.getByWorkoutId({ workoutId: "<workout_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.workouts().getByWorkoutId("<workout_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Workouts.GetByWorkoutId(context.TODO(), &junction.GetByWorkoutIdWorkoutsRequest{
      WorkoutId: "<workout_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Summary
Source: https://docs.junction.com/api-reference/data/workouts/get-summary

GET /v2/summary/workouts/{user_id}
Retrieve processed workout summary data for a specific user, including type, duration, and calorie metrics.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/summary/workouts/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.workouts.get(
      "<user_id>",
      start_date="2022-05-01",
      end_date="2022-06-01",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.workouts.get({
      userId: "<user_id>",
      startDate: "2022-05-01",
      endDate: "2022-06-01",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.workouts.requests.GetWorkoutsRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.workouts().get(
      "<user_id>",
      GetWorkoutsRequest.builder()
          .startDate("2022-05-01")
          .endDate("2022-06-01")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2022-06-01"
  response, err := c.Workouts.Get(context.TODO(), &junction.GetWorkoutsRequest{
      UserId:    "<user_id>",
      StartDate: "2022-05-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get ABN Form PDF
Source: https://docs.junction.com/api-reference/lab-testing/abn-pdf

GET /v3/order/{order_id}/abn_pdf
Retrieve order abn pdf via the Junction API. Requires authentication with your team API key.

Retrieving ABNs is currently in closed beta.

When [getting an order](/api-reference/lab-testing/get-order#response-has-abn), you can view the `has_abn` field to determine if an ABN form is available for this order.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/abn_pdf' \
       --header 'accept: application/pdf' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --output file.pdf
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_order_abn_pdf("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getOrderAbnPdf({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getOrderAbnPdf("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetOrderAbnPdf(context.TODO(), &junction.GetOrderAbnPdfLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get area info
Source: https://docs.junction.com/api-reference/lab-testing/area-info

GET /v3/order/area/info
Retrieve order area info via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/area/info?zip_code=85004' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_area_info(zip_code="85004")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getAreaInfo({ zipCode: "85004" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.GetAreaInfoLabTestsRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getAreaInfo(
      GetAreaInfoLabTestsRequest.builder()
          .zipCode("85004")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetAreaInfo(context.TODO(), &junction.GetAreaInfoLabTestsRequest{
      ZipCode: "85004",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "zip_code": "85004",
    "phlebotomy": {
      "is_served": true,
      "providers": [
          {
            "name": "getlabs",
            "tier": ["appointment-ready"]
          },
          {
            "name": "phlebfinders",
            "tier": ["appointment-request"]
          }
      ]
    },
    "central_labs": {
      "labcorp": {
        "patient_service_centers": {
          "within_radius": 5,
          "radius": "25",
          "capabilities": ["stat"]
        }
      }
    }
  }
  ```
</ResponseExample>


# Create appointment availability
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/appointment-availability

POST /v3/order/phlebotomy/appointment/availability
Create or submit order phlebotomy appointment availability via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/phlebotomy/appointment/availability' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "first_line": "256 West Lincoln Street",
      "second_line": "14",
      "city": "Phoenix",
      "state": "AZ",
      "zip_code": "85004"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_phlebotomy_appointment_availability(
      first_line="123 Main St",
      city="San Francisco",
      state="CA",
      zip_code="94105",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPhlebotomyAppointmentAvailability({
      body: {
          firstLine: "123 Main St",
          city: "San Francisco",
          state: "CA",
          zipCode: "94105",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.types.UsAddress;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPhlebotomyAppointmentAvailability(
      UsAddress.builder()
          .firstLine("123 Main St")
          .city("San Francisco")
          .state("CA")
          .zipCode("94105")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPhlebotomyAppointmentAvailability(context.TODO(), &junction.GetPhlebotomyAppointmentAvailabilityLabTestsRequest{
      Body: &junction.UsAddress{
          FirstLine: "123 Main St",
          City:      "San Francisco",
          State:     "CA",
          ZipCode:   "94105",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "timezone":"America/Phoenix",
      "slots": [
          {
              "date":"2023-05-09",
              "slots": [
                  {
                      "booking_key": "foo123",
                      "start": "2023-05-09T17:00:00+00:00",
                      "end": "2023-05-09T19:00:00+00:00",
                      "expires_at": "2023-05-09T12:39:57.827000+00:00",
                      "price": 3500,
                      "is_priority": true,
                      "num_appointments_available": 5
                  },
                  ...
              ],
          },
          {
              "date":"2023-05-10",
              "slots": [
                  {
                      "booking_key": "bar456",
                      "start": "2023-05-10T12:00:00+00:00",
                      "end": "2023-05-10T14:00:00+00:00",
                      "expires_at": "2023-05-09T12:39:57.852000+00:00",
                      "price": 7900,
                      "is_priority": true,
                      "num_appointments_available": 5
                  },
                  ...
              ],
          },
      ]
  }
  ```
</ResponseExample>


# Create appointment book
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/appointment-booking

POST /v3/order/{order_id}/phlebotomy/appointment/book
Create or submit order phlebotomy appointment book via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/phlebotomy/appointment/book' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "booking_key": "foo123",
      "appointment_notes": "Please bring photo ID"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.book_phlebotomy_appointment(
      "<order_id>",
      booking_key="<booking_key>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.bookPhlebotomyAppointment({
      orderId: "<order_id>",
      body: {
          bookingKey: "<booking_key>",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.BookPhlebotomyAppointmentLabTestsRequest;
  import com.junction.api.types.AppointmentBookingRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().bookPhlebotomyAppointment(
      "<order_id>",
      BookPhlebotomyAppointmentLabTestsRequest.builder()
          .body(
              AppointmentBookingRequest.builder()
                  .bookingKey("<booking_key>")
                  .build()
          )
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.BookPhlebotomyAppointment(context.TODO(), &junction.BookPhlebotomyAppointmentLabTestsRequest{
      OrderId: "<order_id>",
      Body: &junction.AppointmentBookingRequest{
          BookingKey: "<booking_key>",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14",
          "access_notes": "Gate code #1234, use side entrance"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-15T16:00:00+00:00",
      "end_at": "2023-05-15T18:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "phlebotomy",
      "provider": "getlabs",
      "status": "pending",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule":true,
      "appointment_notes": "Please bring photo ID"
  }
  ```
</ResponseExample>


# Get appointment cancellation reasons
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/appointment-cancellation-reasons

GET /v3/order/phlebotomy/appointment/cancellation-reasons
Retrieve order phlebotomy appointment cancellation reasons via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/phlebotomy/appointment/cancellation-reasons' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_phlebotomy_appointment_cancellation_reason()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPhlebotomyAppointmentCancellationReason();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPhlebotomyAppointmentCancellationReason();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPhlebotomyAppointmentCancellationReason(context.TODO())
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  [
     {
        "id": "5c0257ef-6fea-4a22-b20a-3ddab573d5c9",
        "name": "Did not fast for appointment",
        "is_refundable": true
     },
     {
        "id": "448c519c-64b4-4497-ae73-622fa93371b3",
        "name": "Do not trust company",
        "is_refundable": true
     },
     {
        "id": "d378e152-12d1-433e-9dd1-e0410f9331dc",
        "name": "Getlabs cannot deliver to my preferred lab",
        "is_refundable": true
     },
     {
        "id": "5330c863-ac80-4316-901b-d305d0df74d5",
        "name": "No longer interested",
        "is_refundable": true
     },
     {
        "id": "2b9f23fd-163e-4483-bb10-90c74a67e0dc",
        "name": "Other",
        "is_refundable": true
     },
     {
        "id": "98f861dd-fe61-4817-a9d6-1b99b19cd0fb",
        "name": "Provider asked me to cancel",
        "is_refundable": true
     },
     {
        "id": "7dfd7da5-ed6e-40bb-a7e4-c8003f0c10a9",
        "name": "Scheduled for wrong patient",
        "is_refundable": true
     },
     {
        "id": "796da8c8-e654-4347-8ded-1026410c1976",
        "name": "Scheduled time no longer works",
        "is_refundable": true
     },
     {
        "id": "ba02af35-a34f-4a7a-abe5-5f766e8f6cd1",
        "name": "Unable to get lab order from provider",
        "is_refundable": true
     },
     {
        "id": "0c9425db-f11e-49c5-b976-3c0d1d4a4ea8",
        "name": "Wanted to book in-person appointment",
        "is_refundable": true
     },
     {
        "id": "2599a0ea-0b4a-42fe-8df6-d8f7c68182e7",
        "name": "Went to lab for appointment",
        "is_refundable": true
     }
  ]
  ```
</ResponseExample>


# Cancel appointment
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/appointment-cancelling

PATCH /v3/order/{order_id}/phlebotomy/appointment/cancel
Cancel an order phlebotomy appointment via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/phlebotomy/appointment/cancel' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "cancellation_reason_id": "7dfd7da5-ed6e-40bb-a7e4-c8003f0c10a9"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.cancel_phlebotomy_appointment(
      "<order_id>",
      cancellation_reason_id="<cancellation_reason_id>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.cancelPhlebotomyAppointment({
      orderId: "<order_id>",
      cancellationReasonId: "<cancellation_reason_id>",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.ApiApiV1EndpointsVitalApiLabTestingOrdersHelpersAppointmentCancelRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().cancelPhlebotomyAppointment(
      "<order_id>",
      ApiApiV1EndpointsVitalApiLabTestingOrdersHelpersAppointmentCancelRequest.builder()
          .cancellationReasonId("<cancellation_reason_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CancelPhlebotomyAppointment(context.TODO(), &junction.ApiApiV1EndpointsVitalApiLabTestingOrdersHelpersAppointmentCancelRequest{
      OrderId:              "<order_id>",
      CancellationReasonId: "<cancellation_reason_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-17T20:00:00+00:00",
      "end_at": "2023-05-17T22:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "phlebotomy",
      "provider": "getlabs",
      "status": "cancelled",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true
  }
  ```
</ResponseExample>


# Create appointment request
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/appointment-request

POST /v3/order/{order_id}/phlebotomy/appointment/request
Create or submit order phlebotomy appointment request via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/phlebotomy/appointment/request' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "address":
      {
          "first_line": "256 West Lincoln Street",
          "second_line": "14",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004"
      },
      "provider": "phlebfinders",
      "appointment_notes": "Please bring photo ID"
  }
  '
  ```

  ```python Python theme={null}
  from junction import AppointmentProvider, Junction, UsAddress
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.request_phlebotomy_appointment(
      "<order_id>",
      address=UsAddress(
          first_line="123 Main St",
          city="San Francisco",
          state="CA",
          zip_code="94105",
      ),
      provider=AppointmentProvider.PHLEBFINDERS,
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.requestPhlebotomyAppointment({
      orderId: "<order_id>",
      address: {
          firstLine: "123 Main St",
          city: "San Francisco",
          state: "CA",
          zipCode: "94105",
      },
      provider: "phlebfinders",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.RequestAppointmentRequest;
  import com.junction.api.types.AppointmentProvider;
  import com.junction.api.types.UsAddress;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().requestPhlebotomyAppointment(
      "<order_id>",
      RequestAppointmentRequest.builder()
          .address(
              UsAddress.builder()
                  .firstLine("123 Main St")
                  .city("San Francisco")
                  .state("CA")
                  .zipCode("94105")
                  .build()
          )
          .provider(AppointmentProvider.PHLEBFINDERS)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.RequestPhlebotomyAppointment(context.TODO(), &junction.RequestAppointmentRequest{
      OrderId: "<order_id>",
      Address: &junction.UsAddress{
          FirstLine: "123 Main St",
          City:      "San Francisco",
          State:     "CA",
          ZipCode:   "94105",
      },
      Provider: junction.AppointmentProviderPhlebfinders,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14",
          "access_notes": "Gate code #1234, use side entrance"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "type": "phlebotomy",
      "provider": "phlebfinders",
      "status": "pending",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule":false,
      "appointment_notes": "Please bring photo ID"
  }
  ```
</ResponseExample>


# Update appointment reschedule
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/appointment-rescheduling

PATCH /v3/order/{order_id}/phlebotomy/appointment/reschedule
Partially update order phlebotomy appointment reschedule via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/phlebotomy/appointment/reschedule' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "booking_key": "bar456",
      "appointment_notes": "Updated: need morning appointment"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.reschedule_phlebotomy_appointment(
      "<order_id>",
      booking_key="<booking_key>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.reschedulePhlebotomyAppointment({
      orderId: "<order_id>",
      body: {
          bookingKey: "<booking_key>",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.ReschedulePhlebotomyAppointmentLabTestsRequest;
  import com.junction.api.types.AppointmentRescheduleRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().reschedulePhlebotomyAppointment(
      "<order_id>",
      ReschedulePhlebotomyAppointmentLabTestsRequest.builder()
          .body(
              AppointmentRescheduleRequest.builder()
                  .bookingKey("<booking_key>")
                  .build()
          )
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.ReschedulePhlebotomyAppointment(context.TODO(), &junction.ReschedulePhlebotomyAppointmentLabTestsRequest{
      OrderId: "<order_id>",
      Body: &junction.AppointmentRescheduleRequest{
          BookingKey: "<booking_key>",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14",
          "access_notes": "Gate code #1234, use side entrance"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-17T20:00:00+00:00",
      "end_at": "2023-05-17T22:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "phlebotomy",
      "provider": "getlabs",
      "status": "pending",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true,
      "appointment_notes": "Updated: need morning appointment"
  }
  ```
</ResponseExample>


# Get phlebotomy appointment
Source: https://docs.junction.com/api-reference/lab-testing/at-home-phlebotomy/get-appointment

GET /v3/order/{order_id}/phlebotomy/appointment
Retrieve order phlebotomy appointment via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/phlebotomy/appointment' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_phlebotomy_appointment("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPhlebotomyAppointment({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPhlebotomyAppointment("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPhlebotomyAppointment(context.TODO(), &junction.GetPhlebotomyAppointmentLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-17T20:00:00+00:00",
      "end_at": "2023-05-17T22:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "phlebotomy",
      "provider": "getlabs",
      "status": "pending",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true
  }
  ```
</ResponseExample>


# Get Available Biomarkers
Source: https://docs.junction.com/api-reference/lab-testing/biomarkers

GET /v3/lab_tests/markers
Retrieve lab tests markers via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/lab_tests/markers' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_markers()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getMarkers();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getMarkers();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetMarkers(context.TODO(), &junction.GetMarkersLabTestsRequest{})
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "markers": [
        {
            "id": 202,
            "name": "Acetylcholine Receptor (AChR) Antibodies, Complete Profile with Reflex to MuSK Antibodies",
            "slug": "acetylcholine-receptor-achr-antibodies-complete-profile-with-reflex-to-musk-antibodies",
            "description": "Acetylcholine Receptor (AChR) Antibodies, Complete Profile with Reflex to MuSK Antibodies",
            "lab_id": 6,
            "provider_id": "165605",
            "type": "biomarker",
            "unit": null,
            "price": "N/A",
            "expected_results": [
                {
                    "id": 2938,
                    "name": "AChR Blocking Abs, Serum",
                    "slug": "achr-blocking-abs-serum",
                    "lab_id": 6,
                    "provider_id": "085927",
                    "loinc": {
                        "id": 3514,
                        "name": "Acetylcholine receptor blocking Ab Qn (S)",
                        "slug": "acetylcholine-receptor-blocking-ab-qn-s",
                        "code": "11561-8",
                        "unit": "%{inhibition}"
                    }
                },
                {
                    "id": 2939,
                    "name": "AChR Binding Abs, Serum",
                    "slug": "achr-binding-abs-serum",
                    "lab_id": 6,
                    "provider_id": "085904",
                    "loinc": {
                        "id": 3174,
                        "name": "Acetylcholine receptor binding Ab (S) [Moles/Vol]",
                        "slug": "acetylcholine-receptor-binding-ab-s-moles-vol",
                        "code": "11034-6",
                        "unit": "nmol/L"
                    }
                },
                {
                    "id": 2940,
                    "name": "AChR-modulating Ab",
                    "slug": "achr-modulating-ab",
                    "lab_id": 6,
                    "provider_id": "505199",
                    "loinc": {
                        "id": 61121,
                        "name": "Acetylcholine receptor modulation Ab FC Ql (S)",
                        "slug": "acetylcholine-receptor-modulation-ab-fc-ql-s",
                        "code": "99062-2",
                        "unit": null
                    }
                }
            ]
        }
    ],
    "total": 2,
    "page": 1,
    "size": 2
  }
  ```
</ResponseExample>


# Cancel order
Source: https://docs.junction.com/api-reference/lab-testing/cancel-order

POST /v3/order/{order_id}/cancel
Cancel an order via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.cancel_order("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.cancelOrder({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().cancelOrder("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CancelOrder(context.TODO(), &junction.CancelOrderLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "order": {
        "title": "ClientFacingOrder",
        "required": [
          "user_id",
          "id",
          "team_id",
          "patient_details",
          "patient_address",
          "lab_test",
          "details",
          "created_at",
          "updated_at",
          "events"
        ],
        "type": "object",
        "properties": {
          "user_id": {
            "title": "User Id",
            "type": "string",
            "description": "User id returned by vital create user request. This id should be stored in your database against the user and used for all interactions with the vital api.",
            "format": "uuid"
          },
          "user_key": {
            "title": "User Key",
            "type": "string",
            "description": "User key returned by vital create user key request. This key should be stored in your database against the user and used for all interactions with the vital api.",
            "format": "uuid",
            "deprecated": true
          },
          "id": {
            "title": "Id",
            "type": "string",
            "description": "The Vital Order ID",
            "format": "uuid"
          },
          "team_id": {
            "title": "Team Id",
            "type": "string",
            "description": "Your team id.",
            "format": "uuid"
          },
          "patient_details": {
            "title": "Patient Details",
            "allOf": [
              {
                "title": "PatientDetails",
                "required": ["dob", "gender"],
                "type": "object",
                "properties": {
                  "dob": {
                    "title": "Dob",
                    "type": "string",
                    "format": "date-time"
                  },
                  "gender": {
                    "title": "Gender",
                    "type": "string"
                  },
                  "email": {
                    "title": "Email",
                    "type": "string"
                  }
                }
              }
            ],
            "description": "Patient Details"
          },
          "patient_address": {
            "title": "Patient Address",
            "allOf": [
              {
                "title": "PatientAddress",
                "required": [
                  "receiver_name",
                  "street",
                  "city",
                  "state",
                  "zip",
                  "country",
                  "phone_number"
                ],
                "type": "object",
                "properties": {
                  "receiver_name": {
                    "title": "Receiver Name",
                    "type": "string"
                  },
                  "street": {
                    "title": "Street",
                    "type": "string"
                  },
                  "street_number": {
                    "title": "Street Number",
                    "type": "string"
                  },
                  "city": {
                    "title": "City",
                    "type": "string"
                  },
                  "state": {
                    "title": "State",
                    "type": "string"
                  },
                  "zip": {
                    "title": "Zip",
                    "type": "string"
                  },
                  "country": {
                    "title": "Country",
                    "type": "string"
                  },
                  "phone_number": {
                    "title": "Phone Number",
                    "type": "string"
                  }
                }
              }
            ],
            "description": "Patient Address"
          },
          "lab_test": {
            "title": "Lab Test",
            "allOf": [
              {
                "title": "LabTestInDB",
                "required": [
                  "slug",
                  "name",
                  "sample_type",
                  "method",
                  "lab_id",
                  "price",
                  "is_active",
                  "created_at",
                  "updated_at",
                  "id"
                ],
                "type": "object",
                "properties": {
                  "slug": {
                    "title": "Slug",
                    "type": "string"
                  },
                  "name": {
                    "title": "Name",
                    "type": "string"
                  },
                  "description": {
                    "title": "Description",
                    "type": "string"
                  },
                  "sample_type": {
                    "title": "LabTestSampleType",
                    "enum": ["dried_blood_spot"],
                    "type": "string",
                    "description": "The type of sample used to perform a lab test."
                  },
                  "method": {
                    "title": "LabTestMethod",
                    "enum": ["testkit", "walk_in_test"],
                    "type": "string",
                    "description": "The method used to perform a lab test."
                  },
                  "lab_id": {
                    "title": "Lab Id",
                    "type": "integer"
                  },
                  "skus": {
                    "title": "Skus",
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  },
                  "price": {
                    "title": "Price",
                    "type": "number"
                  },
                  "is_active": {
                    "title": "Is Active",
                    "type": "boolean"
                  },
                  "turnaround_time_lower": {
                    "title": "Turnaround Time Lower",
                    "type": "integer"
                  },
                  "turnaround_time_upper": {
                    "title": "Turnaround Time Upper",
                    "type": "integer"
                  },
                  "created_at": {
                    "title": "Created At",
                    "type": "string",
                    "format": "date-time"
                  },
                  "updated_at": {
                    "title": "Updated At",
                    "type": "string",
                    "format": "date-time"
                  },
                  "id": {
                    "title": "Id",
                    "type": "string",
                    "format": "uuid"
                  },
                  "lab": {
                    "title": "LabInDB",
                    "required": [
                      "slug",
                      "name",
                      "first_line_address",
                      "city",
                      "zipcode",
                      "id"
                    ],
                    "type": "object",
                    "properties": {
                      "slug": {
                        "title": "Slug",
                        "type": "string"
                      },
                      "name": {
                        "title": "Name",
                        "type": "string"
                      },
                      "first_line_address": {
                        "title": "First Line Address",
                        "type": "string"
                      },
                      "city": {
                        "title": "City",
                        "type": "string"
                      },
                      "zipcode": {
                        "title": "Zipcode",
                        "type": "string"
                      },
                      "clia": {
                        "title": "Clia",
                        "type": "string"
                      },
                      "created_at": {
                        "title": "Created At",
                        "type": "string",
                        "format": "date-time"
                      },
                      "updated_at": {
                        "title": "Updated At",
                        "type": "string",
                        "format": "date-time"
                      },
                      "id": {
                        "title": "Id",
                        "type": "integer"
                      }
                    }
                  },
                  "markers": {
                    "title": "Markers",
                    "type": "array",
                    "items": {
                      "title": "MarkerInDB",
                      "required": ["name", "slug", "description", "id"],
                      "type": "object",
                      "properties": {
                        "name": {
                          "title": "Name",
                          "type": "string"
                        },
                        "slug": {
                          "title": "Slug",
                          "type": "string"
                        },
                        "description": {
                          "title": "Description",
                          "type": "string"
                        },
                        "created_at": {
                          "title": "Created At",
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "title": "Updated At",
                          "type": "string",
                          "format": "date-time"
                        },
                        "id": {
                          "title": "Id",
                          "type": "integer"
                        }
                      }
                    }
                  }
                },
                "description": "Schema for a LabTest in the database."
              }
            ],
            "description": "The Vital Test associated with the order"
          },
          "details": {
            "title": "Details",
            "anyOf": [
              {
                "title": "ClientFacingWalkInOrderDetails",
                "required": ["type"],
                "type": "object",
                "properties": {
                  "type": {
                    "title": "Type",
                    "enum": ["walk_in_test"],
                    "type": "string"
                  },
                  "data": {
                    "title": "ClientFacingWalkInTestOrder",
                    "required": ["id", "created_at", "updated_at"],
                    "type": "object",
                    "properties": {
                      "id": {
                        "title": "Id",
                        "type": "string",
                        "description": "The Vital walk-in test Order ID",
                        "format": "uuid"
                      },
                      "created_at": {
                        "title": "Created At",
                        "type": "string",
                        "format": "date-time"
                      },
                      "updated_at": {
                        "title": "Updated At",
                        "type": "string",
                        "format": "date-time"
                      }
                    },
                    "description": "Schema for a walk-in test order in the client facing API.\n\nTo be used as part of a ClientFacingOrder.",
                    "example": {
                      "id": "0651ee15-31a1-461b-9c10-86aa960da6c9",
                      "created_at": "2020-01-01T00:00:00Z",
                      "updated_at": "2020-01-01T00:00:00Z"
                    }
                  }
                }
              },
              {
                "title": "ClientFacingTestKitOrderDetails",
                "required": ["type"],
                "type": "object",
                "properties": {
                  "type": {
                    "title": "Type",
                    "enum": ["testkit"],
                    "type": "string"
                  },
                  "data": {
                    "title": "ClientFacingTestkitOrder",
                    "required": ["id", "created_at", "updated_at"],
                    "type": "object",
                    "properties": {
                      "id": {
                        "title": "Id",
                        "type": "string",
                        "description": "The Vital TestKit Order ID",
                        "format": "uuid"
                      },
                      "shipment": {
                        "title": "Shipment",
                        "allOf": [
                          {
                            "title": "ClientFacingShipment",
                            "required": [
                              "id",
                              "outbound_tracking_number",
                              "outbound_tracking_url",
                              "inbound_tracking_number",
                              "inbound_tracking_url",
                              "outbound_courier",
                              "inbound_courier",
                              "notes"
                            ],
                            "type": "object",
                            "properties": {
                              "id": {
                                "title": "Id",
                                "type": "string",
                                "description": "The Vital Shipment ID",
                                "format": "uuid"
                              },
                              "outbound_tracking_number": {
                                "title": "Outbound Tracking Number",
                                "type": "string",
                                "description": "Tracking number for delivery to customer"
                              },
                              "outbound_tracking_url": {
                                "title": "Outbound Tracking Url",
                                "type": "string",
                                "description": "Tracking url for delivery to customer"
                              },
                              "inbound_tracking_number": {
                                "title": "Inbound Tracking Number",
                                "type": "string",
                                "description": "Tracking number for delivery to lab"
                              },
                              "inbound_tracking_url": {
                                "title": "Inbound Tracking Url",
                                "type": "string",
                                "description": "Tracking url for delivery to lab"
                              },
                              "outbound_courier": {
                                "title": "Outbound Courier",
                                "type": "string",
                                "description": "Courier used for delivery to customer"
                              },
                              "inbound_courier": {
                                "title": "Inbound Courier",
                                "type": "string",
                                "description": "Courier used for delivery to lab"
                              },
                              "notes": {
                                "title": "Notes",
                                "type": "string",
                                "description": "Notes associated to the Vital shipment"
                              }
                            },
                            "description": "Schema for a Shipment in the client facing API.\n\nTo be used as part of a ClientFacingTestkitOrder.",
                            "example": {
                              "id": "dcab86c6-a315-493d-97aa-2fd0fa649130",
                              "outbound_tracking_number": "<outbound_tracking_number>",
                              "outbound_tracking_url": "<outbound_tracking_url>",
                              "inbound_tracking_number": "<inbound_tracking_number>",
                              "inbound_tracking_url": "<inbound_tracking_url>",
                              "outbound_courier": "usps",
                              "inbound_courier": "usps",
                              "notes": "<notes>"
                            }
                          }
                        ],
                        "description": "Shipment object"
                      },
                      "created_at": {
                        "title": "Created At",
                        "type": "string",
                        "format": "date-time"
                      },
                      "updated_at": {
                        "title": "Updated At",
                        "type": "string",
                        "format": "date-time"
                      }
                    },
                    "description": "Schema for a testkit order in the client facing API.\n\nTo be used as part of a ClientFacingOrder.",
                    "example": {
                      "id": "43697a79-298f-43db-9703-e4cf16006c66",
                      "shipment": {
                        "id": "51caeb91-cc5c-4f82-a4c2-6f23b68e54c5",
                        "outbound_tracking_number": "<outbound_tracking_number>",
                        "outbound_tracking_url": "<outbound_tracking_url>",
                        "inbound_tracking_number": "<inbound_tracking_number>",
                        "inbound_tracking_url": "<inbound_tracking_url>",
                        "outbound_courier": "usps",
                        "inbound_courier": "usps",
                        "notes": "<notes>",
                        "created_at": "2020-01-01T00:00:00.000Z",
                        "updated_at": "2020-01-01T00:00:00.000Z"
                      },
                      "created_at": "2020-01-01T00:00:00Z",
                      "updated_at": "2020-01-01T00:00:00Z"
                    }
                  }
                }
              }
            ]
          },
          "sample_id": {
            "title": "Sample Id",
            "type": "string",
            "description": "Sample ID"
          },
          "notes": {
            "title": "Notes",
            "type": "string",
            "description": "Notes associated with the order"
          },
          "created_at": {
            "title": "Created At",
            "type": "string",
            "description": "When your order was created",
            "format": "date-time"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string",
            "description": "When your order was last updated",
            "format": "date-time"
          },
          "events": {
            "title": "Events",
            "type": "array",
            "items": {
              "title": "ClientFacingOrderEvent",
              "required": ["id", "created_at", "status"],
              "type": "object",
              "properties": {
                "id": {
                  "title": "Id",
                  "type": "integer"
                },
                "created_at": {
                  "title": "Created At",
                  "type": "string",
                  "format": "date-time"
                },
                "status": {
                  "title": "OrderV2Status",
                  "enum": [
                    "received.walk_in_test.requisition_created",
                    "completed.walk_in_test.order_completed",
                    "failed.walk_in_test.sample_error",
                    "received.testkit.ordered",
                    "received.testkit.requisition_created",
                    "collecting_sample.testkit.transit_customer",
                    "collecting_sample.testkit.out_for_delivery",
                    "collecting_sample.testkit.with_customer",
                    "collecting_sample.testkit.transit_lab",
                    "collecting_sample.testkit.problem_in_transit_customer",
                    "collecting_sample.testkit.problem_in_transit_lab",
                    "sample_with_lab.testkit.delivered_to_lab",
                    "sample_with_lab.testkit.lab_processing_blocked",
                    "completed.testkit.completed",
                    "failed.testkit.failure_to_deliver_to_customer",
                    "failed.testkit.failure_to_deliver_to_lab",
                    "failed.testkit.sample_error",
                    "failed.testkit.lost",
                    "cancelled.testkit.cancelled",
                    "cancelled.testkit.do_not_process"
                  ],
                  "type": "string",
                  "description": "An enumeration."
                }
              }
            }
          },
          "status": {
            "title": "OrderV2TopLevelStatus",
            "enum": [
              "received",
              "collecting_sample",
              "sample_with_lab",
              "completed",
              "cancelled",
              "failed"
            ],
            "type": "string",
            "description": "An enumeration."
          }
        },
        "example": {
          "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
          "team_id": "cbb64555-af07-46c1-be09-ef89308e9b60",
          "user_id": "94e2d9f2-d600-4a23-9f08-536df378e2c7",
          "patient_details": {
            "dob": "2020-01-01",
            "gender": "male"
          },
          "patient_address": {
            "receiver_name": "John Doe",
            "first_line": "123 Main St.",
            "second_line": "Apt. 208",
            "city": "San Francisco",
            "state": "CA",
            "zip": "91189",
            "country": "United States",
            "phone_number": "+1123456789"
          },
          "details": {
            "type": "testkit",
            "data": {
              "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
              "shipment": {
                "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
                "outbound_tracking_number": "<outbound_tracking_number>",
                "outbound_tracking_url": "<outbound_tracking_url>",
                "inbound_tracking_number": "<inbound_tracking_number>",
                "inbound_tracking_url": "<inbound_tracking_url>",
                "outbound_courier": "usps",
                "inbound_courier": "usps",
                "notes": "<notes>",
                "created_at": "2020-01-01T00:00:00.000Z",
                "updated_at": "2020-01-01T00:00:00.000Z"
              },
              "created_at": "2020-01-01T00:00:00Z",
              "updated_at": "2020-01-01T00:00:00Z"
            }
          },
          "lab_test": {
            "name": "Lipids Panel",
            "description": "Cholesterol test",
            "method": "testkit"
          },
          "sample_id": "123456789",
          "notes": "This is a note",
          "created_at": "2020-01-01T00:00:00Z",
          "updated_at": "2020-01-01T00:00:00Z",
          "status": "collecting_sample",
          "events": [
            {
              "id": 1,
              "created_at": "2022-01-01T00:00:00Z",
              "status": "received.testkit.ordered"
            },
            {
              "id": 2,
              "created_at": "2022-01-02T00:00:00Z",
              "status": "received.testkit.requisition_created"
            },
            {
              "id": 3,
              "created_at": "2022-01-03T00:00:00Z",
              "status": "collecting_sample.testkit.transit_customer"
            }
          ]
        }
      },
      "status": {
        "title": "Status",
        "type": "string"
      },
      "message": {
        "title": "Message",
        "type": "string"
      }
    }
  ]
  ```
</ResponseExample>


# Compendium Convert
Source: https://docs.junction.com/api-reference/lab-testing/compendium/convert

POST /v3/compendium/convert
Create or submit compendium convert via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>


# Compendium Search
Source: https://docs.junction.com/api-reference/lab-testing/compendium/search

POST /v3/compendium/search
Create or submit compendium search via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>


# Create order
Source: https://docs.junction.com/api-reference/lab-testing/create-order

POST /v3/order
Create or submit order via the Junction API. Requires authentication with your team API key.

<Note>
  Patient name fields (`first_name`, `last_name`) must follow specific validation rules due to lab restrictions. See [Patient Name Validation](/lab/workflow/order-requirements#patient-name-validation) for complete details.
</Note>

<RequestExample>
  ```python Python theme={null}
  from junction import Gender, Junction, PatientAddressWithValidation, PatientDetailsWithValidation
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.create_order(
      user_id="63661a2b-2bb3-4125-bb1a-b590f64f057f",
      lab_test_id="5b41f610-ebc5-4803-8f0c-a61c3bdc7faf",
      patient_details=PatientDetailsWithValidation(
          first_name="John",
          last_name="Doe",
          dob="2020-01-01",
          gender=Gender.MALE,
          phone_number="+1123456789",
          email="email@email.com",
      ),
      patient_address=PatientAddressWithValidation(
          receiver_name="John Doe",
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
          phone_number="+1123456789",
          access_notes="Gate code #1234, use side entrance",
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.createOrder({
      userId: "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      labTestId: "5b41f610-ebc5-4803-8f0c-a61c3bdc7faf",
      patientDetails: {
          firstName: "John",
          lastName: "Doe",
          dob: "2020-01-01",
          gender: "male",
          phoneNumber: "+1123456789",
          email: "email@email.com",
      },
      patientAddress: {
          receiverName: "John Doe",
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
          phoneNumber: "+1123456789",
          accessNotes: "Gate code #1234, use side entrance",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.CreateOrderRequestCompatible;
  import com.junction.api.types.Gender;
  import com.junction.api.types.PatientAddressWithValidation;
  import com.junction.api.types.PatientDetailsWithValidation;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().createOrder(
      CreateOrderRequestCompatible.builder()
          .userId("63661a2b-2bb3-4125-bb1a-b590f64f057f")
          .patientDetails(
              PatientDetailsWithValidation.builder()
                  .firstName("John")
                  .lastName("Doe")
                  .dob("2020-01-01")
                  .gender(Gender.MALE)
                  .phoneNumber("+1123456789")
                  .email("email@email.com")
                  .build()
          )
          .patientAddress(
              PatientAddressWithValidation.builder()
                  .firstLine("123 Main St.")
                  .city("San Francisco")
                  .state("CA")
                  .zip("91189")
                  .country("US")
                  .build()
          )
          .labTestId("5b41f610-ebc5-4803-8f0c-a61c3bdc7faf")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CreateOrder(context.TODO(), &junction.CreateOrderRequestCompatible{
      UserId:    "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      LabTestId: junction.String("5b41f610-ebc5-4803-8f0c-a61c3bdc7faf"),
      PatientDetails: &junction.PatientDetailsWithValidation{
          FirstName:   "John",
          LastName:    "Doe",
          Dob:         "2020-01-01",
          Gender:      junction.GenderMale,
          PhoneNumber: "+1123456789",
          Email:       "email@email.com",
      },
      PatientAddress: &junction.PatientAddressWithValidation{
          ReceiverName: junction.String("John Doe"),
          FirstLine:    "123 Main St.",
          SecondLine:   junction.String("Apt. 208"),
          City:         "San Francisco",
          State:        "CA",
          Zip:          "91189",
          Country:      "US",
          PhoneNumber:  junction.String("+1123456789"),
          AccessNotes:  junction.String("Gate code #1234, use side entrance"),
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
     "order": {
        "id": "ea7eae96-2c25-404f-b043-bfc08584610d",
        "team_id": "c26a9cc7-cdff-4f23-a5f6-74d40088c16a",
        "user_id": "63661a2b-2bb3-4125-bb1a-b590f64f057f",
        "patient_details": {
           "dob": "2020-01-01",
           "gender": "male"
        },
        "patient_address": {
           "receiver_name": "John Doe",
           "first_line": "123 Main St.",
           "second_line": "Apt. 208",
           "city": "San Francisco",
           "state": "CA",
           "zip": "91189",
           "country": "United States",
           "phone_number": "+1123456789",
           "access_notes": "Gate code #1234, use side entrance"
        },
        "priority": false,
        "health_insurance_id": "33ec11aa-d8bf-4f46-950d-c9171be3c22f",
        "details": {
           "type": "testkit",
           "data": {
           "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
           "shipment": {
              "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
              "outbound_tracking_number": "<outbound_tracking_number>",
              "outbound_tracking_url": "<outbound_tracking_url>",
              "inbound_tracking_number": "<inbound_tracking_number>",
              "inbound_tracking_url": "<inbound_tracking_url>",
              "outbound_courier": "usps",
              "inbound_courier": "usps",
              "notes": "<notes>",
              "created_at": "2020-01-01T00:00:00.000Z",
              "updated_at": "2020-01-01T00:00:00.000Z"
           },
           "created_at": "2020-01-01T00:00:00Z",
           "updated_at": "2020-01-01T00:00:00Z"
           }
        },
        "lab_test": {
           "name": "Lipids Panel",
           "description": "Cholesterol test",
           "method": "testkit"
        },
        "sample_id": "123456789",
        "notes": "This is a note",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "status": "collecting_sample",
        "events": [
           {
           "id": 1,
           "created_at": "2022-01-01T00:00:00Z",
           "status": "received.testkit.ordered"
           },
           {
           "id": 2,
           "created_at": "2022-01-02T00:00:00Z",
           "status": "received.testkit.requisition_created"
           },
           {
           "id": 3,
           "created_at": "2022-01-03T00:00:00Z",
           "status": "collecting_sample.testkit.transit_customer"
           }
        ]
     },
     "status": "string",
     "message": "string"
  }
  ```
</ResponseExample>


# Create Unregistered Testkit Order
Source: https://docs.junction.com/api-reference/lab-testing/create-unregistered-order

POST /v3/order/testkit
Create or submit order testkit via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction, ShippingAddressWithValidation
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.testkit.create_order(
      user_id="63661a2b-2bb3-4125-bb1a-b590f64f057f",
      lab_test_id="5b41f610-ebc5-4803-8f0c-a61c3bdc7faf",
      shipping_details=ShippingAddressWithValidation(
          receiver_name="John Doe",
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
          phone_number="+11234567890",
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.testkit.createOrder({
      userId: "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      labTestId: "5b41f610-ebc5-4803-8f0c-a61c3bdc7faf",
      shippingDetails: {
          receiverName: "John Doe",
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
          phoneNumber: "+11234567890",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.testkit.requests.CreateRegistrableTestkitOrderRequest;
  import com.junction.api.types.ShippingAddressWithValidation;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.testkit().createOrder(
      CreateRegistrableTestkitOrderRequest.builder()
          .userId("63661a2b-2bb3-4125-bb1a-b590f64f057f")
          .labTestId("5b41f610-ebc5-4803-8f0c-a61c3bdc7faf")
          .shippingDetails(
              ShippingAddressWithValidation.builder()
                  .receiverName("John Doe")
                  .firstLine("123 Main St.")
                  .city("San Francisco")
                  .state("CA")
                  .zip("91189")
                  .country("US")
                  .phoneNumber("+11234567890")
                  .build()
          )
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Testkit.CreateOrder(context.TODO(), &junction.CreateRegistrableTestkitOrderRequest{
      UserId:    "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      LabTestId: "5b41f610-ebc5-4803-8f0c-a61c3bdc7faf",
      ShippingDetails: &junction.ShippingAddressWithValidation{
          ReceiverName: "John Doe",
          FirstLine:    "123 Main St.",
          SecondLine:   junction.String("Apt. 208"),
          City:         "San Francisco",
          State:        "CA",
          Zip:          "91189",
          Country:      "US",
          PhoneNumber:  "+11234567890",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```

  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/testkit' \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "user_id": "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      "lab_test_id": "5b41f610-ebc5-4803-8f0c-a61c3bdc7faf",
      "shipping_details": {
        "receiver_name": "John Doe",
        "first_line": "123 Main St.",
        "second_line": "Apt. 208",
        "city": "San Francisco",
        "state": "CA",
        "zip": "91189",
        "country": "US",
        "phone_number": "+1123456789"
      }
  }
  '
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
  "order": {
      "id": "96edc6ef-3b2c-412b-b9e5-96f361f93aec",
      "team_id": "b080b20c-e162-4cf1-9c7d-8faee72ee08e",
      "user_id": "9f1e094e-1641-466b-b668-d4d3300e569f",
      "shipping_details": {
        "receiver_name": "John Doe",
        "first_line": "123 Main St.",
        "second_line": "Apt. 208",
        "city": "San Francisco",
        "state": "CA",
        "zip": "91189",
        "country": "United States",
        "phone_number": "+11234567890"
      },
      "details": {
        "type": "testkit",
        "data": {
          "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
          "created_at": "2020-01-01T00:00:00Z",
          "updated_at": "2020-01-01T00:00:00Z"
        }
      },
      "lab_test": {
        "name": "Lipids Panel",
        "description": "Cholesterol test",
        "method": "testkit"
      },
      "sample_id": "123456789",
      "health_insurance_id": "7695cc28-f9e5-400d-95d2-ec7d9ec580df",
      "notes": "This is a note",
      "created_at": "2020-01-01T00:00:00Z",
      "updated_at": "2020-01-01T00:00:00Z",
      "status": "received",
      "events": [
        {
          "id": 1,
          "created_at": "2022-01-01T00:00:00Z",
          "status": "received.testkit.ordered"
        },
        {
          "id": 2,
          "created_at": "2022-01-02T00:00:00Z",
          "status": "received.testkit.awaiting_registration"
        }
      ]
    },
    "status": "string",
    "message": "string"
  }
  ```
</ResponseExample>


# Get Lab Test
Source: https://docs.junction.com/api-reference/lab-testing/get-lab-test

GET /v3/lab_tests/{lab_test_id}
Retrieve lab tests via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_by_id("<lab_test_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getById({ labTestId: "<lab_test_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getById("<lab_test_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetById(context.TODO(), &junction.GetByIdLabTestsRequest{
      LabTestId: "<lab_test_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "5af405fc-fafb-49e8-b0bf-fe9d91df6a7d",
    "slug": "a94e3005-new-test",
    "name": "New test",
    "sample_type": "serum",
    "method": "at_home_phlebotomy",
    "price": 0.0,
    "is_active": true,
    "status": "active",
    "fasting": false,
    "lab": {
      "id": 26,
      "slug": "quest",
      "name": "Quest",
      "first_line_address": "100 Tri State International #100",
      "city": "Lincolnshire",
      "zipcode": "60069",
      "collection_methods": ["at_home_phlebotomy", "walk_in_test"],
      "sample_types": ["serum", "saliva", "urine"]
    },
    "markers": [
      {
        "id": 6859,
        "name": "17-Hydroxyprogesterone",
        "slug": "17-hydroxyprogesterone",
        "description": "17-Hydroxyprogesterone",
        "lab_id": 26,
        "provider_id": "17180",
        "type": "biomarker",
        "unit": null,
        "price": "N/A",
        "aoe": null,
        "a_la_carte_enabled": true
      },
      {
        "id": 8213,
        "name": "17-Hydroxypregnenolone",
        "slug": "17-hydroxypregnenolone",
        "description": "17-Hydroxypregnenolone",
        "lab_id": 26,
        "provider_id": "8352",
        "type": "biomarker",
        "unit": null,
        "price": "N/A",
        "aoe": null,
        "a_la_carte_enabled": true
      }
    ],
    "is_delegated": false,
    "auto_generated": false
  }
  ```
</ResponseExample>


# Get order
Source: https://docs.junction.com/api-reference/lab-testing/get-order

GET /v3/order/{order_id}
Retrieve order via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_order("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getOrder({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getOrder("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetOrder(context.TODO(), &junction.GetOrderLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
    "team_id": "cbb64555-af07-46c1-be09-ef89308e9b60",
    "user_id": "94e2d9f2-d600-4a23-9f08-536df378e2c7",
    "patient_details": {
      "dob": "2020-01-01",
      "gender": "male"
    },
    "priority": false,
    "patient_address": {
      "receiver_name": "John Doe",
      "first_line": "123 Main St.",
      "second_line": "Apt. 208",
      "city": "San Francisco",
      "state": "CA",
      "zip": "91189",
      "country": "United States",
      "phone_number": "+1123456789"
    },
    "details": {
      "type": "testkit",
      "data": {
        "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
        "shipment": {
          "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
          "outbound_tracking_number": "<outbound_tracking_number>",
          "outbound_tracking_url": "<outbound_tracking_url>",
          "inbound_tracking_number": "<inbound_tracking_number>",
          "inbound_tracking_url": "<inbound_tracking_url>",
          "outbound_courier": "usps",
          "inbound_courier": "usps",
          "notes": "<notes>",
          "created_at": "2020-01-01T00:00:00.000Z",
          "updated_at": "2020-01-01T00:00:00.000Z"
        },
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z"
      }
    },
    "lab_test": {
      "name": "Lipids Panel",
      "description": "Cholesterol test",
      "method": "testkit"
    },
    "sample_id": "123456789",
    "notes": "This is a note",
    "created_at": "2020-01-01T00:00:00Z",
    "updated_at": "2020-01-01T00:00:00Z",
    "status": "collecting_sample",
    "events": [
      {
        "id": 1,
        "created_at": "2022-01-01T00:00:00Z",
        "status": "received.testkit.ordered"
      },
      {
        "id": 2,
        "created_at": "2022-01-02T00:00:00Z",
        "status": "received.testkit.requisition_created"
      },
      {
        "id": 3,
        "created_at": "2022-01-03T00:00:00Z",
        "status": "collecting_sample.testkit.transit_customer"
      }
    ],
    "origin": "initial",
    "order_transaction": {
      "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
      "status": "active",
      "orders": [
        {
          "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
          "created_at": "2020-01-01T00:00:00Z",
          "updated_at": "2020-01-01T00:00:00Z",
          "low_level_status": "transit_customer",
          "low_level_status_created_at": "2022-01-03T00:00:00Z",
          "origin": "initial"
        }
      ]
    }
  }
  ```
</ResponseExample>


# Get orders
Source: https://docs.junction.com/api-reference/lab-testing/get-orders

GET /v3/orders
Retrieve orders via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/orders' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_orders()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getOrders();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getOrders();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetOrders(context.TODO(), &junction.GetOrdersLabTestsRequest{})
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "orders": [
      {
        "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
        "team_id": "cbb64555-af07-46c1-be09-ef89308e9b60",
        "user_id": "94e2d9f2-d600-4a23-9f08-536df378e2c7",
        "patient_details": {
          "dob": "2020-01-01",
          "gender": "male"
        },
        "priority": false,
        "patient_address": {
          "receiver_name": "John Doe",
          "first_line": "123 Main St.",
          "second_line": "Apt. 208",
          "city": "San Francisco",
          "state": "CA",
          "zip": "91189",
          "country": "United States",
          "phone_number": "+1123456789"
        },
        "details": {
          "type": "testkit",
          "data": {
            "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
            "shipment": {
              "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
              "outbound_tracking_number": "<outbound_tracking_number>",
              "outbound_tracking_url": "<outbound_tracking_url>",
              "inbound_tracking_number": "<inbound_tracking_number>",
              "inbound_tracking_url": "<inbound_tracking_url>",
              "outbound_courier": "usps",
              "inbound_courier": "usps",
              "notes": "<notes>",
              "created_at": "2020-01-01T00:00:00.000Z",
              "updated_at": "2020-01-01T00:00:00.000Z"
            },
            "created_at": "2020-01-01T00:00:00Z",
            "updated_at": "2020-01-01T00:00:00Z"
          }
        },
        "lab_test": {
          "name": "Lipids Panel",
          "description": "Cholesterol test",
          "method": "testkit"
        },
        "sample_id": "123456789",
        "notes": "This is a note",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "status": "collecting_sample",
        "events": [
          {
            "id": 1,
            "created_at": "2022-01-01T00:00:00Z",
            "status": "received.testkit.ordered"
          },
          {
            "id": 2,
            "created_at": "2022-01-02T00:00:00Z",
            "status": "received.testkit.requisition_created"
          },
          {
            "id": 3,
            "created_at": "2022-01-03T00:00:00Z",
            "status": "collecting_sample.testkit.transit_customer"
          }
        ],
        "origin": "initial",
        "order_transaction": {
          "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
          "status": "active",
          "orders": [
            {
              "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
              "created_at": "2020-01-01T00:00:00Z",
              "updated_at": "2020-01-01T00:00:00Z",
              "low_level_status": "transit_customer",
              "low_level_status_created_at": "2022-01-03T00:00:00Z",
              "origin": "initial"
            }
          ]
        }
      }
    ],
    "total": 1,
    "page": 1,
    "size": 50
  }
  ```
</ResponseExample>


# Get Team Physicians
Source: https://docs.junction.com/api-reference/lab-testing/get-team-physicians

GET /v2/team/{team_id}/physicians
Retrieve the list of physicians associated with a team. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.tryvital.io/v2/team/{team_id}/physicians \
    --header 'x-vital-api-key: <api-key>'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "first_name": "Jane",
      "last_name": "Smith",
      "npi": "1234567890"
    }
  ]
  ```
</ResponseExample>


# Import Order
Source: https://docs.junction.com/api-reference/lab-testing/import-order

POST /v3/order/import
Create or submit order import via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<Note>
  Patient name fields (`first_name`, `last_name`) must follow specific validation rules due to lab restrictions. See [Patient Name Validation](/lab/workflow/order-requirements#patient-name-validation) for complete details.
</Note>

<RequestExample>
  ```python Python theme={null}
  from junction import Billing, Gender, Junction, LabTestCollectionMethod, OrderSetRequest, PatientAddress, PatientDetailsWithValidation, PhysicianCreateRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.import_order(
      user_id="63661a2b-2bb3-4125-bb1a-b590f64f057f",
      billing_type=Billing.CLIENT_BILL,
      order_set=OrderSetRequest(lab_test_ids=[]),
      collection_method=LabTestCollectionMethod.WALK_IN_TEST,
      physician=PhysicianCreateRequest(first_name="Jane", last_name="Doe", npi=""),
      patient_details=PatientDetailsWithValidation(
          first_name="John",
          last_name="Doe",
          dob="2020-01-01",
          gender=Gender.MALE,
          phone_number="+1123456789",
          email="email@email.com",
      ),
      patient_address=PatientAddress(
          receiver_name="John Doe",
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
      ),
      sample_id="1234567890",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.importOrder({
      userId: "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      billingType: "client_bill",
      orderSet: {
          labTestIds: [],
      },
      collectionMethod: "walk_in_test",
      physician: {
          firstName: "Jane",
          lastName: "Doe",
          npi: "",
      },
      patientDetails: {
          firstName: "John",
          lastName: "Doe",
          dob: "2020-01-01",
          gender: "male",
          phoneNumber: "+1123456789",
          email: "email@email.com",
      },
      patientAddress: {
          receiverName: "John Doe",
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
      },
      sampleId: "1234567890",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.ImportOrderBody;
  import com.junction.api.types.Billing;
  import com.junction.api.types.Gender;
  import com.junction.api.types.LabTestCollectionMethod;
  import com.junction.api.types.OrderSetRequest;
  import com.junction.api.types.PatientAddress;
  import com.junction.api.types.PatientDetailsWithValidation;
  import com.junction.api.types.PhysicianCreateRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().importOrder(
      ImportOrderBody.builder()
          .userId("63661a2b-2bb3-4125-bb1a-b590f64f057f")
          .billingType(Billing.CLIENT_BILL)
          .orderSet(OrderSetRequest.builder().build())
          .collectionMethod(LabTestCollectionMethod.WALK_IN_TEST)
          .patientDetails(
              PatientDetailsWithValidation.builder()
                  .firstName("John")
                  .lastName("Doe")
                  .dob("2020-01-01")
                  .gender(Gender.MALE)
                  .phoneNumber("+1123456789")
                  .email("email@email.com")
                  .build()
          )
          .patientAddress(
              PatientAddress.builder()
                  .receiverName("John Doe")
                  .firstLine("123 Main St.")
                  .city("San Francisco")
                  .state("CA")
                  .zip("91189")
                  .country("US")
                  .build()
          )
          .sampleId("1234567890")
          .physician(
              PhysicianCreateRequest.builder()
                  .firstName("Jane")
                  .lastName("Doe")
                  .npi("")
                  .build()
          )
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.ImportOrder(context.TODO(), &junction.ImportOrderBody{
      UserId:           "63661a2b-2bb3-4125-bb1a-b590f64f057f",
      BillingType:      junction.BillingClientBill,
      OrderSet:         &junction.OrderSetRequest{},
      CollectionMethod: junction.LabTestCollectionMethodWalkInTest,
      Physician: &junction.PhysicianCreateRequest{
          FirstName: junction.String("Jane"),
          LastName:  junction.String("Doe"),
          Npi:       "",
      },
      PatientDetails: &junction.PatientDetailsWithValidation{
          FirstName:   "John",
          LastName:    "Doe",
          Dob:         "2020-01-01",
          Gender:      junction.GenderMale,
          PhoneNumber: "+1123456789",
          Email:       "email@email.com",
      },
      PatientAddress: &junction.PatientAddress{
          ReceiverName: "John Doe",
          FirstLine:    "123 Main St.",
          SecondLine:   junction.String("Apt. 208"),
          City:         "San Francisco",
          State:        "CA",
          Zip:          "91189",
          Country:      "US",
      },
      SampleId: "1234567890",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "order": {
          "user_id": "7ea94b27-a536-461f-ac4b-1c26c854d180",
          "id": "d3ec7ac7-2584-4254-9709-fc61d80635c4",
          "team_id": "59d4dfa1-e7c8-4d96-af15-74ca5ac5d189",
          "patient_details": {
              "first_name": "John",
              "last_name": "Doe",
              "dob": "1990-01-01T00:00:00+00:00",
              "gender": "male",
              "phone_number": "+11231234123",
              "email": "john@example.com"
          },
          "patient_address": {
              "receiver_name": "John Doe",
              "first_line": "123 Main Street",
              "second_line": null,
              "city": "New York",
              "state": "NY",
              "zip": "12345",
              "country": "US",
              "phone_number": "+11231234123"
          },
          "lab_test": {
              "id": "d0b479ec-afcc-4763-985c-4e23f17149f5",
              "slug": "59d4dfa1-testosterone",
              "name": "Testosterone",
              "sample_type": "serum",
              "method": "at_home_phlebotomy",
              "price": 0.0,
              "is_active": true,
              "status": "active",
              "fasting": false,
              "lab": null,
              "markers": null,
              "is_delegated": false,
              "auto_generated": false
          },
          "details": {
              "type": "at_home_phlebotomy",
              "data": {
                  "id": "21cb5e3f-25e3-443d-9fee-272de1393310",
                  "appointment_id": null,
                  "created_at": "2025-03-20T13:07:32+00:00",
                  "updated_at": "2025-03-20T13:07:32+00:00"
              }
          },
          "sample_id": "1234567890",
          "notes": null,
          "created_at": "2025-03-20T13:07:32+00:00",
          "updated_at": "2025-03-20T13:07:32+00:00",
          "events": [
              {
                  "id": 74769,
                  "created_at": "2025-03-20T13:07:32+00:00",
                  "status": "received.at_home_phlebotomy.ordered"
              },
              {
                  "id": 74770,
                  "created_at": "2025-03-20T13:07:32+00:00",
                  "status": "received.at_home_phlebotomy.requisition_bypassed"
              }
          ],
          "status": "received",
          "physician": {
              "first_name": "Jane",
              "last_name": "Doe",
              "npi": "123"
          },
          "health_insurance_id": null,
          "requisition_form_url": null,
          "priority": false,
          "shipping_details": null,
          "activate_by": null,
          "passthrough": null,
          "billing_type": "client_bill",
          "icd_codes": null
      },
      "status": "success",
      "message": "Order created successfully"
  }
  ```
</ResponseExample>


# Create Payor
Source: https://docs.junction.com/api-reference/lab-testing/insurance/create-payor

POST /v3/payor
Create or submit payor via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Create a new payor.

Allows the creation of new payors that don't already exist in Junction's system. Created payors are tied to the team that created them, so they will only be returned as search results when [searched](/api-reference/lab-testing/insurance/search-payor-get) by that same team.


# Get search diagnosis
Source: https://docs.junction.com/api-reference/lab-testing/insurance/search-diagnosis

GET /v3/insurance/search/diagnosis
Retrieve insurance search diagnosis via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/insurance/search/diagnosis?diagnosis_query=glucose' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.insurance.search_diagnosis(diagnosis_query="<query>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.insurance.searchDiagnosis({ diagnosisQuery: "<query>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.insurance.requests.SearchDiagnosisInsuranceRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.insurance().searchDiagnosis(
      SearchDiagnosisInsuranceRequest.builder()
          .diagnosisQuery("<query>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Insurance.SearchDiagnosis(context.TODO(), &junction.SearchDiagnosisInsuranceRequest{
      DiagnosisQuery: "<query>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  [
      {
          "diagnosis_code": "D55.0",
          "description": "ANEMIA DUE TO G6PD DEFICIENCY"
      },
      {
          "diagnosis_code": "D75.A",
          "description": "GLUC-6-PHOS DHYDRGNS DEF W/O ANEMIA"
      },
      {
          "diagnosis_code": "E74.810",
          "description": "GLUC TRANSPORT PROTEIN TYPE 1 DEFIC"
      },
      {
          "diagnosis_code": "E74.818",
          "description": "OTHER DISORDERS GLUCOSE TRANSPORT"
      }
  ]
  ```
</ResponseExample>


# Get search payor
Source: https://docs.junction.com/api-reference/lab-testing/insurance/search-payor-get

GET /v3/insurance/search/payor
Retrieve insurance search payor via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/insurance/search/payor?insurance_name=AETNA' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: {YOUR_KEY}'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "code": "AARPA",
      "name": "AARP",
      "aliases": [
        "AARP",
        "AARP"
      ],
      "org_address": {
        "first_line": "PO BOX 740819",
        "second_line": null,
        "country": "US",
        "zip": "30374",
        "city": "ATLANTA",
        "state": "GA"
      }
    }
  ]
  ```
</ResponseExample>


# Get Lab Report Parser Job
Source: https://docs.junction.com/api-reference/lab-testing/lab-report-parsing/get-lab-report-parser-job

GET /lab_report/v1/parser/job/{job_id}
Retrieve lab report parser job via the Junction API. Requires authentication with your team API key.

Retrieve the status and results of a lab report parsing job. When the job status is `completed`, the `data` field will contain the extracted lab results with LOINC matches.

**Job Status Values:**

* `upload_pending` - Job created, waiting for file upload
* `started` - File uploaded, parsing in progress
* `completed` - Parsing complete, results available in `data`
* `failed` - Parsing failed

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/lab_report/v1/parser/job/<job_id>' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_report.parser_get_job("<job_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labReport.parserGetJob({ jobId: "<job_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labReport().parserGetJob("<job_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabReport.ParserGetJob(context.TODO(), &junction.ParserGetJobLabReportRequest{
      JobId: "<job_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Status: %s\n", response.Status)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "data": {
      "metadata": {
        "patient_first_name": "John",
        "patient_last_name": "Doe",
        "dob": "1990-05-15",
        "lab_name": "Quest Diagnostics",
        "date_reported": "2024-01-15",
        "date_collected": "2024-01-14",
        "specimen_number": "SP123456"
      },
      "results": [
        {
          "test_name": "Glucose",
          "value": "95",
          "type": "numeric",
          "units": "mg/dL",
          "min_reference_range": 70,
          "max_reference_range": 100,
          "interpretation": "normal",
          "is_above_max_range": false,
          "is_below_min_range": false,
          "loinc_matches": [
            {
              "loinc_code": "2345-7",
              "loinc_name": "Glucose [Mass/volume] in Serum or Plasma",
              "display_name": "Glucose [Mass/volume] in Serum or Plasma",
              "aliases": ["Blood Sugar", "Fasting Glucose"],
              "confidence_score": 0.95
            }
          ]
        },
        {
          "test_name": "Hemoglobin A1c",
          "value": "5.4",
          "type": "numeric",
          "units": "%",
          "min_reference_range": 4.0,
          "max_reference_range": 5.6,
          "interpretation": "normal",
          "is_above_max_range": false,
          "is_below_min_range": false,
          "loinc_matches": [
            {
              "loinc_code": "4548-4",
              "loinc_name": "Hemoglobin A1c/Hemoglobin.total in Blood",
              "display_name": "Hemoglobin A1c/Hemoglobin.total in Blood",
              "aliases": ["HbA1c", "Glycated Hemoglobin"],
              "confidence_score": 0.98
            }
          ]
        }
      ]
    },
    "needs_human_review": false,
    "is_reviewed": false
  }
  ```
</ResponseExample>


# Create Lab Report Parser Job
Source: https://docs.junction.com/api-reference/lab-testing/lab-report-parsing/post-lab-report-parser-job

POST /lab_report/v1/parser/job
Create or submit lab report parser job via the Junction API. Requires authentication with your team API key.

Upload a lab report file (PDF, JPEG, or PNG) to create a parsing job. The job will extract lab results and match them to LOINC codes.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/lab_report/v1/parser/job' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --header 'Content-Type: multipart/form-data' \
       --form 'file=@/path/to/lab_report.pdf' \
       --form 'user_id=<user_id>' \
       --form 'needs_human_review=false'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  with open("lab_report.pdf", "rb") as f:
      data = client.lab_report.parser_create_job(
          file=[f],
          user_id="<user_id>",
          needs_human_review=False,
      )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";
  import * as fs from "fs";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labReport.parserCreateJob({
      file: [fs.createReadStream("lab_report.pdf")],
      userId: "<user_id>",
      needsHumanReview: false,
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labreport.requests.CreateLabReportParserJobBody;
  import java.io.File;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labReport().parserCreateJob(
      new File("lab_report.pdf"),
      CreateLabReportParserJobBody.builder()
          .userId("<user_id>")
          .needsHumanReview(false)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"
      "os"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  file, _ := os.Open("lab_report.pdf")
  defer file.Close()

  response, err := c.LabReport.ParserCreateJob(context.TODO(), &junction.CreateLabReportParserJobBody{
      File:             []io.Reader{file},
      UserId:           "<user_id>",
      NeedsHumanReview: junction.Bool(false),
  })
  if err != nil {
      return err
  }
  fmt.Printf("Job ID: %s\n", response.JobId)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "started",
    "data": null,
    "needs_human_review": false,
    "is_reviewed": false
  }
  ```
</ResponseExample>


# Get Collection Instructions PDF for Lab Test
Source: https://docs.junction.com/api-reference/lab-testing/lab-test-collection-instructions-pdf

GET /v3/lab_test/{lab_test_id}/collection_instruction_pdf
Retrieve lab test collection instructions PDF via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/lab_test/<lab_test_id>/collection_instruction_pdf' \
       --header 'accept: application/pdf' \
       --header 'x-vital-api-key: {YOUR_KEY}' \
       --output file.pdf
  ```
</RequestExample>


# Get Markers for Lab Test
Source: https://docs.junction.com/api-reference/lab-testing/lab-test-markers

GET /v3/lab_tests/{lab_test_id}/markers
Retrieve lab tests markers via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/lab_tests/{lab_test_id}/markers' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_markers_for_lab_test("<lab_test_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getMarkersForLabTest({ labTestId: "<lab_test_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getMarkersForLabTest("<lab_test_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetMarkersForLabTest(context.TODO(), &junction.GetMarkersForLabTestLabTestsRequest{
      LabTestId: "<lab_test_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "markers": [
        {
            "id": 202,
            "name": "Acetylcholine Receptor (AChR) Antibodies, Complete Profile with Reflex to MuSK Antibodies",
            "slug": "acetylcholine-receptor-achr-antibodies-complete-profile-with-reflex-to-musk-antibodies",
            "description": "Acetylcholine Receptor (AChR) Antibodies, Complete Profile with Reflex to MuSK Antibodies",
            "lab_id": 6,
            "provider_id": "165605",
            "type": "biomarker",
            "unit": null,
            "price": "N/A",
            "expected_results": [
                {
                    "id": 2938,
                    "name": "AChR Blocking Abs, Serum",
                    "slug": "achr-blocking-abs-serum",
                    "lab_id": 6,
                    "provider_id": "085927",
                    "loinc": {
                        "id": 3514,
                        "name": "Acetylcholine receptor blocking Ab Qn (S)",
                        "slug": "acetylcholine-receptor-blocking-ab-qn-s",
                        "code": "11561-8",
                        "unit": "%{inhibition}"
                    }
                },
                {
                    "id": 2939,
                    "name": "AChR Binding Abs, Serum",
                    "slug": "achr-binding-abs-serum",
                    "lab_id": 6,
                    "provider_id": "085904",
                    "loinc": {
                        "id": 3174,
                        "name": "Acetylcholine receptor binding Ab (S) [Moles/Vol]",
                        "slug": "acetylcholine-receptor-binding-ab-s-moles-vol",
                        "code": "11034-6",
                        "unit": "nmol/L"
                    }
                },
                {
                    "id": 2940,
                    "name": "AChR-modulating Ab",
                    "slug": "achr-modulating-ab",
                    "lab_id": 6,
                    "provider_id": "505199",
                    "loinc": {
                        "id": 61121,
                        "name": "Acetylcholine receptor modulation Ab FC Ql (S)",
                        "slug": "acetylcholine-receptor-modulation-ab-fc-ql-s",
                        "code": "99062-2",
                        "unit": null
                    }
                }
            ]
        }
    ],
    "total": 2,
    "page": 1,
    "size": 2
  }
  ```
</ResponseExample>


# Get Lab Accounts
Source: https://docs.junction.com/api-reference/lab-testing/lab_accounts

GET /v3/lab_test/lab_account
Retrieve lab test lab account via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/lab_test/lab_account' \
       --header 'x-vital-api-key: {YOUR_KEY}'
  ```
</RequestExample>


# Get Labels PDF
Source: https://docs.junction.com/api-reference/lab-testing/labels-pdf

GET /v3/order/{order_id}/labels/pdf
Retrieve order labels pdf via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/labels/pdf' \
       --header 'accept: application/pdf' \
       --header 'x-vital-api-key: {YOUR_KEY}' \
       --output file.pdf
  ```
</RequestExample>


# Get All Available Labs
Source: https://docs.junction.com/api-reference/lab-testing/labs

GET /v3/lab_tests/labs
Retrieve lab tests labs via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/lab_tests/labs' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_labs()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getLabs();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getLabs();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetLabs(context.TODO())
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "id": 1,
      "slug": "labcorp",
      "name": "LabCorp",
      "first_line_address": "123 Main St",
      "city": "San Francisco",
      "zipcode": "91789",
      "collection_methods": ["at_home_phlebotomy", "walk_in_test"],
      "sample_types": ["saliva", "serum"]
    }
  ]
  ```
</ResponseExample>


# Get Collection Instructions PDF for Order
Source: https://docs.junction.com/api-reference/lab-testing/order-collection-instructions-pdf

GET /v3/order/{order_id}/collection_instruction_pdf
Retrieve order collection instructions PDF via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/collection_instruction_pdf' \
       --header 'accept: application/pdf' \
       --header 'x-vital-api-key: {YOUR_KEY}' \
       --output file.pdf
  ```
</RequestExample>


# Get order PSC info
Source: https://docs.junction.com/api-reference/lab-testing/order-psc-info

GET /v3/order/{order_id}/psc/info
Retrieve order PSC info via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/psc/info' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_order_psc_info("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getOrderPscInfo({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getOrderPscInfo("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetOrderPscInfo(context.TODO(), &junction.GetOrderPscInfoLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "lab_id": 27,
    "slug": "labcorp",
    "patient_service_centers": [
      {
        "metadata": {
          "name": "LABCORP",
          "state": "AZ",
          "city": "Phoenix",
          "zip_code": "85006",
          "first_line": "1300 N 12th St",
          "second_line": "Ste 300",
          "phone_number": "480-878-3988",
          "fax_number": "844-346-5903",
          "hours": null
        },
        "distance": "25",
        "capabilities": ["stat"]
      }
    ]
  }
  ```
</ResponseExample>


# Get Markers for Order Set
Source: https://docs.junction.com/api-reference/lab-testing/order-set-marker

POST /v3/lab_tests/list_order_set_markers
Create or submit lab tests list order set markers via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/lab_tests/list_order_set_markers' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       -d '
       {
          "lab_test_ids": ["id"],
          "add_on": {
              "provider_ids": ["123"]
          }
       }
       '
  ```

  ```python Python theme={null}
  from junction import AddOnOrder, Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_markers_for_order_set(
      lab_test_ids=["<lab_test_id>"],
      add_on=AddOnOrder(provider_ids=["123"]),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getMarkersForOrderSet({
      body: {
          labTestIds: ["<lab_test_id>"],
          addOn: { providerIds: ["123"] },
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.types.AddOnOrder;
  import com.junction.api.types.OrderSetRequest;
  import java.util.List;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getMarkersForOrderSet(
      OrderSetRequest.builder()
          .labTestIds(List.of("<lab_test_id>"))
          .addOn(
              AddOnOrder.builder()
                  .providerIds(List.of("123"))
                  .build()
          )
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetMarkersForOrderSet(context.TODO(), &junction.GetMarkersForOrderSetLabTestsRequest{
      Body: &junction.OrderSetRequest{
          LabTestIds: []string{"<lab_test_id>"},
          AddOn: &junction.AddOnOrder{
              ProviderIds: []string{"123"},
          },
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "markers": [
        {
            "id": 202,
            "name": "Acetylcholine Receptor (AChR) Antibodies, Complete Profile with Reflex to MuSK Antibodies",
            "slug": "acetylcholine-receptor-achr-antibodies-complete-profile-with-reflex-to-musk-antibodies",
            "description": "Acetylcholine Receptor (AChR) Antibodies, Complete Profile with Reflex to MuSK Antibodies",
            "lab_id": 6,
            "provider_id": "165605",
            "type": "biomarker",
            "unit": null,
            "price": "N/A",
            "expected_results": [
                {
                    "id": 2938,
                    "name": "AChR Blocking Abs, Serum",
                    "slug": "achr-blocking-abs-serum",
                    "lab_id": 6,
                    "provider_id": "085927",
                    "loinc": {
                        "id": 3514,
                        "name": "Acetylcholine receptor blocking Ab Qn (S)",
                        "slug": "acetylcholine-receptor-blocking-ab-qn-s",
                        "code": "11561-8",
                        "unit": "%{inhibition}"
                    }
                },
                {
                    "id": 2939,
                    "name": "AChR Binding Abs, Serum",
                    "slug": "achr-binding-abs-serum",
                    "lab_id": 6,
                    "provider_id": "085904",
                    "loinc": {
                        "id": 3174,
                        "name": "Acetylcholine receptor binding Ab (S) [Moles/Vol]",
                        "slug": "acetylcholine-receptor-binding-ab-s-moles-vol",
                        "code": "11034-6",
                        "unit": "nmol/L"
                    }
                },
                {
                    "id": 2940,
                    "name": "AChR-modulating Ab",
                    "slug": "achr-modulating-ab",
                    "lab_id": 6,
                    "provider_id": "505199",
                    "loinc": {
                        "id": 61121,
                        "name": "Acetylcholine receptor modulation Ab FC Ql (S)",
                        "slug": "acetylcholine-receptor-modulation-ab-fc-ql-s",
                        "code": "99062-2",
                        "unit": null
                    }
                }
            ]
        }
    ],
    "total": 2,
    "page": 1,
    "size": 2
  }
  ```
</ResponseExample>


# Get order transaction
Source: https://docs.junction.com/api-reference/lab-testing/order-transactions/get-order-transaction

GET /v3/order_transaction/{transaction_id}
Retrieve order transaction via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.order_transaction.get_transaction("<transaction_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.orderTransaction.getTransaction({ transactionId: "<transaction_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.orderTransaction().getTransaction("<transaction_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.OrderTransaction.GetTransaction(context.TODO(), &junction.GetTransactionOrderTransactionRequest{
      TransactionId: "<transaction_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
    "team_id": "cbb64555-af07-46c1-be09-ef89308e9b60",
    "status": "active",
    "orders": [
      {
        "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "low_level_status": "transit_customer",
        "low_level_status_created_at": "2022-01-03T00:00:00Z",
        "origin": "initial"
      }
    ]
  }
  ```
</ResponseExample>


# Update a Test
Source: https://docs.junction.com/api-reference/lab-testing/patch-lab-test

PATCH /v3/lab_tests/{lab_test_id}
Partially update lab tests via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.update_lab_test(
      "<lab_test_id>",
      name="<name>",
      active=True,
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.updateLabTest({
      labTestId: "<lab_test_id>",
      name: "<name>",
      active: true,
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.UpdateLabTestRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().updateLabTest(
      "<lab_test_id>",
      UpdateLabTestRequest.builder()
          .name("<name>")
          .active(true)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.UpdateLabTest(context.TODO(), &junction.UpdateLabTestRequest{
      LabTestId: "<lab_test_id>",
      Name:      junction.String("<name>"),
      Active:    junction.Bool(true),
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "5af405fc-fafb-49e8-b0bf-fe9d91df6a7d",
    "slug": "a94e3005-new-test",
    "name": "New test",
    "sample_type": "serum",
    "method": "at_home_phlebotomy",
    "price": 0.0,
    "is_active": true,
    "status": "active",
    "fasting": false,
    "lab": {
      "id": 26,
      "slug": "quest",
      "name": "Quest",
      "first_line_address": "100 Tri State International #100",
      "city": "Lincolnshire",
      "zipcode": "60069",
      "collection_methods": ["at_home_phlebotomy", "walk_in_test"],
      "sample_types": ["serum", "saliva", "urine"]
    },
    "markers": [
      {
        "id": 6859,
        "name": "17-Hydroxyprogesterone",
        "slug": "17-hydroxyprogesterone",
        "description": "17-Hydroxyprogesterone",
        "lab_id": 26,
        "provider_id": "17180",
        "type": "biomarker",
        "unit": null,
        "price": "N/A",
        "aoe": null,
        "a_la_carte_enabled": true
      },
      {
        "id": 8213,
        "name": "17-Hydroxypregnenolone",
        "slug": "17-hydroxypregnenolone",
        "description": "17-Hydroxypregnenolone",
        "lab_id": 26,
        "provider_id": "8352",
        "type": "biomarker",
        "unit": null,
        "price": "N/A",
        "aoe": null,
        "a_la_carte_enabled": true
      }
    ],
    "is_delegated": false,
    "auto_generated": false
  }
  ```
</ResponseExample>


# Update an order
Source: https://docs.junction.com/api-reference/lab-testing/patch-order

PATCH /v3/order/{order_id}
Update an order via the Junction API. Requires authentication with your team API key.

<Note>
  You can only update an order while its [low-level status](/lab/workflow/lab-test-lifecycle#statuses) is `ordered` or `awaiting_registration`.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.sandbox.junction.com/v3/order/<order_id> \
    --header 'x-vital-api-key: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
      "activate_by": "2026-06-01"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "order": {
      "id": "0ee312e2-6773-4a21-a6e1-506882cd98ed",
      "team_id": "cbb64555-af07-46c1-be09-ef89308e9b60",
      "user_id": "94e2d9f2-d600-4a23-9f08-536df378e2c7",
      "activate_by": "2026-06-01",
      "status": "received"
    },
    "status": "success",
    "message": "order updated"
  }
  ```
</ResponseExample>


# Create a Test
Source: https://docs.junction.com/api-reference/lab-testing/post-test

POST /v3/lab_tests
Create or submit lab tests via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction, LabTestCollectionMethod
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.create(
      name="<name>",
      method=LabTestCollectionMethod.TESTKIT,
      description="<description>",
      provider_ids=["000110"],
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.create({
      name: "<name>",
      method: "testkit",
      description: "<description>",
      providerIds: ["000110"],
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.CreateLabTestRequest;
  import com.junction.api.types.LabTestCollectionMethod;
  import java.util.List;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().create(
      CreateLabTestRequest.builder()
          .name("<name>")
          .method(LabTestCollectionMethod.TESTKIT)
          .description("<description>")
          .providerIds(List.of("000110"))
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.Create(context.TODO(), &junction.CreateLabTestRequest{
      Name:        "<name>",
      Method:      junction.LabTestCollectionMethodTestkit,
      Description: "<description>",
      ProviderIds: []string{"000110"},
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "2e82a1d4-e2e6-421c-9c54-2210d667ce48",
    "slug": "a94e3005-labcorp-panel",
    "name": "Labcorp panel",
    "sample_type": "serum",
    "method": "at_home_phlebotomy",
    "price": 0.0,
    "is_active": true,
    "status": "active",
    "fasting": false,
    "lab": {
      "id": 27,
      "slug": "labcorp",
      "name": "Labcorp",
      "first_line_address": "labcorp address",
      "city": "Lincolnshire",
      "zipcode": "60069",
      "collection_methods": ["at_home_phlebotomy", "walk_in_test"],
      "sample_types": ["serum", "saliva", "urine"]
    },
    "markers": [
      {
        "id": 195,
        "name": "Abnormal Bleeding Profile",
        "slug": "abnormal-bleeding-profile",
        "description": "Abnormal Bleeding Profile",
        "lab_id": 27,
        "provider_id": "116004",
        "type": "biomarker",
        "unit": null,
        "price": "N/A",
        "aoe": null,
        "a_la_carte_enabled": true
      }
    ],
    "is_delegated": false,
    "auto_generated": false
  }
  ```
</ResponseExample>


# Get PSC info
Source: https://docs.junction.com/api-reference/lab-testing/psc-info

GET /v3/order/psc/info
Retrieve order PSC info via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/psc/info?zip_code=85004&lab_id=27' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_psc_info(zip_code="85004", lab_id=27)
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPscInfo({ zipCode: "85004", labId: 27 });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.GetPscInfoLabTestsRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPscInfo(
      GetPscInfoLabTestsRequest.builder()
          .zipCode("85004")
          .labId(27)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPscInfo(context.TODO(), &junction.GetPscInfoLabTestsRequest{
      ZipCode: "85004",
      LabId:   27,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "lab_id": 27,
    "slug": "labcorp",
    "patient_service_centers": [
      {
        "metadata": {
          "name": "LABCORP",
          "state": "AZ",
          "city": "Phoenix",
          "zip_code": "85006",
          "first_line": "1300 N 12th St",
          "second_line": "Ste 300",
          "phone_number": "480-878-3988",
          "fax_number": "844-346-5903",
          "hours": null
        },
        "distance": "25",
        "capabilities": ["stat"]
      }
    ]
  }
  ```
</ResponseExample>


# PSC Appointment Availability
Source: https://docs.junction.com/api-reference/lab-testing/psc-scheduling/appointment-psc-availability

POST /v3/order/psc/appointment/availability
Create or submit order PSC appointment availability via the Junction API. Requires authentication with your team API key.

<Tip>
  Use the `allow_stale` parameter for faster responses via the Availability Cache. This feature is currently in closed beta. See [PSC Appointment Scheduling](/lab/walk-in/psc-appointment-scheduling#availability-cache) for more info.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/psc/appointment/availability' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: {YOUR_KEY}' \
       --data '
  {
      "lab": "quest",
      "zip_code": "85004",
      "radius": "25"
  }
  '
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "slots": [
          {
              "location": {
                  "location": {
                      "lng": -112.0568538,
                      "lat": 33.4631386
                  },
                  "address": {
                      "first_line": "1300 N 12th St",
                      "second_line": "Ste 300",
                      "city": "Phoenix",
                      "state": "AZ",
                      "zip_code": "85006",
                  },
                  "code": "23070",
                  "name": "QUEST",
                  "iana_timezone": "America/Los_Angeles"
              },
              "date":"2023-05-09",
              "slots": [
                  {
                      "booking_key": "foo123",
                      "start": "2023-05-09T17:00:00+00:00",
                      "end": "2023-05-09T19:00:00+00:00",
                      "expires_at": "2023-05-09T12:39:57.827000+00:00",
                      "price": 0.0,
                      "is_priority": true,
                      "num_appointments_available": 1
                  },
                  ...
              ],
          },
          {
              "location": {
                  "location": {
                      "lng": -112.0568538,
                      "lat": 33.4631386
                  },
                  "address": {
                      "first_line": "1300 N 12th St",
                      "second_line": "Ste 300",
                      "city": "Phoenix",
                      "state": "AZ",
                      "zip_code": "85006",
                  },
                  "code": "23070",
                  "name": "QUEST"
              },
              "date":"2023-05-10",
              "slots": [
                  {
                      "booking_key": "bar456",
                      "start": "2023-05-10T12:00:00+00:00",
                      "end": "2023-05-10T14:00:00+00:00",
                      "expires_at": "2023-05-09T12:39:57.852000+00:00",
                      "price": 0.0,
                      "is_priority": true,
                      "num_appointments_available": 1
                  },
                  ...
              ],
          },
      ],
      "timezone": null
  }
  ```
</ResponseExample>


# Book PSC Appointment
Source: https://docs.junction.com/api-reference/lab-testing/psc-scheduling/appointment-psc-booking

POST /v3/order/{order_id}/psc/appointment/book
Create or submit order PSC appointment book via the Junction API. Requires authentication with your team API key.

<Tip>
  This endpoint supports optional `idempotency_key` and Async Confirmation features. These features are currently in closed beta. See [PSC Appointment Scheduling](/lab/walk-in/psc-appointment-scheduling#beta-features) for more info.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/psc/appointment/book' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "booking_key": "foo123"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.book_psc_appointment(
      "<order_id>",
      booking_key="<booking_key>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.bookPscAppointment({
      orderId: "<order_id>",
      body: {
          bookingKey: "<booking_key>",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.types.AppointmentBookingRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().bookPscAppointment(
      "<order_id>",
      AppointmentBookingRequest.builder()
          .bookingKey("<booking_key>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.BookPscAppointment(context.TODO(), &junction.BookPscAppointmentLabTestsRequest{
      OrderId: "<order_id>",
      Body: &junction.AppointmentBookingRequest{
          BookingKey: "<booking_key>",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-15T16:00:00+00:00",
      "end_at": "2023-05-15T18:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "patient_service_center",
      "provider": "quest",
      "status": "pending",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true,
      "external_id": "ABCDEF"
  }
  ```
</ResponseExample>


# PSC Appointment Cancellation Reasons
Source: https://docs.junction.com/api-reference/lab-testing/psc-scheduling/appointment-psc-cancellation-reasons

GET /v3/order/psc/appointment/cancellation-reasons
Retrieve order PSC appointment cancellation-reasons via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/psc/appointment/cancellation-reasons' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_psc_appointment_cancellation_reason()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPscAppointmentCancellationReason();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPscAppointmentCancellationReason();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPscAppointmentCancellationReason(context.TODO())
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  [
     {
        "id": "5c0257ef-6fea-4a22-b20a-3ddab573d5c9",
        "name": "Other",
        "is_refundable": true
     }
  ]
  ```
</ResponseExample>


# Cancel PSC Appointment
Source: https://docs.junction.com/api-reference/lab-testing/psc-scheduling/appointment-psc-cancelling

PATCH /v3/order/{order_id}/psc/appointment/cancel
Partially update order PSC appointment cancel via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/psc/appointment/cancel' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "cancellation_reason_id": "7dfd7da5-ed6e-40bb-a7e4-c8003f0c10a9"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.cancel_psc_appointment(
      "<order_id>",
      cancellation_reason_id="<cancellation_reason_id>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.cancelPscAppointment({
      orderId: "<order_id>",
      cancellationReasonId: "<cancellation_reason_id>",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.VitalCoreClientsLabTestGetlabsSchemaAppointmentCancelRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().cancelPscAppointment(
      "<order_id>",
      VitalCoreClientsLabTestGetlabsSchemaAppointmentCancelRequest.builder()
          .cancellationReasonId("<cancellation_reason_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CancelPscAppointment(context.TODO(), &junction.VitalCoreClientsLabTestGetlabsSchemaAppointmentCancelRequest{
      OrderId:              "<order_id>",
      CancellationReasonId: "<cancellation_reason_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-17T20:00:00+00:00",
      "end_at": "2023-05-17T22:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "patient_service_center",
      "provider": "quest",
      "status": "cancelled",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true
  }
  ```
</ResponseExample>


# Reschedule PSC Appointment
Source: https://docs.junction.com/api-reference/lab-testing/psc-scheduling/appointment-psc-rescheduling

PATCH /v3/order/{order_id}/psc/appointment/reschedule
Partially update order PSC appointment reschedule via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/psc/appointment/reschedule' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "booking_key": "bar456"
  }
  '
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.reschedule_psc_appointment(
      "<order_id>",
      booking_key="<booking_key>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.reschedulePscAppointment({
      orderId: "<order_id>",
      body: {
          bookingKey: "<booking_key>",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.ReschedulePscAppointmentLabTestsRequest;
  import com.junction.api.types.AppointmentRescheduleRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().reschedulePscAppointment(
      "<order_id>",
      ReschedulePscAppointmentLabTestsRequest.builder()
          .body(
              AppointmentRescheduleRequest.builder()
                  .bookingKey("<booking_key>")
                  .build()
          )
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.ReschedulePscAppointment(context.TODO(), &junction.ReschedulePscAppointmentLabTestsRequest{
      OrderId: "<order_id>",
      Body: &junction.AppointmentRescheduleRequest{
          BookingKey: "<booking_key>",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit":"14"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-17T20:00:00+00:00",
      "end_at": "2023-05-17T22:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "patient_service_center",
      "provider": "quest",
      "status": "confirmed",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true
  }
  ```
</ResponseExample>


# Get PSC Appointment
Source: https://docs.junction.com/api-reference/lab-testing/psc-scheduling/get-psc-appointment

GET /v3/order/{order_id}/psc/appointment
Retrieve order PSC appointment via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/413d7205-f8a9-42ed-aa4a-edb99e481ca0/psc/appointment' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_psc_appointment("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPscAppointment({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPscAppointment("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPscAppointment(context.TODO(), &junction.GetPscAppointmentLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
      "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
      "address": {
          "first_line": "West Lincoln Street",
          "second_line": "",
          "city": "Phoenix",
          "state": "AZ",
          "zip_code": "85004",
          "unit": "14"
      },
      "location": {
          "lng": -112.0772235,
          "lat": 33.4421912
      },
      "start_at": "2023-05-17T20:00:00+00:00",
      "end_at": "2023-05-17T22:00:00+00:00",
      "iana_timezone": "America/Phoenix",
      "type": "patient_service_center",
      "provider": "quest",
      "status": "confirmed",
      "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
      "can_reschedule": true
  }
  ```
</ResponseExample>


# Register Testkit Order
Source: https://docs.junction.com/api-reference/lab-testing/register-order

POST /v3/order/testkit/register
Create or submit order testkit register via the Junction API. Requires authentication with your team API key.

<Note>
  Patient name fields (`first_name`, `last_name`) must follow specific validation rules due to lab restrictions. See [Patient Name Validation](/lab/workflow/order-requirements#patient-name-validation) for complete details.
</Note>

<RequestExample>
  ```python Python theme={null}
  from junction import Gender, Junction, PatientAddressWithValidation, PatientDetailsWithValidation
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.testkit.register(
      sample_id="<sample_id>",
      user_id="<user_id>",
      patient_details=PatientDetailsWithValidation(
          first_name="John",
          last_name="Doe",
          dob="1990-01-01",
          gender=Gender.MALE,
          phone_number="+1123456789",
          email="email@email.com",
      ),
      patient_address=PatientAddressWithValidation(
          receiver_name="John Doe",
          first_line="123 Main St",
          second_line="Apt. 1",
          city="San Francisco",
          state="CA",
          zip="94111",
          country="US",
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.testkit.register({
      sampleId: "<sample_id>",
      userId: "<user_id>",
      patientDetails: {
          firstName: "John",
          lastName: "Doe",
          dob: "1990-01-01",
          gender: "male",
          phoneNumber: "+1123456789",
          email: "email@email.com",
      },
      patientAddress: {
          receiverName: "John Doe",
          firstLine: "123 Main St",
          secondLine: "Apt. 1",
          city: "San Francisco",
          state: "CA",
          zip: "94111",
          country: "US",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.testkit.requests.RegisterTestkitRequest;
  import com.junction.api.types.Gender;
  import com.junction.api.types.PatientAddressWithValidation;
  import com.junction.api.types.PatientDetailsWithValidation;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.testkit().register(
      RegisterTestkitRequest.builder()
          .sampleId("<sample_id>")
          .patientDetails(
              PatientDetailsWithValidation.builder()
                  .firstName("John")
                  .lastName("Doe")
                  .dob("1990-01-01")
                  .gender(Gender.MALE)
                  .phoneNumber("+1123456789")
                  .email("email@email.com")
                  .build()
          )
          .patientAddress(
              PatientAddressWithValidation.builder()
                  .firstLine("123 Main St")
                  .city("San Francisco")
                  .state("CA")
                  .zip("94111")
                  .country("US")
                  .build()
          )
          .userId("<user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Testkit.Register(context.TODO(), &junction.RegisterTestkitRequest{
      SampleId: "<sample_id>",
      UserId:   junction.String("<user_id>"),
      PatientDetails: &junction.PatientDetailsWithValidation{
          FirstName:   "John",
          LastName:    "Doe",
          Dob:         "1990-01-01",
          Gender:      junction.GenderMale,
          PhoneNumber: "+1123456789",
          Email:       "email@email.com",
      },
      PatientAddress: &junction.PatientAddressWithValidation{
          ReceiverName: junction.String("John Doe"),
          FirstLine:    "123 Main St",
          SecondLine:   junction.String("Apt. 1"),
          City:         "San Francisco",
          State:        "CA",
          Zip:          "94111",
          Country:      "US",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```

  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/testkit/register' \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --data '
  {
      "user_id":"63661a2b-2bb3-4125-bb1a-b590f64f057f",
      "sample_id":"123123123",
      "patient_details":{
          "first_name": "John",
          "last_name": "Doe",
          "dob": "2020-01-01",
          "gender": "male",
          "phone_number": "+1123456789",
          "email": "email@email.com"
      },
      "patient_address":{
        "receiver_name": "John Doe",
        "street": "123 Main St.",
        "street_number": "Apt. 208",
        "city": "San Francisco",
        "state": "CA",
        "zip": "91189",
        "country": "US"
      },
      "consents":[
        {"consentType": "terms-of-use"},
        {"consentType": "telehealth-informed-consent"},
        {"consentType": "notice-of-privacy-practices"}
      ],
      "physician":{
        "first_name": "Doctor",
        "last_name": "Doc",
        "npi": "123123123"
      }
  }
  '
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "order": {
      "id": "96edc6ef-3b2c-412b-b9e5-96f361f93aec",
      "team_id": "b080b20c-e162-4cf1-9c7d-8faee72ee08e",
      "user_id": "9f1e094e-1641-466b-b668-d4d3300e569f",
      "patient_details": {
        "first_name": "John",
        "last_name": "Doe",
        "phone_number": "+11234567890",
        "email": "doe@email.com",
        "dob": "2020-01-01",
        "gender": "male"
      },
      "patient_address": {
        "receiver_name": "John Doe",
        "first_line": "123 Main St.",
        "second_line": "Apt. 208",
        "city": "San Francisco",
        "state": "CA",
        "zip": "91189",
        "country": "United States",
        "phone_number": "+11234567890"
      },
      "shipping_details": {
        "receiver_name": "John Doe",
        "first_line": "123 Main St.",
        "second_line": "Apt. 208",
        "city": "San Francisco",
        "state": "CA",
        "zip": "91189",
        "country": "United States",
        "phone_number": "+11234567890"
      },
      "details": {
        "type": "testkit",
        "data": {
          "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
          "shipment": {
            "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
            "outbound_tracking_number": "<outbound_tracking_number>",
            "outbound_tracking_url": "<outbound_tracking_url>",
            "inbound_tracking_number": "<inbound_tracking_number>",
            "inbound_tracking_url": "<inbound_tracking_url>",
            "outbound_courier": "usps",
            "inbound_courier": "usps",
            "notes": "<notes>",
            "created_at": "2020-01-01T00:00:00.000Z",
            "updated_at": "2020-01-01T00:00:00.000Z"
          },
          "created_at": "2020-01-01T00:00:00Z",
          "updated_at": "2020-01-01T00:00:00Z"
        }
      },
      "lab_test": {
        "name": "Lipids Panel",
        "description": "Cholesterol test",
        "method": "testkit"
      },
      "sample_id": "123456789",
      "health_insurance_id": "7695cc28-f9e5-400d-95d2-ec7d9ec580df",
      "notes": "This is a note",
      "created_at": "2020-01-01T00:00:00Z",
      "updated_at": "2020-01-01T00:00:00Z",
      "status": "received",
      "events": [
        {
          "id": 1,
          "created_at": "2022-01-01T00:00:00Z",
          "status": "received.testkit.ordered"
        },
        {
          "id": 2,
          "created_at": "2022-01-02T00:00:00Z",
          "status": "received.testkit.awaiting_registration"
        },
        {
          "id": 3,
          "created_at": "2022-01-02T00:00:00Z",
          "status": "received.testkit.requisition_created"
        },
        {
          "id": 4,
          "created_at": "2022-01-03T00:00:00Z",
          "status": "received.testkit.testkit_registered"
        }
      ]
    },
    "status": "string",
    "message": "string"
  }
  ```
</ResponseExample>


# Get Requisition Form PDF
Source: https://docs.junction.com/api-reference/lab-testing/requisition-pdf

GET /v3/order/{order_id}/requisition/pdf
Retrieve order requisition pdf via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/requisition/pdf' \
       --header 'accept: application/pdf' \
       --header 'x-vital-api-key: YOUR_API_KEY' \
       --output file.pdf
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_order_requistion_pdf("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getOrderRequistionPdf({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getOrderRequistionPdf("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetOrderRequistionPdf(context.TODO(), &junction.GetOrderRequistionPdfLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Resend order webhooks
Source: https://docs.junction.com/api-reference/lab-testing/resend-order-events

POST /v3/order/resend_events
Replay the latest webhook event for one or more orders via the Junction API.

<Note>
  At least one of `order_ids` or `start_at` is required. When using `start_at`, `end_at` defaults to the current time and the window cannot exceed 60 days.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.sandbox.junction.com/v3/order/resend_events \
    --header 'x-vital-api-key: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
      "order_ids": ["0ee312e2-6773-4a21-a6e1-506882cd98ed"]
    }'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.order.resend_events(
      order_ids=["0ee312e2-6773-4a21-a6e1-506882cd98ed"],
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.order.resendEvents({
      orderIds: ["0ee312e2-6773-4a21-a6e1-506882cd98ed"],
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.order.requests.ResendWebhookBody;
  import java.util.List;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.order().resendEvents(
      ResendWebhookBody.builder()
          .orderIds(List.of("0ee312e2-6773-4a21-a6e1-506882cd98ed"))
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Order.ResendEvents(context.TODO(), &junction.ResendWebhookBody{
      OrderIds: []string{"0ee312e2-6773-4a21-a6e1-506882cd98ed"},
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "order_ids": ["0ee312e2-6773-4a21-a6e1-506882cd98ed"]
  }
  ```
</ResponseExample>


# Get Order Transaction Results
Source: https://docs.junction.com/api-reference/lab-testing/results/get-order-transaction-results

GET /v3/order_transaction/{transaction_id}/result
Retrieve order transaction result via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order_transaction/<transaction_id>/result' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.order_transaction.get_transaction_result("<transaction_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.orderTransaction.getTransactionResult({ transactionId: "<transaction_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.orderTransaction().getTransactionResult("<transaction_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.OrderTransaction.GetTransactionResult(context.TODO(), &junction.GetTransactionResultOrderTransactionRequest{
      TransactionId: "<transaction_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "metadata": {
      "age": 19,
      "dob": "18/08/1993",
      "clia_number": "12331231",
      "patient": "Bob Smith",
      "provider": "Dr. Jack Smith",
      "laboratory": "LabCorp",
      "date_reported": "2020-01-01",
      "date_collected": "2022-02-02",
      "specimen_number": "123131",
      "date_received": "2022-01-01",
      "status": "final",
      "interpretation": "abnormal"
    },
    "results": [
      {
        "name": "Sex Horm Binding Glob, Serum",
        "slug": "sex-horm-binding-glob-serum",
        "value": 30.4,
        "result": "30.4",
        "type": "numeric",
        "unit": "nmol/L",
        "timestamp": "2024-10-31T09:08:00+00:00",
        "notes": "Final",
        "min_range_value": 24.6,
        "max_range_value": 122,
        "is_above_max_range": false,
        "is_below_min_range": false,
        "interpretation": "normal",
        "loinc": "13967-5",
        "loinc_slug": "sex-hormone-binding-globulin-moles-vol",
        "provider_id": "082016",
        "source_markers": [
          {
            "marker_id": 229,
            "name": "Testosterone Free, Profile I",
            "slug": "testosterone-free-profile-i",
            "provider_id": "140226"
          }
        ]
      }
    ],
    "order_transaction": {
      "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
      "status": "completed",
      "orders": [
        {
          "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
          "created_at": "2020-01-01T00:00:00Z",
          "updated_at": "2022-01-04T00:00:00Z",
          "low_level_status": "redraw_available",
          "low_level_status_created_at": "2022-01-04T00:00:00Z",
          "origin": "initial"
        },
        {
          "id": "15db29d7-ffeb-4540-9513-3c38f26b87aa",
          "created_at": "2020-01-10T00:00:00Z",
          "updated_at": "2022-01-14T00:00:00Z",
          "low_level_status": "completed",
          "low_level_status_created_at": "2022-01-14T00:00:00Z",
          "origin": "redraw"
        }
      ]
    }
  }
  ```
</ResponseExample>


# Get Order Transaction Results PDF
Source: https://docs.junction.com/api-reference/lab-testing/results/get-order-transaction-results-pdf

GET /v3/order_transaction/{transaction_id}/result/pdf
Retrieve order transaction result pdf via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order_transaction/<transaction_id>/result/pdf' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.order_transaction.get_transaction_result_pdf("<transaction_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.orderTransaction.getTransactionResultPdf({ transactionId: "<transaction_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.orderTransaction().getTransactionResultPdf("<transaction_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.OrderTransaction.GetTransactionResultPdf(context.TODO(), &junction.GetTransactionResultPdfOrderTransactionRequest{
      TransactionId: "<transaction_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  "PDF with results"
  ```
</ResponseExample>


# Get Results
Source: https://docs.junction.com/api-reference/lab-testing/results/get-results

GET /v3/order/{order_id}/result
Retrieve order result via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/result' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_result_raw("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getResultRaw({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getResultRaw("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetResultRaw(context.TODO(), &junction.GetResultRawLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "metadata": {
      "age": 19,
      "dob": "18/08/1993",
      "clia_number": "12331231",
      "patient": "Bob Smith",
      "provider": "Dr. Jack Smith",
      "laboratory": "LabCorp",
      "date_reported": "2020-01-01",
      "date_collected": "2022-02-02",
      "specimen_number": "123131",
      "date_received": "2022-01-01",
      "status": "final",
      "interpretation": "abnormal"
    },
    "results": [
      {
        "name": "Sex Horm Binding Glob, Serum",
        "slug": "sex-horm-binding-glob-serum",
        "value": 30.4,
        "result": "30.4",
        "type": "numeric",
        "unit": "nmol/L",
        "timestamp": "2024-10-31T09:08:00+00:00",
        "notes": "Final",
        "min_range_value": 24.6,
        "max_range_value": 122,
        "is_above_max_range": false,
        "is_below_min_range": false,
        "interpretation": "normal",
        "loinc": "13967-5",
        "loinc_slug": "sex-hormone-binding-globulin-moles-vol",
        "provider_id": "082016",
        "source_markers": [
          {
            "marker_id": 229,
            "name": "Testosterone Free, Profile I",
            "slug": "testosterone-free-profile-i",
            "provider_id": "140226"
          }
        ]
      }
    ],
    "order_transaction": {
      "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
      "status": "completed",
      "orders": [
        {
          "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
          "created_at": "2020-01-01T00:00:00Z",
          "updated_at": "2022-02-04T00:00:00Z",
          "low_level_status": "completed",
          "low_level_status_created_at": "2022-02-04T00:00:00Z",
          "origin": "initial"
        }
      ]
    }
  }
  ```
</ResponseExample>


# Get Results Metadata
Source: https://docs.junction.com/api-reference/lab-testing/results/get-results-metadata

GET /v3/order/{order_id}/result/metadata
Retrieve order result metadata via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/result/metadata' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_result_metadata("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getResultMetadata({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getResultMetadata("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetResultMetadata(context.TODO(), &junction.GetResultMetadataLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "age": 19,
    "dob": "18/08/1993",
    "clia_number": "12331231",
    "patient": "Bob Smith",
    "provider": "Dr. Jack Smith",
    "laboratory": "Quest Diagnostics",
    "date_reported": "2020-01-01",
    "date_collected": "2022-02-02",
    "specimen_number": "123131",
    "date_received": "2022-01-01",
    "status": "final",
    "interpretation": "normal"
  }
  ```
</ResponseExample>


# Get Results PDF
Source: https://docs.junction.com/api-reference/lab-testing/results/get-results-pdf

GET /v3/order/{order_id}/result/pdf
Retrieve order result pdf via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url '{{BASE_URL}}/v3/order/<order_id>/result/pdf' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: YOUR_API_KEY'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_result_pdf("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getResultPdf({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getResultPdf("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetResultPdf(context.TODO(), &junction.GetResultPdfLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  "Returns PDF of test data - <specimen_number>.pdf"
  ```
</ResponseExample>


# Simulate Order
Source: https://docs.junction.com/api-reference/lab-testing/simulate-order

POST /v3/order/{order_id}/test
Create or submit order test via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url '{{BASE_URL}}/v3/order/<order_id>/test' \
       --header 'x-vital-api-key: {YOUR_KEY}'
  ```
</RequestExample>


# Get Available Tests
Source: https://docs.junction.com/api-reference/lab-testing/tests

GET /v3/lab_tests
Retrieve lab tests via the Junction API. Requires authentication with your team API key.

> **Deprecated:** This endpoint is deprecated. Please use the paginated version `/v3/lab_test` instead.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.get();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().get();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.Get(context.TODO(), &junction.GetLabTestsRequest{})
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "lab_test": {
        "name": "Lipids Panel",
        "description": "Cholesterol test",
        "sample_type": "dried blood spot",
        "method": "testkit",
        "price": 10,
        "is_active": true,
        "lab": {
          "slug": "USSL",
          "name": "US Specialty Lab",
          "first_line_address": "123 Main St",
          "city": "New York",
          "zipcode": "10001"
        },
        "markers": [
          {
            "name": "Thyroid Stimulating Hormone",
            "slug": "tsh",
            "description": ""
          }
        ]
      }
    }
  ]
  ```
</ResponseExample>


# Get Available Tests
Source: https://docs.junction.com/api-reference/lab-testing/tests-paginated

GET /v3/lab_test
Retrieve lab test via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get_paginated()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.getPaginated();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().getPaginated();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.GetPaginated(context.TODO(), &junction.GetPaginatedLabTestsRequest{})
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "data": [
      {
        "lab_test": {
          "name": "Lipids Panel",
          "description": "Cholesterol test",
          "sample_type": "dried blood spot",
          "method": "testkit",
          "price": 10,
          "is_active": true,
          "lab": {
            "slug": "USSL",
            "name": "US Specialty Lab",
            "first_line_address": "123 Main St",
            "city": "New York",
            "zipcode": "10001"
          },
          "markers": [
            {
              "name": "Thyroid Stimulating Hormone",
              "slug": "tsh",
              "description": ""
            }
          ]
        }
      }
    ],
    "next_cursor": null
  }
  ```
</ResponseExample>


# Accept Unmatched Result
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/accept-unmatched-result

POST /v3/unmatched_result/{raw_result_id}/accept
Accept a match for an unmatched lab result via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>


# Create Unmatched Result Test
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/create-unmatched-result-test

POST /v3/unmatched_result_test
Create a Sandbox test run that generates an unmatched lab result via the Junction API.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<Note>
  This endpoint is available only in Sandbox and requires unmatched-result testing to be enabled for your team. Every request requires an `X-Idempotency-Key` header.
</Note>


# Get Unmatched Result
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/get-unmatched-result

GET /v3/unmatched_result/{raw_result_id}
Retrieve a lab result that requires match review via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>


# Get Unmatched Result Test
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/get-unmatched-result-test

GET /v3/unmatched_result_test/{run_id}
Retrieve the status and generated artifacts for a Sandbox unmatched-result test run via the Junction API.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<Note>
  This endpoint is available only in Sandbox and requires unmatched-result testing to be enabled for your team.
</Note>


# List Unmatched Result Test Cases
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/list-unmatched-result-test-cases

GET /v3/unmatched_result_test/case
List the supported Sandbox unmatched-result test cases and their requirements via the Junction API.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<Note>
  This endpoint is available only in Sandbox and requires unmatched-result testing to be enabled for your team.
</Note>


# List Unmatched Results
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/list-unmatched-results

GET /v3/unmatched_result
List lab results that require match review via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>


# Resolve Unmatched Result
Source: https://docs.junction.com/api-reference/lab-testing/unmatched-results/resolve-unmatched-result

POST /v3/unmatched_result/{raw_result_id}/resolve
Reject or escalate an unmatched lab result via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>


# Update Order Draw Completed
Source: https://docs.junction.com/api-reference/lab-testing/update-order-draw-completed

PATCH /v3/order/{order_id}/draw_completed
Partially update order draw completed via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Update an on-site collection order when the draw has been completed.

<RequestExample>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.update_on_site_collection_order_draw_completed("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.updateOnSiteCollectionOrderDrawCompleted({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().updateOnSiteCollectionOrderDrawCompleted("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.UpdateOnSiteCollectionOrderDrawCompleted(context.TODO(), &junction.UpdateOnSiteCollectionOrderDrawCompletedLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
     "order": {
        "id": "ea7eae96-2c25-404f-b043-bfc08584610d",
        "team_id": "c26a9cc7-cdff-4f23-a5f6-74d40088c16a",
        "user_id": "63661a2b-2bb3-4125-bb1a-b590f64f057f",
        "patient_details": {
           "dob": "2020-01-01",
           "gender": "male"
        },
        "patient_address": {
           "receiver_name": "John Doe",
           "first_line": "123 Main St.",
           "second_line": "Apt. 208",
           "city": "San Francisco",
           "state": "CA",
           "zip": "91189",
           "country": "United States",
           "phone_number": "+1123456789"
        },
        "priority": false,
        "health_insurance_id": "33ec11aa-d8bf-4f46-950d-c9171be3c22f",
        "details": {
           "type": "testkit",
           "data": {
           "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
           "shipment": {
              "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
              "outbound_tracking_number": "<outbound_tracking_number>",
              "outbound_tracking_url": "<outbound_tracking_url>",
              "inbound_tracking_number": "<inbound_tracking_number>",
              "inbound_tracking_url": "<inbound_tracking_url>",
              "outbound_courier": "usps",
              "inbound_courier": "usps",
              "notes": "<notes>",
              "created_at": "2020-01-01T00:00:00.000Z",
              "updated_at": "2020-01-01T00:00:00.000Z"
           },
           "created_at": "2020-01-01T00:00:00Z",
           "updated_at": "2020-01-01T00:00:00Z"
           }
        },
        "lab_test": {
           "name": "Lipids Panel",
           "description": "Cholesterol test",
           "method": "testkit"
        },
        "sample_id": "123456789",
        "notes": "This is a note",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "status": "collecting_sample",
        "events": [
           {
           "id": 1,
           "created_at": "2022-01-01T00:00:00Z",
           "status": "received.on_site_collection.ordered"
           },
           {
           "id": 2,
           "created_at": "2022-01-02T00:00:00Z",
           "status": "received.on_site_collection.requisition_created"
           },
           {
           "id": 3,
           "created_at": "2022-01-03T00:00:00Z",
            "status": "collecting_sample.on_site_collection.draw_completed"
           }
        ]
     },
     "status": "string",
     "message": "string"
  }
  ```
</ResponseExample>


# Bulk Export
Source: https://docs.junction.com/api-reference/link/bulk-export

POST /v2/link/bulk_export
Create or submit link bulk export via the Junction API. Requires authentication with your team API key.

<Note>
  If you are planning to migrate connections, please [chat with us through support channels](/home/getting-support).
  Link Migration endpoints are disabled by default.
</Note>

<Warning>
  You should [pause the connections](/api-reference/link/bulk-pause) before bulk exporting them. Pausing stops
  Junction systems from refreshing the connections, ensuring the exported data is the most up-to-date.
</Warning>


# Bulk Import
Source: https://docs.junction.com/api-reference/link/bulk-import

POST /v2/link/bulk_import
Create or submit link bulk import via the Junction API. Requires authentication with your team API key.

<Note>
  If you are planning to migrate connections, please [chat with us through support channels](/home/getting-support).
  Link Migration endpoints are disabled by default.
</Note>

Import existing provider connections to your [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview) application credentials.

Before migrating any connection through this endpoint, you must first
[configure your BYOO app credential](/wearables/connecting-providers/bring-your-own-oauth/overview#setting-up-your-oauth-credentials) for the data provider
on your Junction Team.

Note that these connections cannot be migrated:

1. Any connection that is bound to an OAuth application credential not in your possession.

2. Any connection that is not bound to the OAuth application credential set on your Junction Team.

### Asynchronous execution

The Bulk Import endpoint enqueues all connections you submitted to a persistent background operation. It then responds
*202 Accepted* immediately afterwards.

You can inspect the status of the resulting background operation through the [List Bulk Ops](/api-reference/link/list-bulk-ops) endpoint.

Optionally, you may opt into the `wait_for_completion` mode, which would respond with 200 OK only if the operation does complete within 20 seconds.
Otherwise, the endpoint responds 202 Accepted.

<Note>
  You can submit any number of — or even all — connections through the Bulk Import API within a short period of time. Enqueuing is fast and
  does not disrupt the progress of the background operation.
</Note>


# Bulk Pause
Source: https://docs.junction.com/api-reference/link/bulk-pause

POST /v2/link/bulk_pause
Create or submit link bulk pause via the Junction API. Requires authentication with your team API key.

<Note>
  If you are planning to migrate connections, please [chat with us through support channels](/home/getting-support).
  Link Migration endpoints are disabled by default.
</Note>


# Bulk Trigger Historical Pull
Source: https://docs.junction.com/api-reference/link/bulk-trigger-historical-pull

POST /v2/link/bulk_trigger_historical_pull
Create or submit link bulk trigger historical pull via the Junction API. Requires authentication with your team API key.

<Note>
  If you are planning to migrate connections, please [chat with us through support channels](/home/getting-support).
  Link Migration endpoints are disabled by default.
</Note>

### Asynchronous execution

The Bulk Trigger Historical Pull endpoint enqueues all trigger requests you submitted to a persistent background operation. It then responds
*202 Accepted* immediately afterwards.

You can inspect the status of the resulting background operation through the [List Bulk Ops](/api-reference/link/list-bulk-ops) endpoint.

Optionally, you may opt into the `wait_for_completion` mode, which would respond with 200 OK only if the operation does complete within 20 seconds.
Otherwise, the endpoint responds 202 Accepted.

<Note>
  You can trigger historical pull on any number of — or even all — connections through the Bulk Trigger Historical Pull API within a short period
  of time. Enqueuing is fast and does not disrupt the progress of the background operation.
</Note>

#### Managing provider API rate limits

Junction transparently batches and throttles the trigger requests. This is to avoid exhausting the provider API rate limits of your Bring Your Own OAuth
custom credentials.

You can submit as many trigger requests as needed.

| Provider            | Historical Pull Triggering Rate                                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Garmin              | 2 connections every 1 minute                                                                                                                                                   |
| Fitbit, Polar       | 250 connections at a time; no time-based throttling                                                                                                                            |
| Strava              | 1 connection every 15 minutes                                                                                                                                                  |
| Oura                | 150 connections every 5 minutes                                                                                                                                                |
| Withings            | 8 connections every 1 minute, assuming your app uses the [Withings Enterprise Plan](https://developer.withings.com/developer-guide/v3/withings-solutions/withings-api-plans/). |
| All other providers | 8 connections every 1 minute                                                                                                                                                   |

#### Historical Pull Range

Bulk Trigger Historical Pull respects any restricting [User Ingestion Bounds](/wearables/providers/data-ingestion-bounds) and/or any
[Team Data Pull Preferences](/wearables/providers/introduction#customizing-historical-data-pull-range) you have specified.

If you have specified a large number of days to pull, you should be aware that:

1. The background operation will take a longer time to run to completion.
2. It may still cause temporary disruption to data polling and webhook processing, even after the triggering has been throttled.


# Complete Password Provider MFA
Source: https://docs.junction.com/api-reference/link/complete-password-provider-mfa

POST /v2/link/provider/password/{provider}/complete_mfa
Create or submit link provider password complete mfa via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/provider/password/{provider}/complete_mfa \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-link-token: <VITAL-LINK-TOKEN>' \
       --data '
  {
       "mfa_code": "012345"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment, PasswordProviders } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.completePasswordProviderMfa({
      provider: PasswordProviders.Whoop,
      mfaCode: "<mfa_code>",
  });
  ```

  ```python Python theme={null}
  from junction import Junction, PasswordProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.complete_password_provider_mfa(
      PasswordProviders.WHOOP,
      mfa_code="<mfa_code>",
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.CompletePasswordProviderMfaBody;
  import com.junction.api.types.PasswordProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().completePasswordProviderMfa(
      PasswordProviders.WHOOP,
      CompletePasswordProviderMfaBody.builder()
          .mfaCode("<mfa_code>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.CompletePasswordProviderMfa(context.TODO(), &junction.CompletePasswordProviderMfaBody{
      Provider: junction.PasswordProvidersWhoop,
      MfaCode:  "<mfa_code>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Create Code
Source: https://docs.junction.com/api-reference/link/create-code

POST /v2/link/code/create
Create or submit link code create via the Junction API. Requires authentication with your team API key.

Used for Junction iOS apps to share Apple HealthKit with you. Please refer [here](/wearables/vital-app/introduction) for more information.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/code/create\?user_id\=1875c190-0cd6-46c1-8670-5b56a7794b78 \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.codeCreate({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.code_create(user_id="<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.CodeCreateLinkRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().codeCreate(
      CodeCreateLinkRequest.builder()
          .userId("<user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.CodeCreate(context.TODO(), &junction.CodeCreateLinkRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Generate a link token
Source: https://docs.junction.com/api-reference/link/generate-link-token

POST /v2/link/token
Create or submit link token via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/token \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --data '
  {
       "user_id": "1875c190-0cd6-46c1-8670-5b56a7794b78"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.token({
      userId: "<user_id>",
      provider: "oura",
  });
  ```

  ```python Python theme={null}
  from junction import Junction, Providers
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.token(user_id="<user_id>", provider=Providers.OURA)
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.LinkTokenExchange;
  import com.junction.api.types.Providers;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .provider(Providers.OURA)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  provider := junction.ProvidersOura
  response, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId:   "<user_id>",
      Provider: &provider,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Link Demo provider
Source: https://docs.junction.com/api-reference/link/link-demo-provider

POST /v2/link/connect/demo
Create or submit link connect demo via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/connect/demo \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-link-token: <VITAL-LINK-TOKEN>' \
       --data '
  {
      "user_id": "1875c190-0cd6-46c1-8670-5b56a7794b78",
      "provider": "fitbit"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment, DemoProviders } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.connectDemoProvider({
      userId: "<user_id>",
      provider: DemoProviders.AppleHealthKit,
  });
  ```

  ```python Python theme={null}
  from junction import DemoProviders, Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.connect_demo_provider(
      user_id="<user_id>",
      provider=DemoProviders.APPLE_HEALTH_KIT,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.DemoConnectionCreationPayload;
  import com.junction.api.types.DemoProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().connectDemoProvider(
      DemoConnectionCreationPayload.builder()
          .userId("<user_id>")
          .provider(DemoProviders.APPLE_HEALTH_KIT)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.ConnectDemoProvider(context.TODO(), &junction.DemoConnectionCreationPayload{
      UserId:   "<user_id>",
      Provider: junction.DemoProvidersAppleHealthKit,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Link Email provider
Source: https://docs.junction.com/api-reference/link/link-email-provider

POST /v2/link/provider/email/{provider}
Create or submit link provider email via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/provider/email/{provider} \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-link-token: <VITAL-LINK-TOKEN>' \
       --data '
  {
       "email": "test@email.com",
       "region": "us"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const tokenResponse = await client.link.token({ userId: "<user_id>" });

  const data = await client.link.connectEmailAuthProvider({
      provider: "freestyle_libre",
      email: "<email>",
      vitalLinkToken: tokenResponse.linkToken,
  });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  token_response = client.link.token(user_id="<user_id>")

  data = client.link.connect_email_auth_provider(
      email="<email>",
      vital_link_token=token_response.link_token,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.EmailProviderAuthLink;
  import com.junction.api.resources.link.requests.LinkTokenExchange;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var tokenResponse = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .build()
  );

  var data = client.link().connectEmailAuthProvider(
      "freestyle_libre",
      EmailProviderAuthLink.builder()
          .email("<email>")
          .vitalLinkToken(tokenResponse.getLinkToken())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  tokenResponse, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }

  response, err := c.Link.ConnectEmailAuthProvider(context.TODO(), &junction.EmailProviderAuthLink{
      Provider:       "freestyle_libre",
      Email:          "<email>",
      VitalLinkToken: &tokenResponse.LinkToken,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Link OAuth provider
Source: https://docs.junction.com/api-reference/link/link-oauth-provider

GET /v2/link/provider/oauth/{oauth_provider}
Retrieve link provider OAuth via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/link/provider/oauth/{oauth_provider} \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-link-token: <VITAL-LINK-TOKEN>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment, OAuthProviders } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const tokenResponse = await client.link.token({ userId: "<user_id>" });

  const data = await client.link.generateOauthLink({
      oauthProvider: OAuthProviders.Oura,
      vitalLinkToken: tokenResponse.linkToken,
  });
  ```

  ```python Python theme={null}
  from junction import Junction, OAuthProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  token_response = client.link.token(user_id="<user_id>")

  data = client.link.generate_oauth_link(
      OAuthProviders.OURA,
      vital_link_token=token_response.link_token,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.GenerateOauthLinkLinkRequest;
  import com.junction.api.resources.link.requests.LinkTokenExchange;
  import com.junction.api.types.OAuthProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var tokenResponse = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .build()
  );

  var data = client.link().generateOauthLink(
      OAuthProviders.OURA,
      GenerateOauthLinkLinkRequest.builder()
          .vitalLinkToken(tokenResponse.getLinkToken())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  tokenResponse, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }

  response, err := c.Link.GenerateOauthLink(context.TODO(), &junction.GenerateOauthLinkLinkRequest{
      OauthProvider:  junction.OAuthProvidersOura,
      VitalLinkToken: &tokenResponse.LinkToken,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Link Password provider
Source: https://docs.junction.com/api-reference/link/link-password-provider

POST /v2/link/provider/password/{provider}
Create or submit link provider password via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/provider/password/{provider} \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-link-token: <VITAL-LINK-TOKEN>' \
       --data '
  {
       "username": "test",
       "password": "test"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment, PasswordProviders } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.connectPasswordProvider({
      provider: PasswordProviders.Whoop,
      username: "<username>",
      password: "<password>",
  });
  ```

  ```python Python theme={null}
  from junction import Junction, PasswordProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.connect_password_provider(
      PasswordProviders.WHOOP,
      username="<username>",
      password="<password>",
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.IndividualProviderData;
  import com.junction.api.types.PasswordProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().connectPasswordProvider(
      PasswordProviders.WHOOP,
      IndividualProviderData.builder()
          .username("<username>")
          .password("<password>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.ConnectPasswordProvider(context.TODO(), &junction.IndividualProviderData{
      Provider: junction.PasswordProvidersWhoop,
      Username: "<username>",
      Password: "<password>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# List Bulk Ops
Source: https://docs.junction.com/api-reference/link/list-bulk-ops

GET /v2/link/bulk_op
Retrieve link bulk op via the Junction API. Requires authentication with your team API key.

<Note>
  If you are planning to migrate connections, please [chat with us through support channels](/home/getting-support).
  Link Migration endpoints are disabled by default.
</Note>


# Create Dashboard URL
Source: https://docs.junction.com/api-reference/org-management/connect/create-dashboard-url

post /v1/org/{org_id}/ehr_integration/create_dashboard_url
Create a post-authorization URL that launches Junction Dashboard for a Junction Connect member.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/create_dashboard_url \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
      "integration_member_id": "user_456",
      "integration_team_id": "clinic_123",
      "modality": "feature_embed",
      "feature": "order_creation",
      "environment": "sandbox"
    }'
  ```
</RequestExample>


# Create Integration-Managed Member
Source: https://docs.junction.com/api-reference/org-management/connect/create-member

post /v1/org/{org_id}/ehr_integration/member
Create an integration-managed member that can sign in through Junction Connect launch flows.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/member \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
      "integration_member_id": "user_456",
      "name": "Alex Kim",
      "email": "alex@example.com",
      "avatar": "https://example.com/avatar.png",
      "team_role_bindings": []
    }'
  ```
</RequestExample>


# Delete Integration-Managed Member
Source: https://docs.junction.com/api-reference/org-management/connect/delete-member

delete /v1/org/{org_id}/ehr_integration/member/{member_id}
Remove an integration-managed member from your Junction organization using the EHR integration endpoint.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/member/{member_id} \
    --header 'X-Management-Key: <management-key>'
  ```
</RequestExample>


# Get Junction Connect Configuration
Source: https://docs.junction.com/api-reference/org-management/connect/get-configuration

get /v1/org/{org_id}/ehr_integration/configuration
Get the Junction Connect integration configuration for an organization.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/configuration \
    --header 'X-Management-Key: <management-key>'
  ```
</RequestExample>


# Get Integration-Managed Member
Source: https://docs.junction.com/api-reference/org-management/connect/get-member

get /v1/org/{org_id}/ehr_integration/member/{member_id}
Get an integration-managed member by Junction member ID.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/member/{member_id} \
    --header 'X-Management-Key: <management-key>'
  ```
</RequestExample>


# Junction Connect
Source: https://docs.junction.com/api-reference/org-management/connect/junction-connect

Configure and launch Junction Connect sessions through the Management API.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

[Junction Connect](/connect/overview) lets you launch Junction Dashboard experiences from your own web application as either a Link Out or a Feature Embed. See the [Junction Connect Getting Started guide](/connect/getting-started) for the end-to-end integration flow.

You configure and launch Junction Connect through the [Management API](/api-details/junction-management-api):

* Configure your Junction Connect integration through ["Org Config → Junction Connect"](https://app.junction.com/org/ehr-integration) in the [Junction Dashboard](https://app.junction.com), or programmatically with the [Get Configuration](/api-reference/org-management/connect/get-configuration) and [Set Configuration](/api-reference/org-management/connect/set-configuration) endpoints.
* Create and manage integration-managed members with the [Create Member](/api-reference/org-management/connect/create-member), [List Members](/api-reference/org-management/connect/list-members), [Get Member](/api-reference/org-management/connect/get-member), [Resolve Member](/api-reference/org-management/connect/resolve-member), [Update Member](/api-reference/org-management/connect/update-member), and [Delete Member](/api-reference/org-management/connect/delete-member) endpoints.
* Launch a session with the [Create Dashboard URL](/api-reference/org-management/connect/create-dashboard-url) endpoint.


# List Integration-Managed Members
Source: https://docs.junction.com/api-reference/org-management/connect/list-members

get /v1/org/{org_id}/ehr_integration/member
List integration-managed members for an organization.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/member \
    --header 'X-Management-Key: <management-key>'
  ```
</RequestExample>


# Managing Patients
Source: https://docs.junction.com/api-reference/org-management/connect/managing-patients

Create and manage patients in Junction Connect teams through the Junction API.

Patients in Junction Connect teams are Junction users. You manage them through the [Junction API](/api-details/junction-api), using one of its [team-scoped authentication methods](/api-details/junction-api#authentication):

* Authenticate with a **Team API Key**.
* Authenticate with a **Team API Access Token** created through the [Create Team API Access Token](/api-reference/org-management/team-api-access-token/create-team-api-access-token) endpoint.

Common patient-management operations include:

* Create a patient with the [Create user](/api-reference/user/create-user) endpoint.
* Retrieve a patient with the [Get User](/api-reference/user/get-user) endpoint.
* Retrieve patient demographics with the [Get User Demographics](/api-reference/user/get-info-latest) endpoint.
* Set patient demographics with the [Update User Demographics](/api-reference/user/upsert-info) endpoint.

<Tip>
  Use these endpoints to provision patients upfront, then launch the Junction Connect [Order Creation feature](/connect/overview#features) with a preselected patient by using the `order_creation:{user_id}` feature slug.
</Tip>

<Info>
  See the [full Core Platform API reference](/api-reference/user/create-user) for all available user-management endpoints.
</Info>


# Managing Teams
Source: https://docs.junction.com/api-reference/org-management/connect/managing-teams

Create, update, list, and delete teams in your Junction organization through the Management API endpoints.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

Junction Connect works with any team in your organization. You manage teams through the [Management API](/api-details/junction-management-api):

* Create a team with the [Create Team](/api-reference/org-management/team/create-team) endpoint.
* Update settings of a team with the [Update Team](/api-reference/org-management/team/update-team) endpoint.
* Get current settings of a team with the [Get Team](/api-reference/org-management/team/get-team) endpoint.
* Get current settings of a team by the *integration team ID* with the [Resolve Team](/api-reference/org-management/team/resolve-team) endpoint.
* Delete a team with the [Delete Team](/api-reference/org-management/team/delete-team) endpoint.
* List all your teams with the [List Teams](/api-reference/org-management/team/list-teams) endpoint.


# Resolve Integration-Managed Member
Source: https://docs.junction.com/api-reference/org-management/connect/resolve-member

post /v1/org/{org_id}/ehr_integration/resolve_member
Get an integration-managed member by your integration member reference.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/resolve_member \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
      "integration_member_id": "user_456"
    }'
  ```
</RequestExample>


# Set Junction Connect Configuration
Source: https://docs.junction.com/api-reference/org-management/connect/set-configuration

put /v1/org/{org_id}/ehr_integration/configuration
Create or update the Junction Connect integration configuration for an organization.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PUT \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/configuration \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
      "slug": "my-clinical-practice",
      "modalities": ["feature_embed", "link_out"],
      "origin_configs": [
        {
          "origin": "https://app.example.com",
          "session_continuation_url": "https://app.example.com/junction/session-continuation"
        }
      ]
    }'
  ```
</RequestExample>


# Update Integration-Managed Member
Source: https://docs.junction.com/api-reference/org-management/connect/update-member

patch /v1/org/{org_id}/ehr_integration/member/{member_id}
Update profile attributes and team role bindings for an integration-managed member.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/member/{member_id} \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
      "name": "Alex Kim",
      "email": "alex@example.com",
      "team_role_bindings": []
    }'
  ```
</RequestExample>


# Using Junction API
Source: https://docs.junction.com/api-reference/org-management/connect/using-junction-api

Use the Junction API to manage patients and access Junction's team-scoped platform capabilities.

Use the [Junction API](/api-details/junction-api) alongside Junction Connect to work with patient and clinical data in a team.

<Info>
  To create, retrieve, and update patients and their demographics, see [Managing Patients](/api-reference/org-management/connect/managing-patients).
</Info>

Authenticate Junction API requests using one of its [team-scoped authentication methods](/api-details/junction-api#authentication):

* Authenticate with a **Team API Key**.
* Authenticate with a **Team API Access Token** created through the [Create Team API Access Token](/api-reference/org-management/team-api-access-token/create-team-api-access-token) endpoint.

Through the Junction API, you can:

* manage users and demographics information in your Team with the [Core API](/api-reference/user/create-user);
* manage user connections, pull ingested device data, and inspect connection backfill statuses through the [Devices API](/wearables/connecting-providers/introduction);
* order lab tests, manage appointments and pull the results through the [Lab Testing API](/lab/overview/introduction); and
* aggregate ingested device data with the [Junction Sense API](/sense/overview).

See the [Junction API overview](/api-details/junction-api) for links to each API category, available environments, regional endpoints, and authentication options.


# Webhooks and Events
Source: https://docs.junction.com/api-reference/org-management/connect/webhooks-and-events

Receive Junction events through webhooks or stream them to supported ETL pipeline destinations.

Use Junction webhooks and events alongside Junction Connect to react to patient, device, and lab testing updates as they happen.

<Info>
  Start with the [Webhooks and Events introduction](/webhooks/introduction), then review the [event structure](/webhooks/event-structure) and [retry policy](/webhooks/retry-policy).
</Info>

The [Event Catalog](/event-catalog) provides schemas and example payloads for every event type. Lab testing examples include:

* [Order created](/event-catalog/labtest.order.created) and [order updated](/event-catalog/labtest.order.updated) events.
* [Appointment created](/event-catalog/labtest.appointment.created) and [appointment updated](/event-catalog/labtest.appointment.updated) events.
* The [critical result](/event-catalog/labtest.result.critical) webhook, emitted when a lab result contains a critical value requiring urgent clinical attention. See [Critical Results](/lab/results/critical-results) for the full workflow.

For higher-volume delivery, [ETL Pipelines](/webhooks/etl-pipelines/overview) continuously stream Junction events to supported destinations such as Google Cloud Pub/Sub, RabbitMQ, and Azure Event Hubs. Webhooks and ETL Pipelines use the same event payload schemas.


# Create or Resend Invite
Source: https://docs.junction.com/api-reference/org-management/invite/create-or-resend-invite

post /v1/org/{org_id}/invite
Post org invite via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/invite \
    --header 'Content-Type: application/json' \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
    "email": "<string>"
  }'
  ```
</RequestExample>


# List Invites
Source: https://docs.junction.com/api-reference/org-management/invite/list-invites

get /v1/org/{org_id}/invite
Get org invite via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/invite \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Revoke Invite
Source: https://docs.junction.com/api-reference/org-management/invite/revoke-invite

delete /v1/org/{org_id}/invite/{invite_id}
Delete org invite via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/invite/{invite_id} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Get Lab Accounts
Source: https://docs.junction.com/api-reference/org-management/lab-accounts/get-lab-accounts

get /v1/org/{org_id}/lab_account/{env}/{region}
Get org lab account via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/lab_account/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Update Lab Account Teams
Source: https://docs.junction.com/api-reference/org-management/lab-accounts/update-lab-account-teams

patch /v1/org/{org_id}/lab_account/{env}/{region}/{account_id}
Patch org lab account via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.management.junction.com/v1/org/{org_id}/lab_account/{env}/{region}/{account_id} \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{ "team_id_allowlist": "<team-id>" }'
  ```
</RequestExample>


# Create Management Key
Source: https://docs.junction.com/api-reference/org-management/management-keys/create-management-key

post /v1/org/{org_id}/management_key
Post org management key via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/management_key \
    --header 'Content-Type: application/json' \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
    "label": "<string>",
    "team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
  }'
  ```
</RequestExample>


# Delete Management Key
Source: https://docs.junction.com/api-reference/org-management/management-keys/delete-management-key

delete /v1/org/{org_id}/management_key/{key_id}
Delete org management key via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/management_key \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# List Management Keys
Source: https://docs.junction.com/api-reference/org-management/management-keys/list-management-keys

get /v1/org/{org_id}/management_key
Get org management key via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/management_key \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# List Members
Source: https://docs.junction.com/api-reference/org-management/member/list-members

get /v1/org/{org_id}/member
Get org member via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/member \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Delete Member
Source: https://docs.junction.com/api-reference/org-management/member/remove-member

delete /v1/org/{org_id}/member/{member_id}
Delete org member via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/member/{member_id} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Get Org
Source: https://docs.junction.com/api-reference/org-management/org/get-org

get /v1/org/{org_id}
Get org via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Patch Org
Source: https://docs.junction.com/api-reference/org-management/org/update-org

patch /v1/org/{org_id}
Patch org via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.management.junction.com/v1/org/{org_id} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Create Team API Access Token
Source: https://docs.junction.com/api-reference/org-management/team-api-access-token/create-team-api-access-token

post /v1/org/{org_id}/team/{team_id}/create_access_token
Create a short-lived Junction API access token for a team.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<Info>
  The access token expires after 10 minutes and acts with Team Admin privileges for the selected team.
</Info>

<Warning>
  This endpoint is rate limited to prevent abuse. Cache and reuse the access token when appropriate, or use Team API Keys for long-lived access.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team/{team_id}/create_access_token \
    --header 'X-Management-Key: <management-key>'
  ```
</RequestExample>


# Create Team API Key
Source: https://docs.junction.com/api-reference/org-management/team-api-keys/create-team-api-key

post /v1/org/{org_id}/team_api_keys/{env}/{region}
Post org team API keys via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team_api_keys/{env}/{region} \
    --header 'Content-Type: application/json' \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
    "label": "<string>",
    "team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
  }'
  ```
</RequestExample>


# Delete Team API Keys
Source: https://docs.junction.com/api-reference/org-management/team-api-keys/delete-team-api-keys

delete /v1/org/{org_id}/team_api_keys/{env}/{region}
Delete org team API keys via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/team_api_keys/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# List Team API Keys
Source: https://docs.junction.com/api-reference/org-management/team-api-keys/list-team-api-keys

get /v1/org/{org_id}/team_api_keys/{env}/{region}
Get org team API keys via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team_api_keys/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Delete Org Team Custom Credentials
Source: https://docs.junction.com/api-reference/org-management/team-custom-credentials/delete-team-custom-credentials

delete /v1/org/{org_id}/team_custom_credentials/{env}/{region}
Delete org team custom credentials via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/team_custom_credentials/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Get Org Team Custom Credentials
Source: https://docs.junction.com/api-reference/org-management/team-custom-credentials/get-team-custom-credentials

get /v1/org/{org_id}/team_custom_credentials/{env}/{region}
Get org team custom credentials via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team_custom_credentials/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Create Org Prepare Team Custom Credentials
Source: https://docs.junction.com/api-reference/org-management/team-custom-credentials/prepare-team-custom-credentials

POST /v1/org/{org_id}/prepare_team_custom_credentials/{env}/{region}
Create or submit org prepare team custom credentials via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/prepare_team_custom_credentials/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Set Team Custom Credentials
Source: https://docs.junction.com/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials

post /v1/org/{org_id}/team_custom_credentials/{env}/{region}
Post org team custom credentials via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

### OAuth Providers

<Info>
  Bring Your Own OAuth (BYOO) is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

While most credentials are active immediately once set through this endpoint, some credentials may not.
Check out the [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview#through-the-org-management-api) documentation
for more information.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team_custom_credentials/{env}/{region} \
    --header 'Content-Type: application/json' \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
    "team_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
    "provider": "oura",
    "client_id": "<string>",
    "client_secret": "<string>",
    "details": {
      "fitbit": {
        "is_hr_enabled": true,
        "workout_verify_token": "<string>",
        "sleep_verify_token": "<string>",
        "body_verify_token": "<string>"
      }
    }
  }'
  ```
</RequestExample>


# Delete Team Data Pull Preferences
Source: https://docs.junction.com/api-reference/org-management/team-data-pull-preferences/delete-team-data-pull-preferences

delete /v1/org/{org_id}/team_data_pull_preferences/{env}/{region}
Delete org team data pull preferences via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<Info>
  Deleting preferences for a provider will revert to the default settings.
</Info>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/team_data_pull_preferences/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Get Team Data Pull Preferences
Source: https://docs.junction.com/api-reference/org-management/team-data-pull-preferences/get-team-data-pull-preferences

get /v1/org/{org_id}/team_data_pull_preferences/{env}/{region}
Get org team data pull preferences via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team_data_pull_preferences/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>

<Info>
  The endpoint will return the default Junction settings if no custom preferences are set.
</Info>

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Post Team Data Pull Preferences
Source: https://docs.junction.com/api-reference/org-management/team-data-pull-preferences/upsert-team-data-pull-preferences

post /v1/org/{org_id}/team_data_pull_preferences/{env}/{region}
Post org team data pull preferences via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team_data_pull_preferences/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
      "team_ids": [
          "<uuid>"
      ],
      "providers": {
          "oura": {
              "default": {
                  "historical_days_to_pull": 120
              },
              "resource_overrides": {
                  "sleep": {
                      "historical_days_to_pull": 180
                  }
              }
          }
      }
  }'
  ```
</RequestExample>

<Warning>
  By setting a higher value, you acknowledge your awareness of *the facts of API life* that:

  1. historical pulls may take longer to complete;
  2. historical pulls may run into provider rate limiting; and
  3. regular data polling may consequentially be delayed as a knock-on effect.
</Warning>

<Warning>
  Provider rate limiting typically happens at OAuth application level.

  Therefore, Junction recommends [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview)
  if you want to set an extended historical pull range. This creates a rate limit quota dedicated to
  your Junction Team, not being shared with other Junction customers.
</Warning>

<Info>
  The Team Data Pull Preferences you specified are *advisory*. There are scenarios in which Junction systems
  may not adhere strictly to your stated preferences.
</Info>

## Supported Providers

| Provider                                                                     | Default  | Configurable | Remarks                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Abbott LibreView](https://www.libreview.com)                                | 90 days  | ❌            | -                                                                                                                                                                                                                             |
| [Fitbit](https://www.fitbit.com/global/uk/home)                              | 90 days  | ⚠️           | Activity and heartrate timeseries data are fixed to 14 days.                                                                                                                                                                  |
| [Garmin](https://www.garmin.com)                                             | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [Google Fit](https://www.google.com/fit/)                                    | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [Oura](https://ouraring.com)                                                 | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [Peloton](https://www.onepeloton.com)                                        | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [Strava](https://www.strava.com)                                             | 14 days  | ✅            | -                                                                                                                                                                                                                             |
| [Wahoo](https://wahoofitness.com)                                            | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [WHOOP](https://www.whoop.com)                                               | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [Zwift](https://zwift.com)                                                   | 270 days | ✅            | -                                                                                                                                                                                                                             |
| [Withings](https://www.withings.com)                                         | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [8Sleep](https://www.eightsleep.com)                                         | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [Apple HealthKit](https://www.apple.com/uk/ios/health/) (SDK)                | 30 days  | ✅            | -                                                                                                                                                                                                                             |
| [Android Health Connect](https://developer.android.com/health-connect) (SDK) | 30 days  | ❌            | Google restricts access to historical data to [30 days before the first successful permission request](https://developer.android.com/health-and-fitness/guides/health-connect/develop/frequently-asked-questions#time-range). |
| [Hammerhead](https://www.hammerhead.io)                                      | 30 days  | ✅            | -                                                                                                                                                                                                                             |
| [Dexcom](https://www.dexcom.com)                                             | 30 days  | ✅            | -                                                                                                                                                                                                                             |
| [Dexcom (G6 And Older)](https://www.dexcom.com)                              | 1 day    | ❌            | -                                                                                                                                                                                                                             |
| [MyFitnessPal](https://www.myfitnesspal.com)                                 | 14 days  | ✅            | -                                                                                                                                                                                                                             |
| [Polar](https://www.polar.com/accesslink-api/#polar-accesslink-api)          | 28 days  | ❌            | Polar only supports historical backfill for Sleep and Sleep Stream resources.                                                                                                                                                 |
| [Cronometer](https://www.cronometer.com)                                     | 28 days  | ✅            | -                                                                                                                                                                                                                             |

<Note>
  The configurable maximum is 365 days at this time.
</Note>


# Delete Team ETL Pipelines
Source: https://docs.junction.com/api-reference/org-management/team-etl-pipeline/delete-team-etl-pipelines

delete /v1/org/{org_id}/team_etl_pipelines/{env}/{region}
Delete org team ETL pipelines via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/team_etl_pipelines/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Get Team ETL Pipelines
Source: https://docs.junction.com/api-reference/org-management/team-etl-pipeline/get-team-etl-pipelines

get /v1/org/{org_id}/team_etl_pipelines/{env}/{region}
Get org team ETL pipelines via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team_etl_pipelines/{env}/{region} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Set Team ETL Pipelines
Source: https://docs.junction.com/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines

post /v1/org/{org_id}/team_etl_pipelines/{env}/{region}
Post org team ETL pipelines via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team_etl_pipelines/{env}/{region} \
    --header 'Content-Type: application/json' \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
    "team_ids": [
      "3c90c3cc-0d44-4b50-8888-8dd25736052a"
    ],
    "push_historical_data": true,
    "provider_raw_data": true,
    "preferences": {
      "preferred": "cloud_pubsub",
      "enabled": [
        "cloud_pubsub"
      ]
    },
    "cloud_pubsub": {
      "project": "<string>",
      "topic": "<string>",
      "message_ordering": true
    },
    "rabbitmq": {
      "uri": "<string>",
      "exchange": "<string>"
    },
    "svix": {
      "regional": true
    },
    "event_type_prefixes": [
      "<string>"
    ]
  }'
  ```
</RequestExample>


# Get Team Scope Requirements
Source: https://docs.junction.com/api-reference/org-management/team-scope-requirements/get-team-scope-requirements

get /v1/org/{org_id}/team/{team_id}/scope_requirements
Get org team scope requirements via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team/{team_id}/scope_requirements \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Set Team Scope Requirements
Source: https://docs.junction.com/api-reference/org-management/team-scope-requirements/upsert-team-scope-requirements

post /v1/org/{org_id}/team/{team_id}/scope_requirements
Post org team scope requirements via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team/{team_id}/scope_requirements \
    --header 'Content-Type: application/json' \
    --header 'X-Vital-Org-Key: <api-key>' \
    --data '{
     "fitbit": null,
     "oura": {
          "user_must_grant": ["daily"],
          "user_may_grant": ["workout"]
     }
  }'
  ```
</RequestExample>


# Create Webhook
Source: https://docs.junction.com/api-reference/org-management/team-webhook/create

post /v1/org/{org_id}/team/{team_id}/{environment}/webhook
Post org team webhook via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Delete Webhook
Source: https://docs.junction.com/api-reference/org-management/team-webhook/delete

delete /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}
Delete org team webhook via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Get Webhook
Source: https://docs.junction.com/api-reference/org-management/team-webhook/get

get /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}
Get org team webhook via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Get Webhook Headers
Source: https://docs.junction.com/api-reference/org-management/team-webhook/get-headers

get /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}/headers
Get org team webhook headers via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Get Webhook Secret
Source: https://docs.junction.com/api-reference/org-management/team-webhook/get-secret

get /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}/secret
Get org team webhook secret via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# List Webhooks
Source: https://docs.junction.com/api-reference/org-management/team-webhook/list

get /v1/org/{org_id}/team/{team_id}/{environment}/webhook
Get org team webhook via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Patch Webhook
Source: https://docs.junction.com/api-reference/org-management/team-webhook/patch

patch /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}
Patch org team webhook via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Rotate Webhook Secret
Source: https://docs.junction.com/api-reference/org-management/team-webhook/rotate-secret

post /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}/secret/rotate
Post org team webhook secret rotate via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Update Webhook Headers
Source: https://docs.junction.com/api-reference/org-management/team-webhook/update-headers

put /v1/org/{org_id}/team/{team_id}/{environment}/webhook/{webhook_id}/headers
Put org team webhook headers via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Create Team
Source: https://docs.junction.com/api-reference/org-management/team/create-team

post /v1/org/{org_id}/team
Create an organization team. Junction Connect integrations can provide an integration team reference.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/team \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
    "name": "<string>",
    "region": "us",
    "integration_team_id": "<string>",
    "lab_tests_patient_sms_communication_enabled": true,
    "lab_tests_patient_email_communication_enabled": true
  }'
  ```
</RequestExample>


# Delete Team
Source: https://docs.junction.com/api-reference/org-management/team/delete-team

delete /v1/org/{org_id}/team/{team_id}
Delete org team via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.management.junction.com/v1/org/{org_id}/team/{team_id} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Get Team
Source: https://docs.junction.com/api-reference/org-management/team/get-team

get /v1/org/{org_id}/team/{team_id}
Get org team via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team/{team_id} \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# List Teams
Source: https://docs.junction.com/api-reference/org-management/team/list-teams

get /v1/org/{org_id}/team
Get org team via the Junction API. Requires authentication with your team API key.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.management.junction.com/v1/org/{org_id}/team \
    --header 'X-Vital-Org-Key: <api-key>'
  ```
</RequestExample>


# Resolve Team
Source: https://docs.junction.com/api-reference/org-management/team/resolve-team

post /v1/org/{org_id}/resolve_team
Look up a Junction team by your own integration team reference and retrieve its current settings.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.management.junction.com/v1/org/{org_id}/resolve_team \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
      "integration_team_id": "clinic_123"
    }'
  ```
</RequestExample>


# Update Team
Source: https://docs.junction.com/api-reference/org-management/team/update-team

patch /v1/org/{org_id}/team/{team_id}
Update an organization team. Junction Connect integrations can update the integration team reference.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.management.junction.com/v1/org/{org_id}/team/{team_id} \
    --header 'Content-Type: application/json' \
    --header 'X-Management-Key: <management-key>' \
    --data '{
    "name": "<string>",
    "integration_team_id": "<string>",
    "sandbox": {
      "lab_tests_patient_sms_communication_enabled": true,
      "lab_tests_patient_email_communication_enabled": true
    },
    "production": {
      "lab_tests_patient_sms_communication_enabled": true,
      "lab_tests_patient_email_communication_enabled": true
    }
  }'
  ```
</RequestExample>


# List of Providers
Source: https://docs.junction.com/api-reference/providers

GET /v2/providers
Retrieve providers via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/providers/ \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.providers.getAll();
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.providers.get_all()
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.providers().getAll();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Providers.GetAll(context.TODO(), nil)
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Create a Continuous Query
Source: https://docs.junction.com/api-reference/sense/continuous-query/create

post /v1/org/{org_id}/team/{team_id}/{environment}/continuous_query
Post org team continuous query via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Get User Result Table
Source: https://docs.junction.com/api-reference/sense/continuous-query/get-result-table

get /aggregate/v1/user/{user_id}/continuous_query/{query_id_or_slug}/result_table
Get aggregate user continuous query result table via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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


# Get Continuous Query Task History
Source: https://docs.junction.com/api-reference/sense/continuous-query/get-task-history

get /aggregate/v1/user/{user_id}/continuous_query/{query_id_or_slug}/task_history
Get aggregate user continuous query task history via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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


# List all Continuous Queries
Source: https://docs.junction.com/api-reference/sense/continuous-query/list

get /v1/org/{org_id}/team/{team_id}/{environment}/continuous_query
Get org team continuous queries via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Update a Continuous Query
Source: https://docs.junction.com/api-reference/sense/continuous-query/update

patch /v1/org/{org_id}/team/{team_id}/{environment}/continuous_query/{query_id}
Patch org team continuous query via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Tip>
  The base URL of this endpoint is `https://api.management.junction.com/`.

  The endpoint accepts only [Management Key](/api-details/junction-management-api#authentication) (`X-Management-Key`).
  Team API Key is not accepted.
</Tip>


# Query a single user
Source: https://docs.junction.com/api-reference/sense/query-one

post /aggregate/v1/user/{user_id}/query
Post aggregate user query via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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

<AccordionGroup>
  <Accordion title="Receiving data in JSON" icon="brackets-curly">
    JSON is the default output format. Each query instruction outputs one entry containing a dataframe to the `$.results` array,
    in the declaration order of the query instructions.

    ```json theme={null}
    {
        "results": [
            {
                "table": {
                    "index": [...],
                    "min": [...],
                    "max": [...],
                    "mean": [...],
                    "newest": [...]
                }
            }
        ]
    }
    ```
  </Accordion>

  <Accordion title="Receiving data in Parquet" icon="chevrons-up">
    Specify `Accept: application/vnd.vital.tar+gzip+parquet` in your request header.

    The response body is a gzipped Tarball of multiple Parquet files, namely `0.parquet`, `1.parquet`, `2.parquet`, etc.
    The numbering corresponds to the declaration order of the query instructions.
  </Accordion>
</AccordionGroup>


# Create user insurance
Source: https://docs.junction.com/api-reference/user/create-insurance-beta

POST /v2/user/{user_id}/insurance
Create or submit user insurance via the Junction API. Requires authentication with your team API key.

If insurance is covered by Medicare or Medicaid, we have closed beta support for inferring the plan to use based on a patient's address. This endpoint supports `payor_code`s with values of `MEDFED` (Medicare) and
`MAIDFED` (Medicaid) to do this.

For example, supplying a `payor_code` of `MEDFED` for a patient that lives in Arizona will create insurance data using Arizona's Medicare plan payor code.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/{user_id}/insurance \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
       --data '
  {
    "payor_code": "UNITE",
    "member_id": "test",
    "group_id": "123",
    "relationship": "Self",
    "insured": {
      "first_name": "John",
      "last_name": "Doe",
      "email": "john@email.com",
      "phone_number": "+1123123123",
      "gender": "Male",
      "dob": "1999-01-01",
      "address": {
        "first_line": "Some Street",
        "second_line": null,
        "zip": "85004",
        "state": "AZ",
        "city": "Phoenix",
        "country": "US"
      }
    }
  }
  '
  ```
</RequestExample>


# Create Portal URL
Source: https://docs.junction.com/api-reference/user/create-portal-url

POST /v2/user/{user_id}/create_portal_url
Create a Portal URL for a user via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Create a URL to Junction User Portal pre-authorized to a particular User.

Junction User Portal requires that demographics have been specified for the User:

* Use the [Get User Demographics](/api-reference/user/get-info-latest) endpoint to review currently set demographics;
* Use the [Update User Demographics](/api-reference/user/upsert-info) endpoint to update the user's demographics.

<Note>
  If you attempt to create a Portal URL for a user without demographics, you will get a **422 Unprocessable Entity** response.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/{{USER_ID}}/create_portal_url \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
       --data '{"context":"launch"}'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.createPortalUrl({
      userId: "<user_id>",
      context: "launch",
  });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment
  from junction.user import CreateUserPortalUrlBodyContext

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.create_portal_url(
      "<user_id>",
      context=CreateUserPortalUrlBodyContext.LAUNCH,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.user.requests.CreateUserPortalUrlBody;
  import com.junction.api.resources.user.types.CreateUserPortalUrlBodyContext;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().createPortalUrl(
      "<user_id>",
      CreateUserPortalUrlBody.builder()
          .context(CreateUserPortalUrlBodyContext.LAUNCH)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.CreatePortalUrl(context.TODO(), &junction.CreateUserPortalUrlBody{
      UserId:  "<user_id>",
      Context: junction.CreateUserPortalUrlBodyContextLaunch,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Create Sign-In Token
Source: https://docs.junction.com/api-reference/user/create-sign-in-token

POST /v2/user/{user_id}/sign_in_token
Create or submit user sign in token via the Junction API. Requires authentication with your team API key.

Create a [Junction Sign-In Token](/wearables/sdks/authentication#junction-sign-in-token) for your mobile apps to
sign in with [Junction Mobile SDKs](/wearables/sdks/authentication).

<Info>
  Avoid requesting Junction Sign-In Token every time your mobile app relaunches.
  Your mobile app only needs to sign in once with the Junction Mobile SDK. A signed-in session is persistent on device.
</Info>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/{{USER_ID}}/sign_in_token \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getUserSignInToken({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_user_sign_in_token("<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getUserSignInToken("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetUserSignInToken(context.TODO(), &junction.GetUserSignInTokenUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Create user
Source: https://docs.junction.com/api-reference/user/create-user

POST /v2/user
Create or submit user via the Junction API. Requires authentication with your team API key.

<Note>
  When the supplied `client_user_id` conflicts with an existing user, the 400 Bad Request error response
  includes the Junction User ID (`user_id`) and the creation date (`created_on`) of the conflicting user.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/ \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
       --data '
  {
       "client_user_id": "your_unique_id"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.create({ clientUserId: "<client_user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.create(client_user_id="<client_user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.user.requests.UserCreateBody;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().create(
      UserCreateBody.builder()
          .clientUserId("<client_user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.Create(context.TODO(), &junction.UserCreateBody{
      ClientUserId: "<client_user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Delete user
Source: https://docs.junction.com/api-reference/user/delete-user

DELETE /v2/user/{user_id}
Remove user via the Junction API. Requires authentication with your team API key.

Deleting a user would also [deregister all their provider connections](/api-reference/user/deregister-a-provider) immediately.
Junction erases the user data after a 7-day grace period.

<Info>
  If you wish to undo a user deletion for any reason, you can undo the deletion through the
  [Undo User Deletion](/api-reference/user/undo-delete-user) API. Note that undoing would not
  restore any provider connections.
</Info>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
       --url {{BASE_URL}}/v2/user/{user_id} \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Accept: application/json'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.delete({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.delete("<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().delete("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.Delete(context.TODO(), &junction.DeleteUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Deregister Connection
Source: https://docs.junction.com/api-reference/user/deregister-a-provider

DELETE /v2/user/{user_id}/{provider}
Deregister a provider connection via the Junction API. Requires authentication with your team API key.

<Note>
  Deregistration has well-defined behavior only on [cloud-based providers](/wearables/providers/introduction#cloud-based-providers).
</Note>

<Warning>
  If your Junction Health SDK uses [the default Auto Connect mode](/wearables/sdks/health/connection-policies#auto-connect-mode-default),
  deregistering an Apple HealthKit, Health Connect or Samsung Health connection has no effect.

  For explicit connection and disconnection of Apple HealthKit, Health Connect and Samsung Health connections, see the [opt-in Explicit Connect mode](/wearables/sdks/health/connection-policies#explicit-connect-mode).

  You can also pause [Health SDK data sync](/wearables/sdks/health/overview#pausing-data-synchronization) client-side, or
  [sign-out the user](/wearables/sdks/vital-core#reset-the-sdk-sign-out) from the SDK.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request DELETE \
       --url {{BASE_URL}}/v2/user/{user_id}/{provider} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.deregisterProvider({
      userId: "<user_id>",
      provider: "oura",
  });
  ```

  ```python Python theme={null}
  from junction import Junction, Providers
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.deregister_provider("<user_id>", Providers.OURA)
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.types.Providers;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().deregisterProvider("<user_id>", Providers.OURA);
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.DeregisterProvider(context.TODO(), &junction.DeregisterProviderUserRequest{
      UserId:   "<user_id>",
      Provider: junction.ProvidersOura,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get User Demographics
Source: https://docs.junction.com/api-reference/user/get-info-latest

GET /v2/user/{user_id}/info/latest
Retrieve user info latest via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/{user_id}/info/latest \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getLatestUserInfo({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_latest_user_info("<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getLatestUserInfo("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetLatestUserInfo(context.TODO(), &junction.GetLatestUserInfoUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "first_name": "John",
    "last_name": "Doe", 
    "email": "john@email.com", 
    "phone_number":"+1123123123",
    "gender": "Male", 
    "dob": "1999-01-01", 
    "address": {
  	  "first_line": "Some Street", 
  	  "second_line": null,
      "zip_code": "85004",
      "state": "AZ",
      "city": "Phoenix"
    }
  }
  ```
</ResponseExample>


# Get latest insurance
Source: https://docs.junction.com/api-reference/user/get-latest-insurance

GET /v2/user/{user_id}/insurance/latest
Retrieve user insurance latest via the Junction API. Requires authentication with your team API key.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/{user_id}/insurance/latest \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "payor_code": "UNITE",
    "member_id": "test",
    "group_id": "123",
    "relationship": "Self",
    "insured": {
      "first_name": "John",
      "last_name": "Doe", 
      "email": "john@email.com", 
      "phone_number":"+1123123123",
      "gender": "Male", 
      "dob": "1999-01-01", 
      "address": {
        "first_line": "Some Street", 
        "second_line": null,
        "zip_code": "85004",
        "state": "AZ",
        "city": "Phoenix"
      }
    }
  }
  ```
</ResponseExample>


# Get User
Source: https://docs.junction.com/api-reference/user/get-user

GET /v2/user/{user_id}
Retrieve user via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/{user_id} \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Accept: application/json'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.get({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get("<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().get("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.Get(context.TODO(), &junction.GetUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get users
Source: https://docs.junction.com/api-reference/user/get-users

GET /v2/user
Retrieve users via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/ \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getAll();
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_all()
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getAll();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetAll(context.TODO(), nil)
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get User Connections
Source: https://docs.junction.com/api-reference/user/get-users-connected-providers

GET /v2/user/providers/{user_id}
Retrieve user providers via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/providers/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getConnectedProviders({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_connected_providers("<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getConnectedProviders("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetConnectedProviders(context.TODO(), &junction.GetConnectedProvidersUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
      "providers": [
          {
              "name": "Fitbit",
              "slug": "fitbit",
              "logo": "https://example.com/fitbit.png",
              "status": "connected",
              "created_on": "2010-01-23T12:34:56+00:00",
              "resource_availability": {
                  "body": {
                      "status": "available",
                      "scope_requirements": {
                          "user_granted": {
                              "required": [
                                  "weight"
                              ],
                              "optional": []
                          },
                          "user_denied": {
                              "required": [],
                              "optional": []
                          }
                      }
                  },
                  "sleep": {
                      "status": "unavailable",
                      "scope_requirements": {
                          "user_granted": {
                              "required": [],
                              "optional": []
                          },
                          "user_denied": {
                              "required": [
                                  "sleep"
                              ],
                              "optional": [
                                  "heartrate",
                                  "oxygen_saturation",
                                  "respiratory_rate"
                              ]
                          }
                      }
                  }
              },
              "error_details": null
          }
      ]
  }
  ```
</ResponseExample>


# Update user
Source: https://docs.junction.com/api-reference/user/patch-user

PATCH /v2/user/{user_id}
Partially update user via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url {{BASE_URL}}/v2/user/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
       --data '
  {
       "fallback_time_zone": "Europe/London",
       "fallback_birth_date": "1980-03-13"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.patch({
      userId: "<user_id>",
      fallbackTimeZone: "Europe/London",
      fallbackBirthDate: "1980-03-13",
  });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.user.patch(
      "<user_id>",
      fallback_time_zone="Europe/London",
      fallback_birth_date="1980-03-13",
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.user.requests.UserPatchBody;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  client.user().patch(
      "<user_id>",
      UserPatchBody.builder()
          .fallbackTimeZone("Europe/London")
          .fallbackBirthDate("1980-03-13")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  fallbackTimeZone := "Europe/London"
  fallbackBirthDate := "1980-03-13"
  err := c.User.Patch(context.TODO(), &junction.UserPatchBody{
      UserId:            "<user_id>",
      FallbackTimeZone:  &fallbackTimeZone,
      FallbackBirthDate: &fallbackBirthDate,
  })
  if err != nil {
      return err
  }
  ```
</RequestExample>


# Refresh User Data
Source: https://docs.junction.com/api-reference/user/refresh-user-data

POST /v2/user/refresh/{user_id}
Create or submit user refresh via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/refresh/{user_id} \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.refresh({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.refresh("<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().refresh("<user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.Refresh(context.TODO(), &junction.RefreshUserRequest{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Get User by Client User ID
Source: https://docs.junction.com/api-reference/user/resolve-user

GET /v2/user/resolve/{client_user_id}
Retrieve user resolve via the Junction API. Requires authentication with your team API key.

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v2/user/resolve/{client_user_id} \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Accept: application/json'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.getByClientUserId({ clientUserId: "<client_user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.get_by_client_user_id("<client_user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().getByClientUserId("<client_user_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.GetByClientUserId(context.TODO(), &junction.GetByClientUserIdUserRequest{
      ClientUserId: "<client_user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Undo User Deletion
Source: https://docs.junction.com/api-reference/user/undo-delete-user

POST /v2/user/undo_delete
Create or submit user undo delete via the Junction API. Requires authentication with your team API key.

You can undo any [user deletion](/api-reference/user/delete-user) that is still in its 7-day
grace period.

<Note>
  Undoing a deletion does not restore any provider connections.
</Note>

<Warning>
  You cannot undo a deletion if you have already [created a new user](/api-reference/user/create-user) using
  the same `client_user_id`.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/undo_delete \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --data '
  {
       "client_user_id": "8DS6YRVBCSQQ4S0"
  }
  '
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.undoDelete({ clientUserId: "<client_user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.undo_delete(client_user_id="<client_user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.user.requests.UndoDeleteUserRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().undoDelete(
      UndoDeleteUserRequest.builder()
          .clientUserId("<client_user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  clientUserId := "<client_user_id>"
  response, err := c.User.UndoDelete(context.TODO(), &junction.UndoDeleteUserRequest{
      ClientUserId: &clientUserId,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</RequestExample>


# Update User Demographics
Source: https://docs.junction.com/api-reference/user/upsert-info

PATCH /v2/user/{user_id}/info
Partially update user info via the Junction API. Requires authentication with your team API key.

<Note>
  Patient name fields (`first_name`, `last_name`) must follow specific validation rules due to lab restrictions. See [Patient Name Validation](/lab/workflow/order-requirements#patient-name-validation) for complete details.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url {{BASE_URL}}/v2/user/{user_id}/info \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
       --data '
  {
    "first_name": "John",
    "last_name": "Doe", 
    "email": "john@email.com", 
    "phone_number":"+1123123123",
    "gender": "Male", 
    "dob": "1999-01-01", 
    "address": {
  	  "first_line": "Some Street", 
  	  "second_line": null,
      "zip_code": "85004",
      "state": "AZ",
      "city": "Phoenix"
    }
  }
  '
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "first_name": "John",
    "last_name": "Doe", 
    "email": "john@email.com", 
    "phone_number":"+1123123123",
    "gender": "Male", 
    "dob": "1999-01-01", 
    "address": {
  	  "first_line": "Some Street", 
  	  "second_line": null,
      "zip_code": "85004",
      "state": "AZ",
      "city": "Phoenix"
    }
  }
  ```
</ResponseExample>


# API
Source: https://docs.junction.com/changelog/core/api

Changelog of core Junction API updates including lab test result interpretation, user ingestion bounds, and Sense query improvements.

### Error for closed beta endpoint calls is now JSON (Nov 2025)

Previously, if you tried to access an endpoint in closed beta and for which your team was not enabled, it would return a plain text error body. It now returns a JSON error body to align with the rest of our API.

### Lab Test Result Interpretation Filtering (June 2025)

You can now filter lab test orders by result interpretation using the new `interpretation` query parameter in the [GET /orders](/api-reference/lab-testing/get-orders) endpoint.

<Accordion icon="filter" title="Details">
  The new filtering capability allows you to query orders based on their clinical interpretation:

  **Query Parameter:**

  * **`interpretation`** - Filter by result interpretation of the lab test
    * Type: `enum<string> | null`
    * Available options: `normal`, `abnormal`, `critical`
    * Note: This enum is non-exhaustive

  **Response Enhancement:**
  Order responses now include a new `interpretation` field providing the clinical assessment of the test results:

  * **`interpretation`** - Interpretation of the order result
    * Type: `enum<string> | null`
    * Available options: `normal`, `abnormal`, `critical`
    * Note: This enum is non-exhaustive

  This enhancement enables you to programmatically identify and prioritize critical lab results, improving clinical workflow efficiency and patient care monitoring.

  Check out the [GET /orders](/api-reference/lab-testing/get-orders) endpoint documentation.
</Accordion>

### Webhook Management Endpoints (May 2025)

You can now programmatically manage your webhooks via the [Webhooks API](/api-reference/org-management/team-webhook/list). The new endpoints allow you to:

* **CRUD** (create/read/update/delete) your webhooks
* Manage webhook **headers**
* Update webhook **secrets**

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

### Team Management Keys (May 2025)

[Junction Management API](/api-details/junction-management-api) now supports Management Keys (previously *Org Keys*) that
are scoped to one or more Teams.

As a recap, there are now two types of Management Keys:

| Type                | Remarks                                                                                                                       |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Org Management Key  | Full control of the organization — notably can create or delete Teams.                                                        |
| Team Management Key | Scoped control over one or more Teams. But it cannot access Org-level resources or actions, e.g., creating or deleting Teams. |

<Accordion title="Details" icon="key">
  All Management Keys — Org or Team — are accepted by the `X-Management-Key` header, as well as the deprecated `X-Vital-Org-Key` header.

  Selected customers can now manage Management Keys in the [Junction Dashboard](https://app.junction.com/) through:

  | Type                | Remarks                                                                    |
  | ------------------- | -------------------------------------------------------------------------- |
  | Org Management Key  | The **Org Config** page; accessible via the top-left corner Dropdown Menu. |
  | Team Management Key | The **Team Config** page.                                                  |

  Check out:

  * the [Create Management Key](/api-reference/org-management/management-keys/create-management-key) endpoint documentation;
  * the [List Management Keys](/api-reference/org-management/management-keys/list-management-keys) endpoint documentation; and
  * the [Delete Management Key](/api-reference/org-management/management-keys/delete-management-key) endpoint documentation.

  <Note>
    If you intend to create a Team Management Key that binds to 2 or more Teams, you must use the
    [Create Management Key](/api-reference/org-management/management-keys/create-management-key) API endpoint. Junction Dashboard does not
    support creating a key for more than one Team.
  </Note>

  <Note>
    Note that Management Keys cannot be used as Team API Keys to access the Junction API. However, you can
    [manage Team API Keys](/api-reference/org-management/team-api-keys/create-team-api-key) through a Management Key.
  </Note>

  <Info>
    [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
  </Info>
</Accordion>

### `X-Management-Key` header for Junction Management API (April 2025)

[Junction Management API](/api-details/junction-management-api) now accepts Management Keys (previously *Org Keys*)
in the `X-Management-Key` header, in addition to the `X-Vital-Org-Key` header.

We will continue to support the `X-Vital-Org-Key` header, though we recommend moving over to `X-Management-Key` to avoid
confusion, especially if you do plan to adopt Team Management Keys.

<Info>
  [Junction Management API](/api-details/junction-management-api) is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

### Enhanced Historical Data Pull Status Tracking (May 2025)

We've added a new "Retrying" state to the Historical Pull Status page and the historical introspection endpoint, helping you distinguish temporary issues from permanent failures.
Additionally, failed historical pulls now include extra error information, making troubleshooting easier and more efficient.

<Info>
  This applies to connections established after May 9th.
</Info>

### Prepare Team Custom Credentials endpoint (Aug 2024)

The new [Prepare Team Custom Credentials](/api-reference/org-management/team-custom-credentials/prepare-team-custom-credentials) endpoint
provides instructions for preparation of [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview) custom credentials.

You can use the information to configure things like OAuth callback URI and the Webhook URI (if applicable), before activating it on your
Junction Team through the [Set Team Custom Credential](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials)
endpoint.

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

### Azure Event Hub: Flexible routing (Aug 2024)

You can now configure your [Azure Event Hub destination in ETL Pipelines](/webhooks/etl-pipelines/azure-event-hubs)
to route Junction data events to different Event Hubs based on their event type prefix.

Check out the [ETL Pipelines - Azure Event Hub](/webhooks/etl-pipelines/azure-event-hubs#multiple-event-hubs) documentation.

<Info>
  ETL Pipelines are available for [the Scale plan](https://tryvital.io/pricing).
</Info>

### Azure Event Hubs as ETL Pipeline destination (Jun 2024)

You can now receive [events](/webhooks/introduction) from Junction directly with your Azure Event Hubs.

Check out the [ETL Pipelines](/webhooks/etl-pipelines/azure-event-hubs) documentation.

<Info>
  ETL Pipelines are available for [the Scale plan](https://tryvital.io/pricing).
</Info>

### Manage Team Brand Information (Apr 2024)

You can now manage Brand Information of your Junction Teams through the Org Management API.

<Accordion title="Details" icon="billboard">
  Team Brand Information is used in:

  1. the Junction-hosted [Link widget](/wearables/connecting-providers/introduction);
  2. all user communications in [Junction Lab Testing](/lab/overview/introduction) sent on your behalf; and
  3. the Junction-hosted Appointment Booking page for [Junction Lab Testing](/lab/overview/introduction).

  <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.
</Accordion>

### Junction Orgs and Org Management API (Apr 2024)

We have introduced Junction Org, a new level that groups all your Junction Teams. Your Junction Teams have been
transparently grouped and migrated to the new structure.

<Accordion title="Details" icon="building">
  We introduced this to provide a unified billing and administrative experience for customers having these use cases:

  1. multi-region presence; or
  2. user organization with diverging team-level configurations.

  We introduced the Org Management API because customers have asked for programmatic access to
  dynamically create Junction Teams and manage different aspects of their Junction Teams.

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

  Check out the [Org Management API](/api-reference/org-management) documentation.
</Accordion>

### New webhook event top-level fields (Mar 2024)

All events now include Team ID, User ID and Client User ID as top-level fields.

<Accordion title="Details" icon="message">
  We introduced this because this helps reduce a Junction User ID → Client User ID database lookup on your end.

  Check out the [Webhook Event Structure](/webhooks/event-structure) documentation.

  #### Before

  ```json Basic event structure theme={null}
  {
    "data": {
      # ... event specific data
    },
    "event_type": "daily.data.glucose.created",
  }
  ```

  #### After

  ```json Basic event structure theme={null}
  {
    "data": {
      # ... event specific data
    },
    "event_type": "daily.data.glucose.created",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```
</Accordion>

### Improved error response for User creation conflicts (Mar 2024)

The [Create User](/api-reference/user/create-user) endpoint has improved handling of
conflicts in Client User ID.

<Accordion title="Details" icon="circle-exclamation">
  We introduced this because the user creation endpoint being idempotent can help
  simplify your application logic.

  When the supplied `client_user_id` conflicts with an existing user, the 400 Bad Request response
  now includes the Junction User ID (`user_id`) and the creation date (`created_on`) of the conflicting user.

  Check out the [Create User](/api-reference/user/create-user) endpoint documentation.
</Accordion>

### User Undo Deletion (Feb 2024)

You can now undo user deletion that is still in the 7-day grace period.

<Accordion title="Details" icon="trash-undo">
  Check out the [User Undo Deletion](/api-reference/user/undo-delete-user) endpoint documentation.
</Accordion>


# Dashboard
Source: https://docs.junction.com/changelog/core/dashboard

Changelog of Junction Dashboard updates including compendium search, lab account selection, and patient demographics improvements.

### Cross-Compendium Marker Search (Mar 2026)

The Dashboard now includes a cross-compendium search tool that lets you search for markers across multiple labs and convert lab tests from one provider to another.

<Accordion title="Details" icon="magnifying-glass">
  ## Search

  * **Cross-lab search** – Search for markers across Quest, Labcorp, BioReference, and Sonora Quest from a single interface.
  * **CPT code filtering** – Narrow results by entering one or more CPT codes.
  * **Related markers** – View alternative canonical test candidates and related markers across labs.
  * **Match confidence** – Each result shows how it relates to your query (exact match, subset, superset, or overlap) along with a confidence score.

  ## Convert

  * **Lab test conversion** – Convert an existing lab test or a list of provider IDs from one lab to another.
  * **Candidate selection** – Review and select from ranked conversion candidates before creating a new panel.
  * **Direct panel creation** – Create a new lab test panel directly from your search or conversion results.
</Accordion>

### Lab Account Selection in Order Flow (Feb 2026)

The order creation flow now includes a lab account selection step. When your team has multiple lab accounts, a new screen lets you choose which account to use before selecting panels.
Each account card shows the lab name, provider account ID, geographic coverage, and supported billing types.

<Accordion title="Details" icon="building">
  ## Order Flow Changes

  * **Lab account selection screen** – Appears after patient selection when your team has 2+ customer lab accounts. If only one account exists, it is auto-selected.
  * **Filtered panels and markers** – After selecting a lab account, only panels and markers available under that lab are shown.
  * **Review screen** – The selected lab account is displayed on the order review screen with a dropdown to change it. Switching to a different lab resets the order set and AoE answers; switching within the same lab preserves them.

  <Info>
    Lab account selection is available in production environments when the feature is enabled for your team.
  </Info>
</Accordion>

### Managing Org and Team Management Keys (June 2025)

You can now provision and manage Management Keys through the Junction Dashboard:

* Org Management Keys, through the new Org Config page.
* Team Management Keys, through the Team Config page of a specific team.

<AccordionGroup>
  <Accordion title="Team Management Keys" icon="key">
    You can manage Team Management Keys also through the Management API — check out the
    [Create Management Keys](/api-reference/org-management/management-keys/create-management-key) and
    [List Management Keys](/api-reference/org-management/management-keys/list-management-keys) endpoint documentation.

    You must be an Org Admin or using an Org Management Key to manage Team Management Keys.
  </Accordion>

  <Accordion title="Org Management Keys" icon="key">
    Only Org Admins can manage Org Management Keys, and it must be done through the Junction Dashboard.
  </Accordion>
</AccordionGroup>

### Managing Brand Information (June 2025)

We have updated the **Branding and Communications** tab (previously *White-Labelling*) in the Team Config page. You can now
review and update your Team's brand information through this page.

<Accordion title="Details" icon="billboard">
  Team Brand Information is used in:

  1. All patient communication in [Junction Lab Testing](/lab/overview/introduction) sent on your behalf;
  2. the Junction-hosted Appointment Booking page for [Junction Lab Testing](/lab/overview/introduction); and
  3. the Junction-hosted [Link Widget](/wearables/connecting-providers/introduction).

  You can also manage this information programmatically through the Management API — check out the
  [Update Team](/api-reference/org-management/team/update-team) and [Create Team](/api-reference/org-management/team/create-team)
  endpoint documentation.
</Accordion>

### Introducing Team Admin Roles (May 2025)

Introducing **Team Admin** roles for the Junction Dashboard users. Choose between organization-wide and team-specific admin privileges when adding members.

<Accordion title="Details" icon="magnifying-glass">
  ## What's New

  * **Two Permission Levels** – When inviting new members to your organization, you can now choose between **Org Admin** and **Team Admin** roles.
  * **Team-Specific Access** – **Team Admins** can be granted access to specific teams by providing a list of team IDs during invitation or role modification.

  ## Role Capabilities

  ### Org Admin

  * Full access to all organization resources and teams
  * Can view and edit roles of all organization members
  * Complete access to billing information and organization settings

  ### Team Admin

  * Access limited to specifically assigned teams
  * Cannot view or edit roles of other organization members
  * No access to teams outside their allowed list
  * Cannot view or modify billing information or other organization-level settings
</Accordion>

### Improved Order Search experience (Aug 2024)

We have improved the search and filtering capabilities in the Orders page, helping you quickly locate specific orders and information.

<Accordion title="Details" icon="magnifying-glass">
  #### Basic filters

  Order searching now supports these filtering criteria:

  * Patient or Recipient (in the case of at-home kits) — First or Last name
  * Patient Email
  * Patient Date of Birth
    * in the YYYY-MM-DD format.
    * e.g., "1988" will filter for all patients born in 1988.
    * e.g., "1988-03" will filter for all patients born in March of 1988 and "1988-03-07" will filter for all patients born on March 7th, 1988.
  * User ID
  * Client User ID
  * Order ID
  * Order Status

  <Info>
    Only one Basic filter can be active at any given time. This is because Basic filters are
    mutually exclusive with each other.
  </Info>

  ### Date filters

  On top of the basic filters, you can now filter by date range based on either:

  * the Order Creation date
    * For scheduled tests, the order creation date is considered instead of the scheduled date.
  * the Order Last Updated date
    * This allows you to filter orders based on the date at which the order status was last updated.

  Dates are currently filtered with respect to the UTC timezone.

  ### Status filters

  Filter based on the most recent status of the order. Note that some statuses are unique to a collection modality such as Mobile Phlebotomy or Self-Collected Test Kits.

  <Info>
    You can apply multiple Status filters at the same time. These stack as a logical OR among themselves.
  </Info>

  ### Order Type filters

  Filter based on the order type: At-Home Phlebotomy, Walk-In Test, At-Home Test Kit.

  <Info>
    You can apply multiple Order Type filters at the same time. These stack as a logical OR among themselves.
  </Info>

  ### Activation Type filters

  Filter for:

  1. Scheduled Orders that would not begin execution until the specified target date; or
  2. Current Orders that have been executed and are being fulfilled.
</Accordion>

### Improved User Search and Introspection experience (Jun 2024)

The Users page in the Junction Dashboard is now a search-first experience with more filtering and sorting options
available. You can now also click on specific users to inspect device connection status and the patient details.

<Accordion title="Details" icon="magnifying-glass">
  Find your user by typing their Client User ID or Junction User ID into the search bar. For lab testing users: Name, Email and Phone Number are also supported.

  Click on a user to reveal the new User Details page:

  #### The General tab

  Shows an overview of all patient details and user settings associated with the Junction User ID.

  #### The Connections tab

  Provides device connection availability and data ingestion insights of the Junction User.

  You can also obtain this same information programmatically through the [Introspection API](/api-reference/data/introspection/user-resources).
</Accordion>


# API
Source: https://docs.junction.com/changelog/lab-testing/api

Changelog of Junction Lab Testing API updates including custom requisition comments, report parsing, and order management enhancements.

### Unmatched Results API (July 2026)

We introduced the Unmatched Results API for reviewing lab results that could not be safely attached to an order automatically.

<Accordion title="Details" icon="link">
  ## New Endpoints

  * [**GET /v3/unmatched\_result**](/api-reference/lab-testing/unmatched-results/list-unmatched-results) – List lab results that require match review.
  * [**GET /v3/unmatched\_result/\{raw\_result\_id}**](/api-reference/lab-testing/unmatched-results/get-unmatched-result) – Retrieve an unmatched result and its candidate orders.
  * [**POST /v3/unmatched\_result/\{raw\_result\_id}/accept**](/api-reference/lab-testing/unmatched-results/accept-unmatched-result) – Accept a proposed match.
  * [**POST /v3/unmatched\_result/\{raw\_result\_id}/resolve**](/api-reference/lab-testing/unmatched-results/resolve-unmatched-result) – Reject a result or escalate it to Junction's operations team.

  ## New Webhook Event

  The `labtest.match_review.created` event notifies your webhook when an unmatched result becomes available for customer review.

  <Info>
    This feature is currently in closed beta. Contact your Customer Success Manager to enable it.
  </Info>

  See the [Unmatched Results integration guide](/lab/workflow/unmatched-results) for the complete review workflow.
</Accordion>

### Lab account delegation status enforced (May 2026)

We will soon be enforcing that at least one physician is supplied on order creation for Labcorp, Quest, and Sonora Quest labs *if your order is configured to use a delegated lab account*. Up until now, even if the order should be delegated, we would allow a fallback to using Junction physicians if a physician wasn't provided in the order. Going forward, non-delegated orders will use Junction physicians, but delegated orders must supply a physician.

There will be a grace period to give you time to ensure your integration is always supplying a physician for delegated orders. Details of how to check your delegation status are below. Please contact your customer success manager with any questions.

<Accordion title="Details" icon="pencil">
  You can view the lab account for a given team using the [team-level Get Lab Accounts endpoint](/api-reference/lab-testing/lab_accounts), or all lab accounts for an org using the [org-level Get Lab Accounts endpoint](/api-reference/org-management/lab-accounts/get-lab-accounts). Each will include the `delegated_flow` for each lab account in the response body.

  Delegated flow values of either `order_delegated` or `fully_delegated` mean that we expect you to be supplying your own physicians in order requests. This only applies when you're using your own lab account or a Junction subaccount. If you only order with Junction platform accounts, these are non-delegated and orders will continue to use our physicians.

  Whenever possible, we recommend specifying the lab account ID in requests.

  Here are some example scenarios.

  #### You haven't created any client lab accounts and you place a Labcorp order

  This order will use Junction's Labcorp account and Junction physicians. You don't need to specify the lab account ID or include physicians in the request.

  #### You are using a non-delegated Junction subaccount and place a Quest order

  This order will use the Junction subaccount. Please provide the lab account ID. Since the lab account isn't delegated, you don't need to supply physicians in the request.

  ```json theme={null}
  {
    "order" {
      ...
      "lab_account_id": "<UUID of the subaccount you want to use>"
      ...
    }
  }
  ```

  #### You are using your own delegated Quest account

  This order will use your own account. Please provide the lab account ID. Since the lab account is delegated, you must supply physician information in the request.

  ```json theme={null}
  {
    "order" {
      ...
      "lab_account_id": "<UUID of the lab account you want to use>",
      "physician": {
        "first_name": "<physician's first name>",
        "last_name": "<physician's last name>",
        "npi": "<physician's NPI>",
      }
      ...
    }
  }
  ```
</Accordion>

### PSC Appointment Scheduling API Improvements (April 2026)

We have introduced three opt-in features to the PSC Appointment Scheduling API to help improve the availability and reliability of the end patient experience.

<Accordion title="Details" icon="pencil">
  You can now:

  * [Query PSC availability through our minutely-updated cache](/lab/walk-in/psc-appointment-scheduling-opt-ins#availability-cache) to improve your slot searching and selection experience.
  * [Specify an Idempotency Key](/lab/walk-in/psc-appointment-scheduling-opt-ins#idempotency-key) to ensure that your booking CTA is resilient against any network connectivity issues.
  * [Adopt the Async Confirmation flow](/lab/walk-in/psc-appointment-scheduling-opt-ins#async-confirmation) to buffer your booking experience from any instability and unavailability of the underlying PSC Provider API.

  Check out the [PSC Appointment Scheduling: Opt-in Features](/lab/walk-in/psc-appointment-scheduling-opt-ins) for details.
</Accordion>

### Status details for order events and top-level `last_event` field (Mar 2026)

Each order event in API responses and webhook bodies now has a nullable `status_detail` field. If populated, it will contain more granular information about the status.

The latest event for an order is now available on the order body. It holds the same data as the last item in the `events` list.

### Access Notes & Appointment Notes for At-Home Phlebotomy (Mar 2026)

You can now provide instructions for phlebotomists through two new optional fields on at-home phlebotomy appointments.

<Accordion title="Details" icon="notes">
  ## New Fields

  * **`access_notes`** (on patient address) – Set at order creation. Provides location access instructions such as gate codes, parking details, or entrance directions. Automatically forwarded to the phlebotomy provider on every booking and reschedule.
  * **`appointment_notes`** (on appointment) – Set at booking, request, or reschedule time. Provides per-appointment special instructions such as "Please bring photo ID" or "Patient prefers left arm". Can be updated on each reschedule.

  ## Updated Endpoints

  * **POST /v3/order** – `patient_address` now accepts an optional `access_notes` field.
  * **POST /v3/order//phlebotomy/appointment/book** – Request body now accepts an optional `appointment_notes` field.
  * **POST /v3/order//phlebotomy/appointment/request** – Request body now accepts an optional `appointment_notes` field.
  * **PATCH /v3/order//phlebotomy/appointment/reschedule** – Request body now accepts an optional `appointment_notes` field.

  Both fields are returned in the appointment response. See the [At-Home Phlebotomy overview](/lab/at-home-phlebotomy/overview#access-notes-and-appointment-notes) for details.
</Accordion>

### Custom Comments on Requisitions (Mar 2026)

You can now add clinical comments during order creation that are forwarded to the lab requisition. Comments can also be configured as defaults on a per-lab-account basis.

<Accordion title="Details" icon="comment-medical">
  ## How It Works

  * **Order-level comments** – Provide free-text clinical context (up to 1,000 characters) when creating an order through the Dashboard or the API.
  * **Lab-account defaults** – Configure default clinical notes on a lab account. These are automatically included on every order placed under that account.
  * **Merging behavior** – When both order-level and lab-account-level comments are present, they are concatenated on the requisition.

  ## Supported Labs

  * Quest
  * Sonora Quest
  * Labcorp
  * BioReference
</Accordion>

### Lab Report Parsing API (Feb 2026)

Introducing the Lab Report Parsing API – extract structured biomarker data from lab report files and match results to standardized LOINC codes.

<Accordion title="Details" icon="file-medical">
  ## Overview

  The Lab Report Parsing API enables you to digitize lab results from any source. Upload a PDF, JPEG, or PNG lab report, and Junction extracts structured data with LOINC standardization.

  ## New Endpoints

  * **POST /lab\_report/v1/parser/job** – Upload a lab report to create a parsing job
  * **GET /lab\_report/v1/parser/job/** – Retrieve job status and extracted results

  ## Key Features

  * **Multi-format support** – Parse PDF, JPEG, and PNG lab reports up to 10 MB
  * **LOINC matching** – Extracted biomarkers are matched to standardized LOINC codes for cross-lab comparability
  * **Human review option** – Flag jobs for manual verification before completion
  * **Structured output** – Get biomarker name, value, unit, reference range, and interpretation

  ## Webhook Events

  * **lab\_report.parsing\_job.created** – Triggered when a new parsing job is submitted
  * **lab\_report.parsing\_job.updated** – Triggered when a parsing job status changes (completed, failed, pending review)

  ## Use Cases

  * **Patient uploads** – Allow patients to submit lab reports from other providers
  * **Historical data import** – Digitize paper records and legacy PDFs
  * **Multi-source aggregation** – Combine results from various labs using LOINC standardization

  <Info>
    This feature is currently in beta. Contact your account manager to enable it.
  </Info>

  Check out the [Lab Report Parsing documentation](/lab/report-parsing/overview) to get started.
</Accordion>

### Lab Account Selection in Ordering Flow (Feb 2026)

You can now scope orders to a specific lab account during creation.
A new team-level lab accounts endpoint lets you list available accounts,
and a new `lab_account_id` parameter on the order creation endpoint lets you specify which account to use.

<Accordion title="Details" icon="building">
  ## New Endpoint

  * **GET /v3/lab\_test/lab\_account** – List lab accounts accessible to your team. Supports optional `lab_account_id` and `status` query parameters.

  ## Updated Endpoints

  * **POST /v3/order/create** – New optional `lab_account_id` parameter to specify which lab account to use. If omitted, the backend resolves the appropriate account automatically.
  * **POST /v3/order/import** – Now accepts lab accounts in `PENDING` status, enabling order imports while accounts are still being set up.
  * **GET /v3/lab\_tests/markers** – New optional `lab_slug` query parameter to filter markers by lab. When a lab account is selected, you can use this to retrieve only the markers available under that lab.

  ## Behavior

  * When `lab_account_id` is provided, available panels and markers are filtered to those supported by the selected account's lab.
  * When recreating an order with `update_reason=INCORRECT_LAB`, the backend now resolves a fresh lab account rather than carrying forward the incorrect one.
</Accordion>

### Idempotency of Ordering (Sep 2025)

We've added idempotency behavior on the order endpoint, to prevent duplicate orders.

<Accordion title="Details" icon="magnifying-glass">
  Check out the [documentation](/lab/overview/idempotency).
</Accordion>

### Enhanced Lab Order Management with Result Interpretation Filtering (June 2025)

We've enhanced the Orders page with new filtering and visualization capabilities for lab test result interpretations, making it easier to identify and prioritize critical patient results.

<Accordion title="Details" icon="filter">
  ## What's New

  * **Interpretation Filter** – Filter orders by clinical result interpretation using the new "Interpretation" dropdown filter
  * **Critical Value Indicators** – The orders table now displays visual indicators for orders with critical results
  * **Enhanced Order Details** – Order detail views now include interpretation badges highlighting critical findings

  ## Filter Options

  The new Interpretation filter allows you to quickly locate orders based on their clinical assessment:

  * **Normal** – Orders with results within normal reference ranges
  * **Abnormal** – Orders with results outside normal ranges but not critically urgent
  * **Critical** – Orders with results requiring immediate clinical attention

  ## Visual Enhancements

  ### Orders Table

  * Critical value indicators help you quickly spot high-priority results that need immediate attention
  * Clear visual distinction between normal, abnormal, and critical interpretations

  ### Order Details View

  * Prominent badges display the interpretation status for easy identification
  * Critical results are highlighted with distinctive styling to ensure they don't go unnoticed

  <Info>
    You can combine the Interpretation filter with existing filters like Order Status, Date Range, and Order Type for more precise result filtering.
  </Info>

  This enhancement improves clinical workflow efficiency by enabling healthcare teams to quickly identify and prioritize orders requiring immediate attention.
</Accordion>

### Introducing Panel Renaming & Archiving (May 2025)

We've added more control to how you manage test panels.

<Accordion title="Details" icon="magnifying-glass">
  Now you can skip the support email and work directly in the Test Catalog to:

  * Rename panels
  * Archive outdated or unused panels to keep things clean
  * View archived panels using the "Status" filter and "unarchive" to make panels active again

  Existing orders tied to archived panels will remain active and processable.

  [Full product guide →](https://support.tryvital.com/articles/1278554980-managing-lab-panels)
</Accordion>

### Problem in Transit Order Statuses (Mar 2025)

Testkit orders now support two new statuses representing problems in transit. Refer to the [lab test lifecycle documentation](/lab/workflow/lab-test-lifecycle).

<Accordion title="Details" icon="billboard">
  We're introducing two new statuses for the `OrderLowLevelStatus` enum (`problem_in_transit_lab` and `problem_in_transit_customer`) and two for the `OrderStatus` enum (`collecting_sample.testkit.problem_in_transit_customer` and `collecting_sample.testkit.problem_in_transit_lab`)—and we may make further changes in the future. To ensure future compatibility, we ask that you avoid exhaustive matching on enum values. Code that assumes all current values are exhaustive could break or fail to compile with SDK upgrades.

  Here's how to verify and ensure you benefit from future enhancements:

  1. Look for areas in your code where you take an action based on the values of a Junction-defined enum. For example, you might have Python code like this:

  ```python Python theme={null}
  match status:
      case OrderStatus.TESTKIT_ORDERED:
          handle_ordered()
      case OrderStatus.TESTKIT_AWAITING_REGISTRATION:
          handle_awaiting_registration()
          # other cases...
  ```

  2. Check that unknown values in your code paths are handled gracefully—for example, by using default cases. Logging unknown values can help you stay informed.

  ```python Python theme={null}
  match status:
      # previous cases...
      case unknown_status:
          logger.warning(f"Unknown status received: {repr(unknown_status)}")
  ```

  3. Make sure you're running the latest version of our SDK.
  4. You're good to go. Once you've checked the code paths won't break if Junction-defined enums start including new values, no further action is needed.
</Accordion>

### Source Marker Identification for Results (Nov 2024)

Results now feature the source orderable marker from which they originated.
Refer to the [documentation](/lab/results/result-formats).

### PSC Appointment Scheduling API (Oct 2024)

Walk-in Phlebotomy orders now allow for appointment booking directly with Junction.
Refer to the [documentation](/lab/walk-in/order-lifecycle).

### Phlebotomy Availability API - Start Date Query (Sep 2024)

You can now supply a `start_date` to the [Appointment Availability API](/api-reference/lab-testing/at-home-phlebotomy/appointment-availability).
The API always responds with 14 days' worth of slots.

### À La Carte Ordering (Sep 2024)

Junction now supports ordering à la carte, as well as a revamped ordering flow.

<Accordion title="Details" icon="billboard">
  Check out the [Ordering](/lab/workflow/ordering) documentation.
</Accordion>

### Partial Results Webhook (Jul 2024)

Junction now supports sending webhooks for partial results, on a team-by-team configuration.

<Accordion title="Details" icon="billboard">
  Check out the [Partial Results Notifications](/lab/workflow/partials) documentation.
</Accordion>

### Patient Service Center (PSC) Availability API (Jul 2024)

It is now possible to verify lab PSC availability in regard to a zip code, radius or order.

<Accordion title="Details" icon="billboard">
  Check out the [Patient Service Center](/lab/overview/locations) documentation.
</Accordion>

### Create Lab Tests With Provider IDs (Jun 2024)

It is now possible to create lab tests using the Laboratory's unique provider ID.
This allows payloads to be shared across sandbox and production.

<Accordion title="Details" icon="billboard">
  Check out the [Create a Lab Test](/lab/workflow/create-test) documentation.
</Accordion>

### Ask on Order Entry (AOE) (Jun 2024)

You can now order panels with AOE requirements via the API.

<Accordion title="Details" icon="billboard">
  Check out the [AOE](/lab/workflow/aoe) documentation.
</Accordion>


# Deprecations
Source: https://docs.junction.com/changelog/lab-testing/deprecations

Deprecated features in the Junction Lab Testing API, including the lab_test_id field replaced by order_set in order creation.

### `lab_test_id` in the [POST /v3/order](/api-reference/lab-testing/create-order) endpoint.

`lab_test_id` is deprecated in favor of the `order_set` field. More information [here](/lab/workflow/ordering).

<Steps>
  <Step title="Remove lab_test_id usage">
    In the `order` payload, remove the usage of `lab_test_id`

    ```json theme={null}
    {
        "lab_test_id": "some_id" # Remove this
    }
    ```
  </Step>

  <Step title="Use the order_set field">
    In the `order` payload, add the `order_set` object.

    ```json theme={null}
    {
        "order_set": {
            "lab_test_ids": ["some_id"]
        }
    }
    ```
  </Step>
</Steps>

### `value` field in the `results` object

We are removing the `value` field from the [results object](/api-reference/lab-testing/results/get-results), as it does not accurately capture all possible result values. In turn, we've introduced the `result` field.

<Card title="Recommended Action">
  Parse the `result` field in accordance with the `result_type` field. [More information here](/lab/results/result-formats#resulttype).
</Card>

### `requisition_form_url` in the `order` object

This field is a signed GCP bucket URL, which is only active for 7 days. It will be removed in favor of the [download PDF](/api-reference/lab-testing/requisition-pdf) endpoint.

### `GET /v3/lab_tests` in favor of `GET /v3/lab_test`

<Warning>The deprecation cycle ends on **1 October 2026**. The API endpoint will be removed afterwards.</Warning>

The endpoint `GET /v3/lab_tests` is deprecated in favor of `GET /v3/lab_test`. The new endpoint will return a paginated list of lab tests and supports two new parameters: `lab_test_limit` and `next_cursor`.


# API
Source: https://docs.junction.com/changelog/wearables/api

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

### Junction Sense: ALIGN clause (May 2026)

Junction Sense queries now support a new `.align()` clause that fills empty time buckets in your aggregated output with values carried over from neighboring buckets. Use it when you want a contiguous time series from sparse input - for example a daily weight composite from a once-a-week weigh-in.

<Accordion title="Details" icon="arrows-left-right-to-line">
  The ALIGN clause supports three carry operators:

  * **`"carry_forward"`** - fills empty buckets with the most recent prior non-empty value. Use for causal or streaming scenarios where only past data should influence the current value.
  * **`"carry_backward"`** - fills empty buckets with the next subsequent non-empty value. Use for retrospective analysis where a measurement on day N applies back to days N-1, N-2, etc.
  * **`"carry_nearest"`** - fills empty buckets with the value from the nearest non-empty bucket in either direction. Ties prefer the past value.

  Each operator takes a duration argument (`max_age` for forward/backward, `span` for nearest) that caps how far the carry will reach. Buckets beyond the cap remain as explicit nulls in the output.

  **Example - daily weight trend from sparse weigh-ins:**

  ```python theme={null}
  import vitalx.aggregation as va

  va.select(
      va.group_key("*"),
      va.Body.col("weight_kilogram").mean(),
  ).group_by(
      va.date_trunc(va.Body.index(), 1, "day"),
  ).align("carry_forward", max_age=va.period(14, "day"))
  ```

  A user who weighs once a week now produces a row for every day in the spine, with the last known weight carried forward for up to 14 days. Days beyond 14d of the last weigh-in remain as explicit null rows.

  When you group by additional dimensions like `Source.col("source_provider")`, carry stays within each partition - values do not cross provider boundaries.

  For the full reference including operator semantics, validation rules, and per-partition behavior, see the [ALIGN clause documentation](/sense/query-dsl/align-clause).
</Accordion>

### 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" title="Details">
  * 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" title="Details">
  * 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" title="Details">
  * `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" title="Details">
  * 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" title="Details">
  * 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" title="Details">
  * `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" title="Details">
  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 icon="person-digging">
  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" title="Details">
  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 icon="person-digging">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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 so, 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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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" title="Details">
  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>


# Deprecations
Source: https://docs.junction.com/changelog/wearables/deprecations

Deprecated features in the Junction Wearables API, including the hypnogram timeseries type and redefined Source/Provider terminology.

### Non-grouped timeseries endpoints

<Warning>The deprecation cycle ends on **14 September 2026**. The API endpoints will be removed afterwards.</Warning>

We have introduced [Get Grouped Timeseries](/changelog/wearables/api#grouped-timeseries-feb-2024) endpoints since February 2024.
Check out the [Get Heartrate](/api-reference/data/timeseries/heartrate) endpoint for an example. It has been supported by all Junction API
SDKs (backend), as well as all relevant Junction Mobile SDKs (Native iOS, Native Android, Flutter) since 2024 as well.

The [Get Grouped Timeseries](/changelog/wearables/api#grouped-timeseries-feb-2024) endpoints have several advantages:

1. They use cursor-based pagination, allowing you to fetch and process data incrementally.
2. They expose the [data source attributions](/wearables/providers/data-attributions), which are absent from the legacy endpoints.

| Category                | Status        | API endpoint path                             | API SDKs                    |
| ----------------------- | ------------- | --------------------------------------------- | --------------------------- |
| Get Grouped Timeseries  | 🟢 Supported  | `/v2/timeseries/{user_id}/{resource}/grouped` | `vitals.{resource}_grouped` |
| Get Timeseries (Legacy) | ⚠️ Deprecated | `/v2/timeseries/{user_id}/{resource}`         | `vitals.{resource}`         |

<Card title="Recommended Action" icon="lightbulb-exclamation-on">
  For Junction API SDKs, the client methods for the Get Timeseries (Legacy) endpoints have been marked as deprecated since 2024. Make sure you use the
  latest Junction API SDK releases, and take action on all the deprecations.

  For relevant Junction Mobile SDKs (Native iOS, Native Android, Flutter), there is no action for you to take assuming you use the latest SDK releases.

  For direct API calls, make sure your request URL paths include `/grouped`, your logic expects the grouped timeseries response schema, and that you handle pagination.
</Card>

### The `hypnogram` timeseries type

<Warning>The deprecation cycle ends on ~~31 January 2025~~ **14 September 2026**. The resource will be removed afterwards.</Warning>

We have introduced a new Sleep Cycle summary type (`sleep_cycle`) which captures the detailed hypnogram of a sleep session.
It replaces the existing `hypnogram` timeseries type. More details can be found here: [Sleep Cycle Summary](/api-reference/data/sleep-cycle/get-summary)

<Card title="Recommended Action" icon="lightbulb-exclamation-on">
  Start processing the new `historical.data.sleep_cycle.created`, `daily.data.sleep_cycle.created` and `daily.data.sleep_cycle.updated` events.
  Stop processing the `historical.timeseries.hypnogram.created`, `daily.timeseries.hypnogram.created` and `daily.timeseries.hypnogram.updated` events.
</Card>

### Redefining Source and Provider

<Warning>The deprecation cycle ends on ~~31 July 2024~~ **14 September 2026**. The deprecated fields will be removed afterwards.</Warning>

Junction has redefined what *Source* and *Provider* mean across our API and event schemas:

<Accordion title="The new definitions of Provider and Source">
  | Entity   | Definition                                                                                            |
  | -------- | ----------------------------------------------------------------------------------------------------- |
  | Source   | The source context of a specific piece of data (summary or timeseries resources).                     |
  | Provider | Static metadata of a wearable data provider (an app, a platform, a service, or Junction Mobile SDKs). |

  A *Source* context comprises:

  * the Provider (slug only)
  * the [Source Type](/wearables/providers/data-attributions#source-type)
  * the [App ID](/wearables/providers/data-attributions#app-id) (*optional*)

  We also have plans for the *Source* context to include source device metadata where available.

  A *Provider* object is a short description of a wearable data provider. It comprises the familiar
  `name`, `logo` and `slug` trio you have been receiving in many data events.
</Accordion>

To migrate to the new definitions, Junction is announcing the following deprecations:

<Steps>
  <Step title="All Data Events (daily.data.*)">
    Inside the Source context located at `$.data.source`, the `name`, `logo`, and `slug` fields are
    now deprecated.

    Junction no longer embeds these *Provider* object fields in every data event, except for the provider
    slug.

    Using the [Steps data event](/event-catalog/daily.data.steps.created) as an illustration, originally
    the `source` field described the wearable data provider. As part of this redefinition, the `source` field is
    reappropriated to track the *Source* context of the `steps` timeseries value group. So we have added a few
    new *Source* context fields, and marked several *Provider* fields as deprecated:

    ```json daily.data.steps.created (Current) theme={null}
    {
        "event_type": "daily.data.steps.created",
        "data": {
            "data": [...],
            "source": {
                /** 🟢 BEGIN: New source fields **/
                "provider": "oura",
                "type": "ring",
                "app_id": null,
                /** 🟢 END: New source fields **/

                /** ⚠️ BEGIN: Deprecated provider fields, to be removed **/
                "name": "Oura",
                "slug": "oura",
                "logo": "https://example.com/oura.svg"
                /** ⚠️ END: Deprecated provider fields, to be removed **/
            }
        },
        "user_id": "fb58770e-8b7b-4416-a5bd-8433786d10dc"
    }
    ```

    Once the deprecation cycle ends, Junction will remove the deprecated fields:

    ```json daily.data.steps.created (Future) theme={null}
    {
        "event_type": "daily.data.steps.created",
        "data": {
            "data": [...],
            "source": {
                /** 🟢 BEGIN: New source fields **/
                "provider": "oura",
                "type": "ring",
                "app_id": null
                /** 🟢 END: New source fields **/
            }
        },
        "user_id": "fb58770e-8b7b-4416-a5bd-8433786d10dc"
    }
    ```

    <Card title="Recommended Action" icon="lightbulb-exclamation-on">
      Stop parsing `name`, `logo` and `slug` when processing the `$.data.source` field in
      **all Data Events** (daily.data.\*).
    </Card>

    <Info>
      If you need the Provider information, you can obtain it through the [Get Providers](/api-reference/providers)
      endpoint.
    </Info>
  </Step>

  <Step title="Get Summary endpoints">
    <Note>
      This applies to:

      * Get Activity: `GET /v2/summary/activity/*`
      * Get Body: `GET /v2/summary/body/*`
      * Get Meal: `GET /v2/summary/meal/*`
      * Get Profile: `GET /v2/summary/profile/*`
      * Get Sleep: `GET /v2/summary/sleep/*`
      * Get Workouts: `GET /v2/summary/workouts/*`
    </Note>

    Similar to changes to the Data Events, the `name`, `logo`, and `slug` sub-fields under the `source` field
    in each and every summary are now deprecated.

    Junction no longer embeds these *Provider* object fields in every summary, except for the provider
    slug.

    Using the [Get Profile endpoint](/api-reference/data/profile/get-summary) as an illustration, the `source` object has
    been reappropriated as the *Source* context of this Profile summary. So we have added a few new *Source* context fields,
    and marked several *Provider* fields as deprecated:

    ```json Profile (Current) theme={null}
    {
        "id": "eadba0c7-4e81-4d17-962c-9ebe0629c08f",
        "date": "2022-08-04",
        "height": 183,
        "source": {
            /** 🟢 BEGIN: New source fields **/
            "provider": "oura",
            "type": "app",
            "app_id": null,
            /** 🟢 END: New source fields **/

            /** ⚠️ BEGIN: Deprecated provider fields, to be removed **/
            "name": "Oura",
            "slug": "oura",
            "logo": "https://example.com/oura.svg"
            /** ⚠️ END: Deprecated provider fields, to be removed **/
        },
        "user_id": "71937dd3-aebe-46b7-ab64-c287bd75b2a6"
    }
    ```

    Once the deprecation cycle ends, Junction will remove the deprecated fields:

    ```json Profile (Future) theme={null}
    {
        "id": "eadba0c7-4e81-4d17-962c-9ebe0629c08f",
        "date": "2022-08-04",
        "height": 183,
        "source": {
            /** 🟢 BEGIN: New source fields **/
            "provider": "oura",
            "type": "app",
            "app_id": null
            /** 🟢 END: New fields **/
        },
        "user_id": "71937dd3-aebe-46b7-ab64-c287bd75b2a6"
    }
    ```

    <Card title="Recommended Action" icon="lightbulb-exclamation-on">
      Stop parsing `name`, `logo` and `slug` fields in **all Get Summary endpoint** responses.
    </Card>

    <Info>
      If you need the Provider information, you can obtain it through the [Get Providers](/api-reference/providers)
      endpoint.
    </Info>
  </Step>

  <Step title="Provider Connection events">
    <Note>
      This applies to:

      * Provider Connection Created: `provider.connection.created`.
    </Note>

    Junction has renamed the `source` field in this event to `provider`, aligning with the redefinition of *Source*
    and *Provider*.

    To illustrate the change, originally the `source` field described the wearable data provider. As part of this
    redefinition, `source` no longer applies in this context. Junction has introduced an identical
    `provider` field as its replacement:

    ```json provider.connection.created (Current) theme={null}
    {
        "event_type": "provider.connection.created",
        "data": {
            "user_id": "71937dd3-aebe-46b7-ab64-c287bd75b2a6",
            "resource_availability": {...},

            /** 🟢 BEGIN: New field **/
            "provider": {
                "name": "Oura",
                "slug": "oura",
                "logo": "https://logo_url.com"
            },

            /** ⚠️ BEGIN: Deprecated field, to be removed **/
            "source": {
                "name": "Oura",
                "slug": "oura",
                "logo": "https://logo_url.com"
            }
            /** ⚠️ END: Deprecated field, to be removed **/
        }
    }
    ```

    Once the deprecation cycle ends, Junction will remove the deprecated `source` field:

    ```json provider.connection.created (Future) theme={null}
    {
        "event_type": "provider.connection.created",
        "data": {
            "user_id": "71937dd3-aebe-46b7-ab64-c287bd75b2a6",
            "resource_availability": {...}

            /** 🟢 BEGIN: New field **/
            "provider": {
                "name": "Oura",
                "slug": "oura",
                "logo": "https://logo_url.com"
            },
            /** 🟢 END: New field **/
        }
    }
    ```

    <Card title="Recommended Action" icon="lightbulb-exclamation-on">
      Update your `provider.connection.created` event parsing logic to parse the `provider` field,
      and stop parsing the `source` field.
    </Card>
  </Step>
</Steps>

### The `is_final` flag in `historical.data.*.created` events

<Warning>The deprecation cycle ends on ~~30 June 2024~~ **14 September 2026**. The deprecated fields will be removed afterwards.</Warning>

We are removing the `is_final` flag from the Historical Pull Completed (`historical.data.*.created`) events.

For each resource, Junction now signals one Historical Pull Completed event, only after we have finished fetching
all data chunks. This means `is_final` no longer has a meaning, since the event itself is *final*.

<Card title="Recommended Action" icon="lightbulb-exclamation-on">
  Stop parsing `is_final` as a required field when processing `historical.data.*.created` events.
</Card>


# Providers and Resources
Source: https://docs.junction.com/changelog/wearables/providers

Changelog of wearable provider updates including Health SDK Explicit Connect mode, sync progress logs, and provider-specific improvements.

### Strava is BYOO-only (July 2026)

New Strava connections and reconnects now require [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview) credentials.
Junction's default Strava OAuth application is unavailable for new Strava connections.

Teams that need Strava should configure their own Strava OAuth application before connecting users. See the [Strava guide](/wearables/guides/strava) for setup details.

### Sleep summary extension: `recovery_readiness_score` (April 2026)

The Sleep summary now includes `recovery_readiness_score`, a value between 0 and 100 representing the provider's recovery or readiness proxy.

<Accordion icon="bed" title="Details">
  * Currently sourced from Oura readiness score, WHOOP recovery score, and Ultrahuman recovery.
  * Available as a regular column in Continuous Query: `Sleep.col("recovery_readiness_score")` / `{ "sleep": "recovery_readiness_score" }`.
</Accordion>

### Samsung Health via Junction Health SDK (March 2026)

Junction Health SDK now supports Samsung Health as an SDK-based connection on Android phones.

<Accordion icon="heart" title="Details">
  Samsung Health uses the same Sync On App Launch and opt-in Background Sync model as Health Connect.

  On Expo and React Native, the Health SDK now accepts an optional `provider` argument throughout the entire API surface. Health Connect remains
  the default provider, so you must pass `"samsung_health"` explicitly to target Samsung Health.

  Check out the [Samsung Health guide](/wearables/guides/samsung-health) for the Samsung Health setup requirements and details. Check out
  the [Junction Health SDK overview](/wearables/sdks/health/overview) for documentation on the shared API surface.

  <Note>
    This feature is available on Android SDK 5.0.0+ and React Native SDK 6.0.0+.
  </Note>
</Accordion>

### Health SDK: Explicit Connect mode (Sep 2025)

The Health SDK now supports an opt-in **[Explicit Connect mode](/wearables/sdks/health/connection-policies#explicit-connect-mode)**, which enables your application to
explicitly control the moment of Health Connect connection creation and disconnection.

<Accordion icon="heart" title="Details">
  The opt-in Explicit Connect mode also allows remote disconnection through the [Deregister Connection](/api-reference/user/deregister-a-provider) endpoint, which was not supported in the default [Auto Connect](/wearables/sdks/health/connection-policies#auto-connect-mode-default) mode.

  If your device connection management UX is built upon an explicit notion of connecting and disconnecting Apple HealthKit and Health Connect
  connections — as if they are like their cloud-based counterparts — you might find the [Explicit Connect](/wearables/sdks/health/connection-policies#explicit-connect-mode)
  mode more appealing.

  Check out the [Health SDK Connection Policies](/wearables/sdks/health/connection-policies) documentation for further details.

  <Note>
    This feature is available on iOS SDK 1.8.0+, Android SDK 4.2.0+, React Native SDK 5.4.0+ and Flutter SDK 4.6.0+.
  </Note>
</Accordion>

### Health Connect sync progress logs in Junction Dashboard (Sep 2025)

Health Connect integrations using Android SDK 4.2.0 or above now report client-side sync progress regularly to the Junction Dashboard.

<Accordion icon="heart" title="Details">
  Similar to the iOS SDK equivalent, this report includes metadata of any exception that had disrupted a sync attempt. We hope that this
  brings more visibility into Health Connect sync issues.

  <Note>
    This feature is available on Android SDK 4.2.0+, React Native SDK 5.4.0+ and Flutter SDK 4.6.0+.
  </Note>
</Accordion>

### Expanded Apple HealthKit data type coverage (April 2025)

Junction iOS SDK 1.6.0 has expanded the HealthKit data type coverage:

| Type                      | Remarks                                                                                                                                                                                           | `VitalResource`              |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| Workout Stream timeseries | Workout Distance — including cycling, swimming, rowing, cross-country skiing, downhill snow sports, paddle sports, skating sports, walking, running, and wheelchair.<br />Workout Swimming Stroke | As part of `Workout`         |
| Timeseries                | Heart Rate Recovery One Minute                                                                                                                                                                    | `HeartRateRecoveryOneMinute` |

Workout Stream timeseries data always have [Workout ID](/wearables/providers/data-attributions#workout-id) and [Sport](/wearables/providers/data-attributions#sport) attributions.

<Accordion icon="heart" title="Details">
  For more information, check out:

  #### Data Events

  * the [Workout Distance data events](/event-catalog/daily.data.workout_distance.created) in the Event Catalog;
  * the [Workout Swimming Stroke data events](/event-catalog/daily.data.workout_swimming_stroke.created) in the Event Catalog;
  * the [Heart Rate Recovery One Minute data events](/event-catalog/daily.data.heart_rate_recovery_one_minute.created) in the Event Catalog;

  #### Data access API

  * the [Get Workout Distance](/api-reference/data/timeseries/workout-distance) endpoint documentation;
  * the [Get Workout Swimming Stroke](/api-reference/data/timeseries/workout-swimming-stroke) endpoint documentation; and
  * the [Get Heart Rate Recovery One Minute](/api-reference/data/timeseries/heart-rate-recovery-one-minute) endpoint documentation.
</Accordion>

### Summary Ingestion Timestamps (April 2025)

Activity, Body, Sleep, Sleep Cycle, Workout, Meal and Menstrual Cycle now expose ingestion timestamps:

| Field        | Remarks                                                                     |
| ------------ | --------------------------------------------------------------------------- |
| `created_at` | The time at which Junction first ingested this summary.                     |
| `updated_at` | The time at which Junction ingested the most recent update to this summary. |

<Accordion icon="heart" title="Details">
  For more information, check out:

  #### Data Events

  * the [Activity data events](/event-catalog/daily.data.activity.created) in the Event Catalog;
  * the [Body data events](/event-catalog/daily.data.body.created) in the Event Catalog;
  * the [Sleep data events](/event-catalog/daily.data.sleep.created) in the Event Catalog;
  * the [Sleep Cycle data events](/event-catalog/daily.data.sleep_cycle.created) in the Event Catalog;
  * the [Workout data events](/event-catalog/daily.data.workouts.created) in the Event Catalog;
  * the [Menstrual Cycle data events](/event-catalog/daily.data.menstrual_cycle.created) in the Event Catalog; and
  * the [Meal data events](/event-catalog/daily.data.meal.created) in the Event Catalog.

  #### Data access API

  * the [Get Activity](/api-reference/data/activity/get-summary) endpoint documentation;
  * the [Get Body](/api-reference/data/body/get-summary) endpoint documentation;
  * the [Get Sleep](/api-reference/data/sleep/get-summary) endpoint documentation;
  * the [Get Sleep Cycle](/api-reference/data/sleep-cycle/get-summary) endpoint documentation;
  * the [Get Workout](/api-reference/data/workouts/get-summary) endpoint documentation;
  * the [Get Menstrual Cycle](/api-reference/data/menstrual-cycle/get-summary) endpoint documentation; and
  * the [Get Meal](/api-reference/data/meal/get-summary) endpoint documentation.
</Accordion>

### Expanded Apple HealthKit data type coverage (March 2025)

Junction iOS SDK 1.5.0 has expanded the HealthKit data type coverage:

| Type                 | Remarks                                                                                                                                                                                                                                                                                                                                 |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Profile summary      | Wheelchair Mode usage                                                                                                                                                                                                                                                                                                                   |
| Activity summary     | Wheelchair Mode support (distance and push count)                                                                                                                                                                                                                                                                                       |
| Body summary         | Lean Body Mass, Body Mass Index, Waist Circumference                                                                                                                                                                                                                                                                                    |
| Timeseries resources | Lean Body Mass, Body Mass Index, Waist Circumference, Wheelchair Push, Distance (Wheelchair Mode support), Stand Hour, Stand Duration, Sleep Apnea Alert, Sleep Breathing Disturbance, FEV1, Forced Vital Capacity, Peak Expiratory Flow Rate, Inhaler Usage, Fall, UV Exposure, Daylight Exposure, Handwashing, Basal Body Temperature |

<Accordion icon="heart" title="Details">
  For more information, check out:

  #### Data Events

  * the [Profile data events](/event-catalog/daily.data.profile.created) in the Event Catalog;
  * the [Activity data events](/event-catalog/daily.data.activity.created) in the Event Catalog;
  * the [Body data events](/event-catalog/daily.data.body.created) in the Event Catalog;
  * the [Lean Body Mass data events](/event-catalog/daily.data.lean_body_mass.created) in the Event Catalog;
  * the [Body Mass Index data events](/event-catalog/daily.data.body_mass_index.created) in the Event Catalog;
  * the [Waist Circumference data events](/event-catalog/daily.data.waist_circumference.created) in the Event Catalog;
  * the [Wheelchair Push data events](/event-catalog/daily.data.wheelchair_push.created) in the Event Catalog;
  * the [Distance data events](/event-catalog/daily.data.distance.created) in the Event Catalog;
  * the [Stand Hour data events](/event-catalog/daily.data.stand_hour.created) in the Event Catalog;
  * the [Stand Duration data events](/event-catalog/daily.data.stand_duration.created) in the Event Catalog;
  * the [Sleep Apnea Alert data events](/event-catalog/daily.data.sleep_apnea_alert.created) in the Event Catalog;
  * the [Sleep Breathing Disturbance data events](/event-catalog/daily.data.sleep_breathing_disturbance.created) in the Event Catalog;
  * the [FEV1 data events](/event-catalog/daily.data.forced_expiratory_volume_1.created) in the Event Catalog;
  * the [Forced Vital Capacity data events](/event-catalog/daily.data.forced_vital_capacity.created) in the Event Catalog;
  * the [Peak Expiratory Flow Rate data events](/event-catalog/daily.data.peak_expiratory_flow_rate.created) in the Event Catalog;
  * the [Inhaler Usage data events](/event-catalog/daily.data.inhaler_usage.created) in the Event Catalog;
  * the [Fall data events](/event-catalog/daily.data.fall.created) in the Event Catalog;
  * the [UV Exposure data events](/event-catalog/daily.data.uv_exposure.created) in the Event Catalog;
  * the [Daylight Exposure data events](/event-catalog/daily.data.daylight_exposure.created) in the Event Catalog;
  * the [Handwashing data events](/event-catalog/daily.data.handwashing.created) in the Event Catalog; and
  * the [Basal Body Temperature data events](/event-catalog/daily.data.basal_body_temperature.created) in the Event Catalog.

  #### Data access API

  * the [Get Profile](/api-reference/data/profile/get-summary) endpoint documentation;
  * the [Get Activity](/api-reference/data/activity/get-summary) endpoint documentation;
  * the [Get Body](/api-reference/data/body/get-summary) endpoint documentation;
  * the [Get Lean Body Mass](/api-reference/data/timeseries/lean-body-mass) endpoint documentation;
  * the [Get Body Mass Index](/api-reference/data/timeseries/body-mass-index) endpoint documentation;
  * the [Get Waist Circumference](/api-reference/data/timeseries/waist-circumference) endpoint documentation;
  * the [Get Wheelchair Push](/api-reference/data/timeseries/wheelchair-push) endpoint documentation;
  * the [Get Distance](/api-reference/data/timeseries/distance) endpoint documentation;
  * the [Get Stand Hour](/api-reference/data/timeseries/stand-hour) endpoint documentation;
  * the [Get Stand Duration](/api-reference/data/timeseries/stand-duration) endpoint documentation;
  * the [Get Sleep Apnea Alert](/api-reference/data/timeseries/sleep-apnea-alert) endpoint documentation;
  * the [Get Sleep Breathing Disturbance](/api-reference/data/timeseries/sleep-breathing-disturbance) endpoint documentation;
  * the [Get FEV1](/api-reference/data/timeseries/forced-expiratory-volume-1) endpoint documentation;
  * the [Get Forced Vital Capacity](/api-reference/data/timeseries/forced-vital-capacity) endpoint documentation;
  * the [Get Peak Expiratory Flow Rate](/api-reference/data/timeseries/peak-expiratory-flow-rate) endpoint documentation;
  * the [Get Inhaler Usage](/api-reference/data/timeseries/inhaler-usage) endpoint documentation;
  * the [Get Fall](/api-reference/data/timeseries/fall) endpoint documentation;
  * the [Get UV Exposure](/api-reference/data/timeseries/uv-exposure) endpoint documentation;
  * the [Get Daylight Exposure](/api-reference/data/timeseries/daylight-exposure) endpoint documentation;
  * the [Get Handwashing](/api-reference/data/timeseries/handwashing) endpoint documentation; and
  * the [Get Basal Body Temperature](/api-reference/data/timeseries/basal-body-temperature) endpoint documentation.
</Accordion>

### Improved LibreView practice connection process (Jan 2025)

You can now connect LibreView patients in [46 regions in the EU environment](/wearables/guides/abbott-libreview#-junction-eu-region)
and [8 regions in the US environment](/wearables/guides/abbott-libreview#-junction-us-region).

<Accordion icon="link" title="Details">
  Previously, the Link API and Link Widget listed only the regions to which our practice account was registered. However, these
  practice accounts can often connect to patients in a larger geographical area beyond the region of registration. For example,
  patients in most EMEA countries can connect through our Netherlands practice account.

  Therefore, we have now changed:

  1. Link Widget to display an exhaustive list of regions which our practice account can reach; and
  2. Link API to accept any ISO 3166-1 code that is on the list.

  In other words, say if you have a patient domiciled in Belgium, it is no longer necessary to teach them to choose "Netherlands" when
  connecting through the Link Widget. They can now intuitively choose "Belgium" when asked by the Link Widget.

  Check out the [Abbott LibreView](/wearables/guides/abbott-libreview) guide and
  the [Connect Email Provider](/api-reference/link/link-email-provider) endpoint
  for more information.
</Accordion>

### Electrocardiogram, Heart Rate Alert and AFib Burden (Dec 2024)

Junction now supports collecting Electrocardiogram (ECG) data and a couple of related data points from a set of providers:

| Resource                                                | Apple HealthKit <sup>\[3]</sup> | Fitbit            | Withings          | Kardia            |
| ------------------------------------------------------- | ------------------------------- | ----------------- | ----------------- | ----------------- |
| Electrocardiogram Summary<br />`electrocardiogram`      | ✅                               | -                 | ✅                 | ✅                 |
| ECG Voltage timeseries<br />`electrocardiogram_voltage` | ✅                               | -                 | ✅                 | ✅                 |
| Heart Rate Alert<br />`heart_rate_alert`                | ✅ <sup>\[1]</sup>               | ✅ <sup>\[2]</sup> | ✅ <sup>\[2]</sup> | ✅ <sup>\[2]</sup> |
| AFib Burden<br />`afib_burden`                          | ✅                               | -                 | -                 | -                 |

<Info>
  <sup>\[1]</sup> Irregular rhythm alerts, high heart rate alerts and low heart rate alerts.<br />
  <sup>\[2]</sup> Irregular rhythm alerts only.<br />
  <sup>\[3]</sup> Requires Junction iOS SDK 1.3.0+, Junction Flutter SDK 4.4.0+ or Junction React Native SDK 5.1.0+.
</Info>

<Accordion icon="heart" title="Details">
  For more information, check out:

  * the [Get Electrocardiogram](/api-reference/data/electrocardiogram/get-summary) endpoint documentation;
  * the [Get Electrocardiogram Voltage](/api-reference/data/timeseries/electrocardiogram-voltage) endpoint documentation;
  * the [Get Heart Rate Alert](/api-reference/data/timeseries/heart-rate-alert) endpoint documentation;
  * the [Get AFib Burden](/api-reference/data/timeseries/afib-burden) endpoint documentation;
  * the [Electrocardiogram data events](/event-catalog/daily.data.electrocardiogram.created) in the Event Catalog;
  * the [Electrocardiogram Voltage data events](/event-catalog/daily.data.electrocardiogram_voltage.created) in the Event Catalog;
  * the [Heart Rate Alert data events](/event-catalog/daily.data.heart_rate_alert.created) in the Event Catalog; and
  * the [AFib Burden data events](/event-catalog/daily.data.afib_burden.created) in the Event Catalog.
</Accordion>

### Sleep Cycle Summary Type (Nov 2024)

We are introducing a new Sleep Cycle summary type (`sleep_cycle`) which captures the detailed hypnogram of a sleep session.
The existing hypnogram timeseries type is deprecated in favor of this new summary type.

<Accordion icon="moon" title="Details">
  Check out the [Get Sleep Cycle](/api-reference/data/sleep-cycle/get-summary) endpoint and
  the [Sleep Cycle data events](/event-catalog/daily.data.sleep_cycle.created) in the Event Catalog
  for more information.
</Accordion>

### Body Summary model extension (Nov 2024)

The Body Summary model now includes 4 new fields:

* `water_percentage` - The percentage of water in the body.
* `muscle_mass_percentage` - The percentage of muscle mass in the body.
* `visceral_fat_index` - Provider's score of visceral fat levels.
* `bone_mass_percentage` - The percentage of bone mass in the body.

At the time of writing, all four are only available from the Withings provider.

<Accordion icon="chart-scatter" title="Details">
  Check out the [Get Body Summary](/api-reference/data/body/get-summary) endpoint and
  the [Body Created](/event-catalog/daily.data.body.created) event for more information.
</Accordion>

### Sleep Type (Nov 2024)

Sleep summaries now have a new *Sleep Type* field.

<Accordion icon="moon" title="Details">
  Junction maps the Sleep Type from the source provider whenever possible. If this is unavailable at source,
  Junction infers the Sleep Type based on the sleep session duration.

  | Sleep Type         | Description                                              |
  | ------------------ | -------------------------------------------------------- |
  | `long_sleep`       | >=3 hours of sleep                                       |
  | `short_sleep`      | \<3 hours of sleep                                       |
  | `acknowledged_nap` | User-acknowledged naps, typically under 3 hours of sleep |
  | `unknown`          | The sleep session recording is ongoing.                  |

  Check out the [Get Sleep endpoint](/api-reference/data/sleep/get-summary) and the [Sleep data events](/event-catalog/daily.data.sleep.created)
  in the Event Catalog for more information.
</Accordion>

### Apple HealthKit data sync improvements (Aug 2024)

An overhauled Apple HealthKit sync engine is now available across all Junction Mobile SDK platforms.
The changes bring historical data sync incrementality, prioritization, network resiliency and reduced resource usage.

<Accordion icon="apple" title="Details">
  1. Most resources now use a resumable sync & upload process.

     * This enables the SDK to make constant forward progress, especially when you have specified a very large historical pull stage (60-365 days)
       through the [Team Data Pull Preferences](/api-reference/org-management/team-data-pull-preferences/upsert-team-data-pull-preferences) API.

  2. The SDK now prioritizes syncing of summary types over timeseries data.

     * During the historical pull stage, all timeseries resources would be suppressed until all the summary types have been successfully uploaded.

     * After the historical pull stage, the SDK would make forward progress on summary types, before it proceeds to deal with the more CPU-time-consuming timeseries data that are more prone to operating system throttling.

  3. With an updated iOS App Target configuration, the SDK would now self-register as a `BGProcessingTask` or `BGHealthResearchTask` (iOS 17+) to the iOS BackgroundTasks framework.

     * This would provide the SDK an additional opportunity to complete any resource sync that is not able to complete during normal HealthKit background delivery.

     * Please refer to the [Apple HealthKit guide](/wearables/guides/apple-healthkit#1-setup-app-entitlements) on how to update your iOS App Target configuration in Xcode.

  | Platform     | Release Note                                                                                      |
  | ------------ | ------------------------------------------------------------------------------------------------- |
  | Native iOS   | [vital-ios 1.2.2](https://github.com/tryVital/vital-ios/releases/tag/1.2.2)                       |
  | Flutter      | [vital-flutter 4.2.1](https://github.com/tryVital/vital-flutter/releases/tag/vital_health-v4.2.1) |
  | React Native | [vital-react-native 4.2.1](https://github.com/tryVital/vital-react-native/releases/tag/4.2.1)     |
</Accordion>

### Menstrual Cycle Tracking (July 2024)

Menstrual cycle tracking data can now be collected through the Apple HealthKit and Android
Health Connect integrations.

<Accordion icon="venus" title="Details">
  Ask for data permission on the `MenstrualCycle` resource from the user through the Junction Health SDK.

  Check out the [Get Menstrual Cycles](/api-reference/data/menstrual-cycle/get-summary) endpoint and
  the [Menstrual Cycle data events](/event-catalog/daily.data.menstrual_cycle.created) in the Event Catalog
  for more information.
</Accordion>

### Steps count in workouts (May 2024)

Workouts now include the total steps count during the session (if available).

<Accordion icon="tag" title="Details">
  Check out the [Get Workouts](/api-reference/data/workouts/get-summary) endpoint and
  the [Workout Created](/event-catalog/daily.data.workouts.created) event for more information.
</Accordion>

### New Abbott LibreView integration (May 2024)

Junction has introduced a new Abbott LibreView provider (`abbott_libreview`). It uses password authentication,
accepting LibreView patient account credentials.

<Accordion icon="tag" title="Details">
  This is an alternative option to our practice-based Freestyle Libre provider (`freestyle_libre`).

  With `abbott_libreview`, Junction uses the provided LibreView patient account credentials to connect directly
  to LibreView. There is no involvement of a LibreView practice in this integration.

  Note that both practice-based and patient-based connections would continue to be supported in parallel. The
  `abbott_libreview` integration does not replace the `freestyle_libre` integration.

  Check out the [Abbott LibreView / Freestyle Libre](/wearables/guides/abbott-libreview) guide for more information.
</Accordion>

### Removed Fitbit and Oura required scopes (May 2024)

The Fitbit profile scope and Oura email scope are no longer required for establishing connections.

For Fitbit specifically, please be aware of the [updated Fitbit Time Zone resolution precedences](/wearables/guides/fitbit#time-zone-resolution). When
your user refuses to grant both the activity and the profile scope, Fitbit data are more likely to be timestamped incorrectly on a UTC time basis
due to inaccurate or unavailable time zone information.

<Accordion icon="tag" title="Details">
  We have now updated our Fitbit and Oura integrations not to depend on these scopes to be **minimally functional**.

  If you are interested in requiring your users to grant certain scopes when connecting an OAuth provider, check out the
  [Team Scope Requirements](/changelog/wearables/api#team-scope-requirements-apr-2024) changelog entry and
  the [Set Team Scope Requirements](/api-reference/org-management/team-scope-requirements/upsert-team-scope-requirements) endpoint for more information.
</Accordion>

### Freestyle Libre: India region (Apr 2024)

Our Freestyle Libre integration now supports the LibreView India region.

<Accordion icon="tag" title="Details">
  Check out the [Abbott LibreView](/wearables/guides/abbott-libreview#bring-your-own-practice) documentation for more information.
</Accordion>

### Health Connect Background Sync (Mar 2024)

Junction Mobile SDK now includes an experimental Background Sync feature for Android Health Connect.

<Accordion icon="robot" title="Details">
  The feature is available through the following Junction Mobile SDK releases:

  | Platform       | Version |
  | -------------- | ------- |
  | Native Android | 2.0.0+  |
  | Flutter        | 3.2.0+  |
  | React Native   | 3.1.0+  |

  Check out the following documentation for information, integration guidance, as well as caveats
  of the experimental Background Sync:

  * [Junction Health SDK: Automatic Data Sync](/wearables/sdks/health/overview#automatic-data-sync) for a general overview
  * [Android Health Connect integration guide](/wearables/guides/android-health-connect#background-sync), which includes guidance on the experimental Background Sync feature.
</Accordion>

### Expanded Source Type attribution (Feb 2024)

Data from Fitbit, Oura, Garmin and Freestyle Libre now comes with Source Type attributions.

<Accordion icon="tag" title="Details">
  Check out the [Data Attributions](/wearables/providers/data-attributions#source-type) documentation for more information.
</Accordion>


# Callbacks
Source: https://docs.junction.com/connect/callbacks

Receive postMessage callbacks from Feature Embed iframes.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

When running in the `feature_embed` modality, the Junction iframe notifies your host page of significant events by posting a message to the parent window via the [Web Messaging API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).

Callbacks are only emitted from Feature Embed iframes. They are never emitted in the `link_out` modality.

## Listening for callbacks

Add a `message` event listener to the host window and filter by the `type` field:

```ts theme={null}
// Replace with your organization's slug from Junction Connect configuration.
const JUNCTION_ORIGIN = "https://<slug>.ehr.junction.com";

window.addEventListener("message", (event) => {
  // Always validate the origin before acting on the message.
  if (event.origin !== JUNCTION_ORIGIN) {
    return;
  }

  const message = event.data;

  switch (message.type) {
    case "urn:junction:order:created":
      console.log("Order created", message.data);
      break;

    case "urn:junction:order:creation_cancelled":
      console.log("Order creation cancelled", message.data);
      break;
  }
});
```

<Warning>
  Always check `event.origin` before reading `event.data`. Reject any message whose origin does not match your Junction subdomain.
</Warning>

## Security

Each Junction organization is served from a dedicated subdomain: `https://<slug>.ehr.junction.com`, where `<slug>` is the unique slug from your [Junction Connect configuration](/connect/concepts#configuration).

Junction always targets your registered origin explicitly — it never posts to `"*"`. The target origin is resolved from the iframe's `ancestorOrigins` or `document.referrer`. If the origin cannot be resolved, the message is silently dropped rather than broadcast.

Your listener must independently validate `event.origin` before trusting the payload, since any page on your origin may receive `message` events.

## Callback reference

### `urn:junction:order:created`

Emitted when the provider successfully submits a lab order via the `order_creation` feature.

**Payload**

| Field      | Type            | Description                                                   |
| ---------- | --------------- | ------------------------------------------------------------- |
| `order_id` | `string (UUID)` | The Junction order ID.                                        |
| `user_id`  | `string (UUID)` | The Junction user (patient) ID for whom the order was placed. |
| `team_id`  | `string (UUID)` | The Junction team ID the order belongs to.                    |

**Example**

```json theme={null}
{
  "type": "urn:junction:order:created",
  "data": {
    "order_id": "00000000-0000-0000-0000-000000000000",
    "user_id": "00000000-0000-0000-0000-000000000000",
    "team_id": "00000000-0000-0000-0000-000000000000"
  }
}
```

**Common uses**

* Close or hide the embed iframe after a successful order.
* Navigate the host application to an order detail view.
* Trigger a server-side webhook or audit log entry.

***

### `urn:junction:order:creation_cancelled`

Emitted when the provider explicitly cancels the order creation flow before submitting.

**Payload**

| Field     | Type                    | Description                                                                                                 |
| --------- | ----------------------- | ----------------------------------------------------------------------------------------------------------- |
| `user_id` | `string (UUID) \| null` | The Junction user (patient) ID, if one was selected before cancellation. `null` if no patient was selected. |
| `team_id` | `string (UUID)`         | The Junction team ID associated with the session.                                                           |

**Example**

```json theme={null}
{
  "type": "urn:junction:order:creation_cancelled",
  "data": {
    "user_id": null,
    "team_id": "00000000-0000-0000-0000-000000000000"
  }
}
```

**Common uses**

* Close or hide the embed iframe.
* Return the provider to a previous screen in your application.


# Concepts
Source: https://docs.junction.com/connect/concepts

Core concepts behind Junction Connect, including orgs, teams, members, users, and integration-managed members.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

## Preface

Junction Connect helps you integrate Junction Dashboard features into your web application.

To facilitate this, Junction Connect makes a few assumptions:

1. You programmatically manage the profiles of the providers as [integration-managed members](#integration-managed-members).
2. An integration-managed member must be at least a **team admin** of the team for Junction Connect to work.

What *provider* means in your use case may differ. Here are two example mappings for your consideration:

<AccordionGroup>
  <Accordion title="I am a health care practice..." icon="hospital">
    | Junction noun          | Synonym     | Meaning                                                                           | API                                                    |
    | ---------------------- | ----------- | --------------------------------------------------------------------------------- | ------------------------------------------------------ |
    | Org                    | -           | Your business entity.                                                             | [Management API](/api-details/junction-management-api) |
    | Team                   | -           | A division in your Junction organization, containing an isolated pool of *users*. | [Management API](/api-details/junction-management-api) |
    | Member<br />Org Admin  | Staff       | Your members of staff.                                                            | [Management API](/api-details/junction-management-api) |
    | Member<br />Team Admin | Provider 📍 | Your members of staff, with access restricted to a specific set of *teams*.       | [Management API](/api-details/junction-management-api) |
    | User                   | Patient     | An end user of the device and lab testing services; belongs to a *team*.          | [Junction API](/api-details/junction-api)              |
  </Accordion>

  <Accordion title="I am an EMR..." icon="database">
    | Junction noun          | Synonym     | Meaning                                                                  | API                                                    |
    | ---------------------- | ----------- | ------------------------------------------------------------------------ | ------------------------------------------------------ |
    | Org                    | -           | Your business entity.                                                    | [Management API](/api-details/junction-management-api) |
    | Team                   | -           | A customer of yours.                                                     | [Management API](/api-details/junction-management-api) |
    | Member<br />Org Admin  | -           | Your own members of staff.                                               | [Management API](/api-details/junction-management-api) |
    | Member<br />Team Admin | Provider 📍 | A customer's members of staff.                                           | [Management API](/api-details/junction-management-api) |
    | User                   | Patient     | An end user of the device and lab testing services; belongs to a *team*. | [Junction API](/api-details/junction-api)              |
  </Accordion>
</AccordionGroup>

## Core flow

<Steps>
  <Step title="Prepare identity and team records">
    Use the Junction Management API to create or resolve the team and integration-managed member for the current provider.
  </Step>

  <Step title="Create a Dashboard URL">
    Call [Create Dashboard URL](/api-reference/org-management/connect/create-dashboard-url) with the target member, team, modality, feature, and environment.
  </Step>

  <Step title="Launch Junction Connect">
    Open the returned Dashboard URL in a top-level browser context for Link Out, or load it in an iframe for Feature Embed.
  </Step>
</Steps>

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant PartnerApp as Your Web App
    participant PartnerBackend as Your Backend
    participant API as Junction Management API
    participant Junction as Junction Web App

    PartnerApp->>PartnerBackend: Request dashboard URL
    PartnerBackend->>API: POST /v1/org/{org_id}/ehr_integration/create_dashboard_url
    API-->>PartnerBackend: Dashboard URL
    PartnerBackend-->>PartnerApp: Dashboard URL
    PartnerApp->>Junction: Open URL
    Junction->>Junction: Configure session and route to feature
```

## Configuration

You can configure Junction Connect either through ["Org Config → Junction Connect"](https://app.junction.com/org/ehr-integration) in the Junction Dashboard, or programmatically via the [Set Junction Connect Configuration](/api-reference/org-management/connect/set-configuration) endpoint on the Junction Management API.

| Setting         | Description                                                                                                                                                                                                                                                                                                                                                                                                |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Unique slug     | A unique kebab-case slug that identifies your organization, e.g., `my-clinical-practice`. Your [Fast Launch](/connect/fast-launch) subdomain is based on this slug.                                                                                                                                                                                                                                        |
| Modalities      | The Junction Connect modalities you intend to use: `link_out`, `feature_embed`, both, or none.                                                                                                                                                                                                                                                                                                             |
| Allowed origins | Origins allowed to launch Link Out or Feature Embed sessions.<br /><br />For each origin, you may optionally specify a publicly accessible [Session Continuation URL](/connect/session-continuation). If this is left unspecified, [Fast Launch](/connect/fast-launch) will not work, and Junction Connect will show an error screen when it encounters a launch error or an irrecoverable client session. |

## Integration-managed members

To launch Junction Connect in either modality, identify the provider who should use Junction Dashboard as an integration-managed member.

These members are created and managed through the Junction Management API by your backend system.

Integration-managed members:

* can sign in only through Junction Connect launch flows;
* cannot sign in through Junction Dashboard identity providers; and
* can be assigned team role bindings when created or updated.

## Feature slug

[Features](/connect/overview#features) are represented by a **feature slug**.

Use a feature slug when you:

* create a pre-authorized [Dashboard URL](/api-reference/org-management/connect/create-dashboard-url);
* assemble a [Fast Launch URL](/connect/fast-launch); or
* handle a [Session Continuation request](/connect/session-continuation) from Junction Connect.


# Fast Launch
Source: https://docs.junction.com/connect/fast-launch

Use Fast Launch URLs to reduce repeated Junction Connect launch latency when a compatible Junction session already exists.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Fast Launch lets your client-side application open a Junction feature without first asking your backend to create a dashboard URL. When Junction detects an existing compatible session, it routes directly to the requested feature. If it cannot reuse a session, it redirects to your [session continuation URL](/connect/session-continuation), where your application creates a new dashboard URL.

Use Fast Launch when:

* you embed Junction features in iframes; and
* users often move between parts of your app that embed different Junction features.

## Fast Launch URL

Use your partner integration slug as the subdomain.

```text theme={null}
https://{integration_slug}.ehr.junction.com/auth/launch
```

You can identify the target member and team with either partner references or Junction IDs.

<CodeGroup>
  ```text Partner references theme={null}
  https://{integration_slug}.ehr.junction.com/auth/launch
    ?integration_member_id={integration_member_id}
    &integration_team_id={integration_team_id}
    &modality={modality}
    &feature={feature}
    &environment={environment}
  ```

  ```text Junction IDs theme={null}
  https://{integration_slug}.ehr.junction.com/auth/launch
    ?member_id={member_id}
    &team_id={team_id}
    &modality={modality}
    &feature={feature}
    &environment={environment}
  ```
</CodeGroup>

| Parameter               | Description                                                                 |
| ----------------------- | --------------------------------------------------------------------------- |
| `integration_member_id` | Your unique reference for the member. Mutually exclusive with `member_id`.  |
| `member_id`             | Junction-issued member ID. Mutually exclusive with `integration_member_id`. |
| `integration_team_id`   | Your unique reference for the team. Mutually exclusive with `team_id`.      |
| `team_id`               | Junction-issued team ID. Mutually exclusive with `integration_team_id`.     |
| `modality`              | `feature_embed` or `link_out`.                                              |
| `feature`               | Supported feature slug, such as `order_creation` or `team_config`.          |
| `environment`           | `sandbox` or `production`.                                                  |

## Feature Embed flow

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant PartnerApp as Your Web App
    participant Junction as Junction Connect
    participant Continuation as Session Continuation URL
    participant API as Junction Management API

    PartnerApp->>Junction: Load Fast Launch URL in iframe
    alt Compatible Junction session exists
        Junction->>Junction: Route directly to requested feature
    else Session missing or incompatible
        Junction-->>PartnerApp: Redirect iframe to session continuation URL
        PartnerApp->>Continuation: Request with launch parameters
        Continuation->>Continuation: Validate app session and parameters
        Continuation->>API: Create dashboard URL
        API-->>Continuation: Post-authorization URL
        Continuation-->>PartnerApp: 307 redirect to post-authorization URL
        PartnerApp->>Junction: Open post-authorization URL
        Junction->>Junction: Configure session and route to feature
    end
```

## Link Out flow

For Link Out, open the same Fast Launch URL in a top-level browser context. If Junction can reuse the session, it routes immediately. Otherwise, it redirects the browser to your session continuation URL before returning to Junction with a new post-authorization URL.

## Implementation notes

* Keep the `feature`, `modality`, and `environment` values in your own allowlist.
* Prefer partner references (`integration_member_id` and `integration_team_id`) when they are already available in your client runtime.
* Your session continuation endpoint must validate the current partner session before creating the dashboard URL.


# Getting Started
Source: https://docs.junction.com/connect/getting-started

Configure Junction Connect and launch a Junction workflow from your application.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Use this guide to launch Junction from your application for the first time.

## Prerequisites

You need:

* a Junction organization ID;
* a Junction Management Key;
* a registered Junction Connect integration with at least one enabled modality;
* an allowed origin for your web application; and
* a session continuation URL controlled by your application.

<Tip>
  Junction Management API requests use `https://api.management.junction.com/` and the `X-Management-Key` header.
</Tip>

<Steps>
  <Step title="Configure your integration">
    Use the [Set Junction Connect Configuration endpoint](/api-reference/org-management/connect/set-configuration) to set your slug, modalities, and allowed origins. See [Concepts -> Configuration](/connect/concepts#configuration) for details about each setting.
  </Step>

  <Step title="Create or resolve a team">
    Use the [Create Team endpoint](/api-reference/org-management/team/create-team). Teams can include an `integration_team_id`, which is your stable team reference.

    ```bash cURL theme={null}
    curl --request POST \
      --url https://api.management.junction.com/v1/org/{org_id}/team \
      --header 'Content-Type: application/json' \
      --header 'X-Management-Key: <management-key>' \
      --data '{
        "name": "Downtown Clinic",
        "region": "us",
        "integration_team_id": "clinic_123"
      }'
    ```

    If the team already exists, use the [Resolve Team endpoint](/api-reference/org-management/team/resolve-team) to resolve it by integration reference:

    ```bash cURL theme={null}
    curl --request POST \
      --url https://api.management.junction.com/v1/org/{org_id}/resolve_team \
      --header 'Content-Type: application/json' \
      --header 'X-Management-Key: <management-key>' \
      --data '{
        "integration_team_id": "clinic_123"
      }'
    ```
  </Step>

  <Step title="Create or resolve an integration-managed member">
    Use the [Create Integration-Managed Member endpoint](/api-reference/org-management/connect/create-member) to create the member that should be signed in through Junction Connect. Include team role bindings for every team the member should access.

    ```bash cURL theme={null}
    curl --request POST \
      --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/member \
      --header 'Content-Type: application/json' \
      --header 'X-Management-Key: <management-key>' \
      --data '{
        "integration_member_id": "user_456",
        "name": "Alex Kim",
        "email": "alex@example.com",
        "team_role_bindings": [
          {
            "team_id": "00000000-0000-0000-0000-000000000000",
            "role": "admin"
          }
        ]
      }'
    ```

    If the member already exists, use the [Resolve Integration-Managed Member endpoint](/api-reference/org-management/connect/resolve-member) to resolve it by integration reference:

    ```bash cURL theme={null}
    curl --request POST \
      --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/resolve_member \
      --header 'Content-Type: application/json' \
      --header 'X-Management-Key: <management-key>' \
      --data '{
        "integration_member_id": "user_456"
      }'
    ```
  </Step>

  <Step title="(Optional) Create or resolve a user (patient)">
    Skip this step unless you are launching a [feature](/connect/overview#features) that is scoped to a specific patient, such as `order_creation:{user_id}`. In that case the patient must exist as a Junction user in the destination team before you create the dashboard URL — the patient-scoped feature slug embeds the Junction `user_id`.

    Use the team-scoped Junction API (see [authentication](/api-details/junction-api#authentication) and [environments](/api-details/junction-api#environments)):

    * [Create user](/api-reference/user/create-user) — keyed by your `client_user_id`.
    * [Update user demographics](/api-reference/user/upsert-info) — set name, date of birth, and any other fields the feature requires.

    To launch the embed scoped to this patient, use the patient-scoped feature slug (e.g. `order_creation:{user_id}`) as the `feature` value in the next step, substituting the resolved Junction `user_id`.
  </Step>

  <Step title="Create a dashboard URL">
    Use the [Create Dashboard URL endpoint](/api-reference/org-management/connect/create-dashboard-url) to create a post-authorization URL for the member, team, modality, feature, and environment.

    ```bash cURL theme={null}
    curl --request POST \
      --url https://api.management.junction.com/v1/org/{org_id}/ehr_integration/create_dashboard_url \
      --header 'Content-Type: application/json' \
      --header 'X-Management-Key: <management-key>' \
      --data '{
        "integration_member_id": "user_456",
        "integration_team_id": "clinic_123",
        "modality": "feature_embed",
        "feature": "order_creation",
        "environment": "sandbox"
      }'
    ```
  </Step>

  <Step title="Launch the URL">
    Use the URL returned by the [Create Dashboard URL endpoint](/api-reference/org-management/connect/create-dashboard-url). For `feature_embed`, load it in an iframe:

    ```html theme={null}
    <iframe
      src="https://..."
      title="Junction order creation"
      style="width: 100%; height: 800px; border: 0;"
    ></iframe>
    ```

    For `link_out`, open the returned URL in a top-level navigation context, such as a new tab or popup window.

    <Note>
      For faster repeated launches, use [Fast Launch](/connect/fast-launch) after you have implemented [Session Continuation](/connect/session-continuation).
    </Note>
  </Step>
</Steps>


# Overview
Source: https://docs.junction.com/connect/overview

Embed Junction Dashboard experiences into your application or launch the dashboard in a separate browser context.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Junction Connect enables you to integrate the Junction Dashboard provider experience seamlessly into your existing web application.
This serves as an alternative to building your own provider experience on top of the [Lab Testing API](/lab/overview/introduction).

## Modalities

Junction supports two Junction Connect modalities:

<Card title="Link Out" icon="link">
  Opens Junction Dashboard in a top-level browser context, such as a new tab or popup window.
  <Info>You want the provider to leave your app context and use the full Junction Dashboard.</Info>
</Card>

<Card title="Feature Embed" icon="frame">
  Embed a specific Junction Dashboard feature as an iframe into your web application.
  <Info>You want your web application to own navigation and page structure while the iframe stays focused on one Junction workflow.</Info>
</Card>

## Environments

Junction Connect supports launching a team into the sandbox environment or the production environment. The sandbox environment is
isolated from the production environment, and provides you access to [additional testing facilities](/lab/overview/sandbox), such as
creating test orders as well as order life cycle simulation.

<Info>
  Specify your target environment through the request `environment` field in the [Create Dashboard URL](/api-reference/org-management/connect/create-dashboard-url) endpoint.
</Info>

For example, you can create test orders in Junction Dashboard and test the embedded order detail experience throughout the order lifecycle:

1. Create a Dashboard URL with `environment` set to `sandbox` and `feature` set to `order_creation`.
2. Open the URL and create a test order in Junction Dashboard.
3. As the order moves through its lifecycle, create a new sandbox Dashboard URL with `feature` set to `order:{id}`.
4. Load each returned URL in your embed to test the order detail experience at the relevant lifecycle state.

## Features

Junction Connect supports the following features:

| Feature slug               | Feature                                      |
| -------------------------- | -------------------------------------------- |
| `order_creation`           | Order creation                               |
| `order_creation:{user_id}` | Order creation for a specific patient (user) |
| `order:{id}`               | Order detail                                 |
| `team_panels`              | Panel management                             |
| `team_config`              | Team configuration                           |

While you must specify a feature to launch for both the *Link Out* and *Feature Embed* modalities, they have different behaviors:

| Modality      | Behavior                                                                                                                                                   |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Link Out      | The feature controls where the Junction Dashboard lands initially. The provider can navigate away afterward and use other parts of the Junction Dashboard. |
| Feature Embed | The iframe is locked to the feature you requested.                                                                                                         |


# Session Continuation
Source: https://docs.junction.com/connect/session-continuation

Implement the endpoint Junction calls when it needs your application to recover a Junction Connect session.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Session continuation lets Junction recover a Junction Connect session when no compatible session exists, such as after prolonged inactivity or when using Fast Launch.

Your session continuation URL must be a publicly accessible endpoint controlled by your application. Junction redirects the user's browser to this endpoint with launch parameters. Your application validates the current signed-in user, creates a Junction dashboard URL, and returns a `307 Temporary Redirect` to that URL.

## Requirements

Your endpoint must:

* validate that the browser has a valid session with your application;
* verify that the requested integration member and team match the signed-in user and account context;
* validate the requested modality, feature, and environment before forwarding them to Junction;
* create a dashboard URL through the Junction Management API; and
* respond with a `307 Temporary Redirect` to the returned post-authorization URL.

<Warning>
  Do not blindly forward query parameters into the Management API. Treat session continuation requests as untrusted input and authorize them against your application's current session.
</Warning>

## Continuation flow

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant Browser as User Browser
    participant Junction as Junction App
    participant Partner as Partner Session Continuation URL
    participant API as Junction Management API

    Browser->>Junction: Open Junction Connect or Fast Launch URL
    Junction->>Junction: No compatible session
    Junction-->>Browser: Redirect to session continuation URL
    Browser->>Partner: Request with launch parameters and partner credentials
    Partner->>Partner: Validate partner session and launch parameters
    Partner->>API: Create dashboard URL
    API-->>Partner: Post-authorization URL
    Partner-->>Browser: 307 Temporary Redirect
    Browser->>Junction: Open post-authorization URL
    Junction->>Junction: Configure session and route to feature
```

## Example endpoint

```python theme={null}
from typing import Annotated

import httpx
from fastapi import APIRouter, HTTPException, Query, Request
from pydantic import BaseModel, HttpUrl
from starlette.responses import RedirectResponse

router = APIRouter()


class CreateDashboardURLResponse(BaseModel):
    url: HttpUrl
    expires_in: int


@router.get("/junction-session-continuation")
async def handle_junction_session_continuation(
    request: Request,
    integration_member_id: Annotated[str, Query()],
    integration_team_id: Annotated[str | None, Query()] = None,
    team_id: Annotated[str | None, Query()] = None,
    modality: Annotated[str, Query()] = "feature_embed",
    feature: Annotated[str, Query()] = "order_creation",
    environment: Annotated[str, Query()] = "sandbox",
) -> RedirectResponse:
    session_user = await verify_valid_session_cookie(request)

    if session_user.integration_member_id != integration_member_id:
        raise HTTPException(401, "Invalid continuation request")

    if modality not in {"feature_embed", "link_out"}:
        raise HTTPException(400, "Invalid modality")

    if feature not in allowed_features_for_user(session_user):
        raise HTTPException(403, "Feature is not allowed")

    payload = {
        "integration_member_id": integration_member_id,
        "modality": modality,
        "feature": feature,
        "environment": environment,
    }

    if integration_team_id is not None:
        payload["integration_team_id"] = integration_team_id
    elif team_id is not None:
        payload["team_id"] = team_id

    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"https://api.management.junction.com/v1/org/{org_id}/ehr_integration/create_dashboard_url",
            headers={"X-Management-Key": management_key},
            json=payload,
        )
        response.raise_for_status()
        dashboard_url = CreateDashboardURLResponse.model_validate_json(response.text)

    return RedirectResponse(str(dashboard_url.url), status_code=307)
```

## Redirect status

Use `307 Temporary Redirect` so the browser follows the continuation result without treating it as a permanent redirect. This keeps future session recovery requests pointed at your session continuation URL.


# Continuous Query Overview
Source: https://docs.junction.com/continuous-query-overview

Automatically aggregate raw activity and biometric data from connected devices into structured datasets that update as new data arrives.

[Continuous Query](https://docs.junction.com/api-reference/horizon-ai/aggregation/using-continuous-query) automatically transforms raw activity and biometric data from 300+ connected devices into structured, aggregated datasets that update as new data arrives. Instead of building complex data pipelines or repeatedly polling APIs, you define your query once and Junction handles the rest—from data ingestion and aggregation to intelligent scheduling and delivery.

## How It Works

Continuous Query runs your Junction Sense Queries automatically across all users in your team. When new data arrives from connected devices—whether via cloud providers or [mobile SDKs](https://docs.junction.com/wearables/sdks/vital-health)—Junction intelligently schedules query re-evaluation and pushes any changes to your configured destinations.

### Key Capabilities

<Columns>
  <Column>
    <Card title="Automatic Evaluation" icon="bolt-auto">
      Queries run automatically on all existing and new users in your team, eliminating manual execution
    </Card>

    <Card title="Push Delivery" icon="box-check">
      Result changes are pushed to your webhook or ETL pipeline destinations—no polling required
    </Card>
  </Column>

  <Column>
    <Card title="Intelligent Scheduling" icon="timer">
      Junction monitors data connections and schedules queries in response to new data points or updates
    </Card>

    <Card title="Pull Access" icon="arrow-down-to-bracket">
      Query the latest result table through the [API](https://docs.junction.com/api-reference/horizon-ai/continuous-query/get-result-table) anytime
    </Card>
  </Column>
</Columns>

## Common Use Cases

<Accordion title="Health Monitoring & Reporting">
  Track key health metrics over time to power patient dashboards, clinical insights, or wellness reports.

  **Example: Daily Sleep Analysis**

  * Analyze sleep efficiency, scores, and chronotype for primary sleep sessions
  * Filter for long sleep periods to focus on nighttime rest
  * Monitor quality trends across consecutive nights
  * [View example →](https://docs.junction.com/api-reference/horizon-ai/aggregation/examples#daily-main-sleep-session-analysis)

  **Example: Weekly Activity Summaries**

  * Calculate average resting heart rate and active duration
  * Track maximum daily calorie burn and minimum step counts
  * Group activity data by week for trend analysis
  * [View example →](https://docs.junction.com/api-reference/horizon-ai/aggregation/examples#weekly-insights-into-users-activity)
</Accordion>

<Accordion title="Continuous Glucose Monitoring">
  Support diabetes management and metabolic health tracking with automated CGM data aggregation.

  **Example: First Glucose Reading of Each Day**

  * Capture fasting glucose values (first measurement per day)
  * Group by data source and provider for device comparison
  * Track morning glucose patterns over time
  * [View example →](https://docs.junction.com/api-reference/horizon-ai/aggregation/examples#first-glucose-measurement-of-the-day-grouped-by-source-type-and-provider)

  **Example: Daily Summaries of Metabolites**

  * Calculate mean glucose levels throughout the day
  * Combine with heart rate, HRV, and temperature data
  * Analyze multi-metric patterns by device source
  * [View example →](https://docs.junction.com/api-reference/horizon-ai/aggregation/examples#daily-aggregations-of-select-timeseries-datapoints-grouped-by-source-type-and-provider)
</Accordion>

<Accordion title="Fitness & Performance Tracking">
  Power adaptive training programs with continuously updated workout and activity metrics.

  **Example: Weekly Workout Statistics**

  * Track calorie expenditure ranges (min/max) across workouts
  * Monitor heart rate zone distribution for training intensity
  * Aggregate distance and active duration metrics
  * [View example →](https://docs.junction.com/api-reference/horizon-ai/aggregation/examples#weekly-insights-into-workout-statistics)

  **Use case highlights:**

  * Real-time metric tracking for goal progress
  * Device-specific analysis to handle data from multiple sources
  * Webhook integration for triggering adaptive training plans
  * Historical trend data for personalized coaching
</Accordion>

<Accordion title="Longitudinal Research Studies">
  Support research protocols requiring consistent, automated data collection across participants.

  **Use case highlights:**

  * Automatic data aggregation for all enrolled participants
  * Consistent time-window grouping (daily, weekly, monthly)
  * Event notifications when new data becomes available
  * Result tables compatible with research analysis tools
  * Source-level granularity for data quality assessment
</Accordion>

<Accordion title="Population Health Analytics">
  Analyze health trends across user populations for public health initiatives or employer wellness programs.

  **Use case highlights:**

  * Scalable aggregation across thousands of users
  * Standardized metrics despite diverse device ecosystems
  * Automated daily/weekly rollups for reporting dashboards
  * ETL pipeline integration for data warehouses
</Accordion>

## Supported Data Sources

Continuous Query works with the following health data resources:

* **Activity** - Steps, calories, distance, active duration, resting heart rate
* **Sleep** - Duration, stages, efficiency, scores, heart rate during sleep, sleep type filtering
* **Workout** - Exercise sessions, heart rate zones, distance, duration, calories
* **Body** - Weight, BMI, body fat percentage, temperature
* **Meal** - Nutritional intake, macros, timing
* **Time Series** - Continuous measurements including heart rate, glucose, HRV, steps, body temperature

Each query focuses on a single data resource, ensuring clean schemas and optimal performance. For multi-resource analysis, create separate queries and join results in your application layer.

[View complete data resource documentation →](https://docs.junction.com/wearables/providers/resources)

## Query Capabilities

### Time-Based Aggregation

Group data by day, week, month, or other time periods to create time-series datasets:

* Daily summaries for dashboards and trend visualization
* Weekly rollups for progress tracking
* Monthly aggregations for longitudinal analysis

### Flexible Metrics

Calculate meaningful insights using built-in [aggregation functions](https://docs.junction.com/api-reference/horizon-ai/aggregation/query-dsl/select-clause#aggregate-a-table-column):

* **Mean/Average** - Average sleep efficiency, resting heart rate, glucose levels
* **Sum** - Total steps, cumulative calories
* **Min/Max** - Lowest/highest values in a period
* **Standard Deviation** - Variability in metrics
* **Newest/Oldest** - Most recent or first value (useful for fasting glucose, chronotype)

### Multi-Dimensional Grouping

Organize data across multiple dimensions for richer analysis:

* Time periods (day, week, month)
* Data sources (provider, device type)
* Custom fields (workout type, sleep type)

[View complete grouping reference →](https://docs.junction.com/api-reference/horizon-ai/aggregation/query-dsl/column-expressions#table-column-expression)

### Source Prioritization

When users connect multiple devices, Junction's source prioritization ensures data quality:

* Configure team-level provider priorities
* Override priorities at the query level for experiments
* Optionally split results by data source for comparative analysis

### Filtering & Refinement

Use WHERE clauses to focus on specific data subsets:

* Filter for main sleep sessions (`type = 'long_sleep'`)
* Target specific workout types or activity levels
* Isolate data from particular device sources

## Getting Started

**Step 1: Define Your Query**

Use the [Query DSL](https://docs.junction.com/api-reference/horizon-ai/aggregation/query-dsl/) to specify what data to aggregate, how to group it, and which metrics to calculate.

**Step 2: Validate the Query**

Use the Junction Dashboard Query Editor to preview and validate the result table schema and catch any issues before creation.

**Step 3: Deploy Query**

Deploy your query using the [create endpoint](https://docs.junction.com/api-reference/horizon-ai/continuous-query/create) or by saving your query in the Junction Dashboard.

**Step 4: Configure Delivery**

Set up webhooks or ETL pipelines to receive result updates automatically.

[Jump to Getting Started Guide →](https://docs.junction.com/api-reference/horizon-ai/aggregation/using-continuous-query)

## Query API vs. Continuous Query

Both use the same Query DSL, making it easy to prototype with Query API and deploy to production with Continuous Query.

[Compare in detail →](https://docs.junction.com/api-reference/horizon-ai/aggregation/overview#query-api-and-continuous-query)

| *Feature*     | *Query API*                      | *Continuous Query*                    |
| :------------ | :------------------------------- | :------------------------------------ |
| **Execution** | On-demand, single request        | Automatic, continuous evaluation      |
| **Scope**     | Single user, point-in-time       | All team users, ongoing               |
| **Delivery**  | Synchronous API response         | Webhooks, ETL pipelines, API pull     |
| **Best For**  | Experimentation, ad-hoc analysis | Production workflows, monitoring      |
| **Use When**  | Testing queries, exploring data  | Building features, generating reports |

## What's Next?

[Manage Queries Using Junction Dashboard](/sense/managing-queries)

**View Example Queries**

* Copy-paste examples for sleep, activity, glucose, and workout queries
* [Browse examples →](https://docs.junction.com/api-reference/horizon-ai/aggregation/examples)

**Getting Started Guide**

* Step-by-step tutorial to create your first Junction Sense query
* [Get started →](https://docs.junction.com/api-reference/horizon-ai/continuous-query/getting-started)

**Query DSL Reference**

* Complete reference for building queries with available functions and constraints
* [Read documentation →](https://docs.junction.com/api-reference/horizon-ai/aggregation/query-dsl/)

**API Endpoints**

* Create, manage, and retrieve results from your Continuous Queries
* [View API reference →](https://docs.junction.com/api-reference/horizon-ai/continuous-query/create)


# continuous_query.result_table.changed
Source: https://docs.junction.com/event-catalog/continuous_query.result_table.changed

Webhook event Junction sends when a Continuous Query result table changes and new rows are available to fetch.



# daily.data.activity.created
Source: https://docs.junction.com/event-catalog/daily.data.activity.created

Webhook event Junction sends when a new activity summary entry is discovered from a connected provider.



# daily.data.activity.updated
Source: https://docs.junction.com/event-catalog/daily.data.activity.updated

Webhook event Junction sends when an existing activity summary entry is updated by a connected provider.



# daily.data.afib_burden.created
Source: https://docs.junction.com/event-catalog/daily.data.afib_burden.created

Webhook event Junction sends when a new atrial fibrillation burden entry is discovered from a connected provider.



# daily.data.afib_burden.updated
Source: https://docs.junction.com/event-catalog/daily.data.afib_burden.updated

Webhook event Junction sends when an existing atrial fibrillation burden entry is updated by a connected provider.



# daily.data.basal_body_temperature.created
Source: https://docs.junction.com/event-catalog/daily.data.basal_body_temperature.created

Webhook event Junction sends when a new basal body temperature entry is discovered from a connected provider.



# daily.data.basal_body_temperature.updated
Source: https://docs.junction.com/event-catalog/daily.data.basal_body_temperature.updated

Webhook event Junction sends when an existing basal body temperature entry is updated by a connected provider.



# daily.data.blood_oxygen.created
Source: https://docs.junction.com/event-catalog/daily.data.blood_oxygen.created

Webhook event Junction sends when a new blood oxygen (SpO2) entry is discovered from a connected provider.



# daily.data.blood_oxygen.updated
Source: https://docs.junction.com/event-catalog/daily.data.blood_oxygen.updated

Webhook event Junction sends when an existing blood oxygen (SpO2) entry is updated by a connected provider.



# daily.data.blood_pressure.created
Source: https://docs.junction.com/event-catalog/daily.data.blood_pressure.created

Webhook event Junction sends when a new blood pressure entry is discovered from a connected provider.



# daily.data.blood_pressure.updated
Source: https://docs.junction.com/event-catalog/daily.data.blood_pressure.updated

Webhook event Junction sends when an existing blood pressure entry is updated by a connected provider.



# daily.data.body.created
Source: https://docs.junction.com/event-catalog/daily.data.body.created

Webhook event Junction sends when a new body composition entry is discovered from a connected provider.



# daily.data.body.updated
Source: https://docs.junction.com/event-catalog/daily.data.body.updated

Webhook event Junction sends when an existing body composition entry is updated by a connected provider.



# daily.data.body_mass_index.created
Source: https://docs.junction.com/event-catalog/daily.data.body_mass_index.created

Webhook event Junction sends when a new body mass index entry is discovered from a connected provider.



# daily.data.body_mass_index.updated
Source: https://docs.junction.com/event-catalog/daily.data.body_mass_index.updated

Webhook event Junction sends when an existing body mass index entry is updated by a connected provider.



# daily.data.body_temperature.created
Source: https://docs.junction.com/event-catalog/daily.data.body_temperature.created

Webhook event Junction sends when a new body temperature entry is discovered from a connected provider.



# daily.data.body_temperature.updated
Source: https://docs.junction.com/event-catalog/daily.data.body_temperature.updated

Webhook event Junction sends when an existing body temperature entry is updated by a connected provider.



# daily.data.body_temperature_delta.created
Source: https://docs.junction.com/event-catalog/daily.data.body_temperature_delta.created

Webhook event Junction sends when a new wrist temperature delta entry is discovered from a connected provider.



# daily.data.body_temperature_delta.updated
Source: https://docs.junction.com/event-catalog/daily.data.body_temperature_delta.updated

Webhook event Junction sends when an existing wrist temperature delta entry is updated by a connected provider.



# daily.data.caffeine.created
Source: https://docs.junction.com/event-catalog/daily.data.caffeine.created

Webhook event Junction sends when a new caffeine intake entry is discovered from a connected provider.



# daily.data.caffeine.updated
Source: https://docs.junction.com/event-catalog/daily.data.caffeine.updated

Webhook event Junction sends when an existing caffeine intake entry is updated by a connected provider.



# daily.data.calories_active.created
Source: https://docs.junction.com/event-catalog/daily.data.calories_active.created

Webhook event Junction sends when a new active calories burned entry is discovered from a connected provider.



# daily.data.calories_active.updated
Source: https://docs.junction.com/event-catalog/daily.data.calories_active.updated

Webhook event Junction sends when an existing active calories burned entry is updated by a connected provider.



# daily.data.calories_basal.created
Source: https://docs.junction.com/event-catalog/daily.data.calories_basal.created

Webhook event Junction sends when a new basal (resting) calories entry is discovered from a connected provider.



# daily.data.calories_basal.updated
Source: https://docs.junction.com/event-catalog/daily.data.calories_basal.updated

Webhook event Junction sends when an existing basal (resting) calories entry is updated by a connected provider.



# daily.data.carbohydrates.created
Source: https://docs.junction.com/event-catalog/daily.data.carbohydrates.created

Webhook event Junction sends when a new carbohydrate intake entry is discovered from a connected provider.



# daily.data.carbohydrates.updated
Source: https://docs.junction.com/event-catalog/daily.data.carbohydrates.updated

Webhook event Junction sends when an existing carbohydrate intake entry is updated by a connected provider.



# daily.data.daylight_exposure.created
Source: https://docs.junction.com/event-catalog/daily.data.daylight_exposure.created

Webhook event Junction sends when a new daylight exposure entry is discovered from a connected provider.



# daily.data.daylight_exposure.updated
Source: https://docs.junction.com/event-catalog/daily.data.daylight_exposure.updated

Webhook event Junction sends when an existing daylight exposure entry is updated by a connected provider.



# daily.data.distance.created
Source: https://docs.junction.com/event-catalog/daily.data.distance.created

Webhook event Junction sends when a new distance entry is discovered from a connected provider.



# daily.data.distance.updated
Source: https://docs.junction.com/event-catalog/daily.data.distance.updated

Webhook event Junction sends when an existing distance entry is updated by a connected provider.



# daily.data.electrocardiogram.created
Source: https://docs.junction.com/event-catalog/daily.data.electrocardiogram.created

Webhook event Junction sends when a new electrocardiogram (ECG) entry is discovered from a connected provider.



# daily.data.electrocardiogram.updated
Source: https://docs.junction.com/event-catalog/daily.data.electrocardiogram.updated

Webhook event Junction sends when an existing electrocardiogram (ECG) entry is updated by a connected provider.



# daily.data.electrocardiogram_voltage.created
Source: https://docs.junction.com/event-catalog/daily.data.electrocardiogram_voltage.created

Webhook event Junction sends when a new ECG voltage timeseries entry is discovered from a connected provider.



# daily.data.electrocardiogram_voltage.updated
Source: https://docs.junction.com/event-catalog/daily.data.electrocardiogram_voltage.updated

Webhook event Junction sends when an existing ECG voltage timeseries entry is updated by a connected provider.



# daily.data.fall.created
Source: https://docs.junction.com/event-catalog/daily.data.fall.created

Webhook event Junction sends when a new fall detection entry is discovered from a connected provider.



# daily.data.fall.updated
Source: https://docs.junction.com/event-catalog/daily.data.fall.updated

Webhook event Junction sends when an existing fall detection entry is updated by a connected provider.



# daily.data.fat.created
Source: https://docs.junction.com/event-catalog/daily.data.fat.created

Webhook event Junction sends when a new fat intake entry is discovered from a connected provider.



# daily.data.fat.updated
Source: https://docs.junction.com/event-catalog/daily.data.fat.updated

Webhook event Junction sends when an existing fat intake entry is updated by a connected provider.



# daily.data.floors_climbed.created
Source: https://docs.junction.com/event-catalog/daily.data.floors_climbed.created

Webhook event Junction sends when a new floors climbed entry is discovered from a connected provider.



# daily.data.floors_climbed.updated
Source: https://docs.junction.com/event-catalog/daily.data.floors_climbed.updated

Webhook event Junction sends when an existing floors climbed entry is updated by a connected provider.



# daily.data.forced_expiratory_volume_1.created
Source: https://docs.junction.com/event-catalog/daily.data.forced_expiratory_volume_1.created

Webhook event Junction sends when a new forced expiratory volume (FEV1) entry is discovered from a connected provider.



# daily.data.forced_expiratory_volume_1.updated
Source: https://docs.junction.com/event-catalog/daily.data.forced_expiratory_volume_1.updated

Webhook event Junction sends when an existing forced expiratory volume (FEV1) entry is updated by a connected provider.



# daily.data.forced_vital_capacity.created
Source: https://docs.junction.com/event-catalog/daily.data.forced_vital_capacity.created

Webhook event Junction sends when a new forced vital capacity entry is discovered from a connected provider.



# daily.data.forced_vital_capacity.updated
Source: https://docs.junction.com/event-catalog/daily.data.forced_vital_capacity.updated

Webhook event Junction sends when an existing forced vital capacity entry is updated by a connected provider.



# daily.data.glucose.created
Source: https://docs.junction.com/event-catalog/daily.data.glucose.created

Webhook event Junction sends when a new glucose entry is discovered from a connected provider.



# daily.data.glucose.updated
Source: https://docs.junction.com/event-catalog/daily.data.glucose.updated

Webhook event Junction sends when an existing glucose entry is updated by a connected provider.



# daily.data.handwashing.created
Source: https://docs.junction.com/event-catalog/daily.data.handwashing.created

Webhook event Junction sends when a new handwashing entry is discovered from a connected provider.



# daily.data.handwashing.updated
Source: https://docs.junction.com/event-catalog/daily.data.handwashing.updated

Webhook event Junction sends when an existing handwashing entry is updated by a connected provider.



# daily.data.heart_rate_alert.created
Source: https://docs.junction.com/event-catalog/daily.data.heart_rate_alert.created

Webhook event Junction sends when a new heart rate alert entry is discovered from a connected provider.



# daily.data.heart_rate_alert.updated
Source: https://docs.junction.com/event-catalog/daily.data.heart_rate_alert.updated

Webhook event Junction sends when an existing heart rate alert entry is updated by a connected provider.



# daily.data.heart_rate_recovery_one_minute.created
Source: https://docs.junction.com/event-catalog/daily.data.heart_rate_recovery_one_minute.created

Webhook event Junction sends when a new one-minute heart rate recovery entry is discovered from a connected provider.



# daily.data.heart_rate_recovery_one_minute.updated
Source: https://docs.junction.com/event-catalog/daily.data.heart_rate_recovery_one_minute.updated

Webhook event Junction sends when an existing one-minute heart rate recovery entry is updated by a connected provider.



# daily.data.heartrate.created
Source: https://docs.junction.com/event-catalog/daily.data.heartrate.created

Webhook event Junction sends when a new heart rate entry is discovered from a connected provider.



# daily.data.heartrate.updated
Source: https://docs.junction.com/event-catalog/daily.data.heartrate.updated

Webhook event Junction sends when an existing heart rate entry is updated by a connected provider.



# daily.data.hrv.created
Source: https://docs.junction.com/event-catalog/daily.data.hrv.created

Webhook event Junction sends when a new heart rate variability (HRV) entry is discovered from a connected provider.



# daily.data.hrv.updated
Source: https://docs.junction.com/event-catalog/daily.data.hrv.updated

Webhook event Junction sends when an existing heart rate variability (HRV) entry is updated by a connected provider.



# daily.data.inhaler_usage.created
Source: https://docs.junction.com/event-catalog/daily.data.inhaler_usage.created

Webhook event Junction sends when a new inhaler usage entry is discovered from a connected provider.



# daily.data.inhaler_usage.updated
Source: https://docs.junction.com/event-catalog/daily.data.inhaler_usage.updated

Webhook event Junction sends when an existing inhaler usage entry is updated by a connected provider.



# daily.data.insulin_injection.created
Source: https://docs.junction.com/event-catalog/daily.data.insulin_injection.created

Webhook event Junction sends when a new insulin injection entry is discovered from a connected provider.



# daily.data.insulin_injection.updated
Source: https://docs.junction.com/event-catalog/daily.data.insulin_injection.updated

Webhook event Junction sends when an existing insulin injection entry is updated by a connected provider.



# daily.data.lean_body_mass.created
Source: https://docs.junction.com/event-catalog/daily.data.lean_body_mass.created

Webhook event Junction sends when a new lean body mass entry is discovered from a connected provider.



# daily.data.lean_body_mass.updated
Source: https://docs.junction.com/event-catalog/daily.data.lean_body_mass.updated

Webhook event Junction sends when an existing lean body mass entry is updated by a connected provider.



# daily.data.meal.created
Source: https://docs.junction.com/event-catalog/daily.data.meal.created

Webhook event Junction sends when a new meal entry is discovered from a connected provider.



# daily.data.meal.updated
Source: https://docs.junction.com/event-catalog/daily.data.meal.updated

Webhook event Junction sends when an existing meal entry is updated by a connected provider.



# daily.data.menstrual_cycle.created
Source: https://docs.junction.com/event-catalog/daily.data.menstrual_cycle.created

Webhook event Junction sends when a new menstrual cycle entry is discovered from a connected provider.



# daily.data.menstrual_cycle.updated
Source: https://docs.junction.com/event-catalog/daily.data.menstrual_cycle.updated

Webhook event Junction sends when an existing menstrual cycle entry is updated by a connected provider.



# daily.data.mindfulness_minutes.created
Source: https://docs.junction.com/event-catalog/daily.data.mindfulness_minutes.created

Webhook event Junction sends when a new mindfulness minutes entry is discovered from a connected provider.



# daily.data.mindfulness_minutes.updated
Source: https://docs.junction.com/event-catalog/daily.data.mindfulness_minutes.updated

Webhook event Junction sends when an existing mindfulness minutes entry is updated by a connected provider.



# daily.data.note.created
Source: https://docs.junction.com/event-catalog/daily.data.note.created

Webhook event Junction sends when a new user note entry is discovered from a connected provider.



# daily.data.note.updated
Source: https://docs.junction.com/event-catalog/daily.data.note.updated

Webhook event Junction sends when an existing user note entry is updated by a connected provider.



# daily.data.peak_expiratory_flow_rate.created
Source: https://docs.junction.com/event-catalog/daily.data.peak_expiratory_flow_rate.created

Webhook event Junction sends when a new peak expiratory flow rate entry is discovered from a connected provider.



# daily.data.peak_expiratory_flow_rate.updated
Source: https://docs.junction.com/event-catalog/daily.data.peak_expiratory_flow_rate.updated

Webhook event Junction sends when an existing peak expiratory flow rate entry is updated by a connected provider.



# daily.data.profile.created
Source: https://docs.junction.com/event-catalog/daily.data.profile.created

Webhook event Junction sends when a new user profile entry is discovered from a connected provider.



# daily.data.profile.updated
Source: https://docs.junction.com/event-catalog/daily.data.profile.updated

Webhook event Junction sends when an existing user profile entry is updated by a connected provider.



# daily.data.respiratory_rate.created
Source: https://docs.junction.com/event-catalog/daily.data.respiratory_rate.created

Webhook event Junction sends when a new respiratory rate entry is discovered from a connected provider.



# daily.data.respiratory_rate.updated
Source: https://docs.junction.com/event-catalog/daily.data.respiratory_rate.updated

Webhook event Junction sends when an existing respiratory rate entry is updated by a connected provider.



# daily.data.sleep.created
Source: https://docs.junction.com/event-catalog/daily.data.sleep.created

Webhook event Junction sends when a new sleep entry is discovered from a connected provider.



# daily.data.sleep.updated
Source: https://docs.junction.com/event-catalog/daily.data.sleep.updated

Webhook event Junction sends when an existing sleep entry is updated by a connected provider.



# daily.data.sleep_apnea_alert.created
Source: https://docs.junction.com/event-catalog/daily.data.sleep_apnea_alert.created

Webhook event Junction sends when a new sleep apnea alert entry is discovered from a connected provider.



# daily.data.sleep_apnea_alert.updated
Source: https://docs.junction.com/event-catalog/daily.data.sleep_apnea_alert.updated

Webhook event Junction sends when an existing sleep apnea alert entry is updated by a connected provider.



# daily.data.sleep_breathing_disturbance.created
Source: https://docs.junction.com/event-catalog/daily.data.sleep_breathing_disturbance.created

Webhook event Junction sends when a new sleep breathing disturbance entry is discovered from a connected provider.



# daily.data.sleep_breathing_disturbance.updated
Source: https://docs.junction.com/event-catalog/daily.data.sleep_breathing_disturbance.updated

Webhook event Junction sends when an existing sleep breathing disturbance entry is updated by a connected provider.



# daily.data.sleep_cycle.created
Source: https://docs.junction.com/event-catalog/daily.data.sleep_cycle.created

Webhook event Junction sends when a new sleep cycle entry is discovered from a connected provider.



# daily.data.sleep_cycle.updated
Source: https://docs.junction.com/event-catalog/daily.data.sleep_cycle.updated

Webhook event Junction sends when an existing sleep cycle entry is updated by a connected provider.



# daily.data.stand_duration.created
Source: https://docs.junction.com/event-catalog/daily.data.stand_duration.created

Webhook event Junction sends when a new stand duration entry is discovered from a connected provider.



# daily.data.stand_duration.updated
Source: https://docs.junction.com/event-catalog/daily.data.stand_duration.updated

Webhook event Junction sends when an existing stand duration entry is updated by a connected provider.



# daily.data.stand_hour.created
Source: https://docs.junction.com/event-catalog/daily.data.stand_hour.created

Webhook event Junction sends when a new stand hour entry is discovered from a connected provider.



# daily.data.stand_hour.updated
Source: https://docs.junction.com/event-catalog/daily.data.stand_hour.updated

Webhook event Junction sends when an existing stand hour entry is updated by a connected provider.



# daily.data.steps.created
Source: https://docs.junction.com/event-catalog/daily.data.steps.created

Webhook event Junction sends when a new step count entry is discovered from a connected provider.



# daily.data.steps.updated
Source: https://docs.junction.com/event-catalog/daily.data.steps.updated

Webhook event Junction sends when an existing step count entry is updated by a connected provider.



# daily.data.stress_level.created
Source: https://docs.junction.com/event-catalog/daily.data.stress_level.created

Webhook event Junction sends when a new stress level entry is discovered from a connected provider.



# daily.data.stress_level.updated
Source: https://docs.junction.com/event-catalog/daily.data.stress_level.updated

Webhook event Junction sends when an existing stress level entry is updated by a connected provider.



# daily.data.uv_exposure.created
Source: https://docs.junction.com/event-catalog/daily.data.uv_exposure.created

Webhook event Junction sends when a new UV exposure entry is discovered from a connected provider.



# daily.data.uv_exposure.updated
Source: https://docs.junction.com/event-catalog/daily.data.uv_exposure.updated

Webhook event Junction sends when an existing UV exposure entry is updated by a connected provider.



# daily.data.vo2_max.created
Source: https://docs.junction.com/event-catalog/daily.data.vo2_max.created

Webhook event Junction sends when a new VO2 max entry is discovered from a connected provider.



# daily.data.vo2_max.updated
Source: https://docs.junction.com/event-catalog/daily.data.vo2_max.updated

Webhook event Junction sends when an existing VO2 max entry is updated by a connected provider.



# daily.data.waist_circumference.created
Source: https://docs.junction.com/event-catalog/daily.data.waist_circumference.created

Webhook event Junction sends when a new waist circumference entry is discovered from a connected provider.



# daily.data.waist_circumference.updated
Source: https://docs.junction.com/event-catalog/daily.data.waist_circumference.updated

Webhook event Junction sends when an existing waist circumference entry is updated by a connected provider.



# daily.data.water.created
Source: https://docs.junction.com/event-catalog/daily.data.water.created

Webhook event Junction sends when a new water intake entry is discovered from a connected provider.



# daily.data.water.updated
Source: https://docs.junction.com/event-catalog/daily.data.water.updated

Webhook event Junction sends when an existing water intake entry is updated by a connected provider.



# daily.data.weight.created
Source: https://docs.junction.com/event-catalog/daily.data.weight.created

Webhook event Junction sends when a new body weight entry is discovered from a connected provider.



# daily.data.weight.updated
Source: https://docs.junction.com/event-catalog/daily.data.weight.updated

Webhook event Junction sends when an existing body weight entry is updated by a connected provider.



# daily.data.wheelchair_push.created
Source: https://docs.junction.com/event-catalog/daily.data.wheelchair_push.created

Webhook event Junction sends when a new wheelchair push entry is discovered from a connected provider.



# daily.data.wheelchair_push.updated
Source: https://docs.junction.com/event-catalog/daily.data.wheelchair_push.updated

Webhook event Junction sends when an existing wheelchair push entry is updated by a connected provider.



# daily.data.workout_distance.created
Source: https://docs.junction.com/event-catalog/daily.data.workout_distance.created

Webhook event Junction sends when a new workout distance timeseries entry is discovered from a connected provider.



# daily.data.workout_distance.updated
Source: https://docs.junction.com/event-catalog/daily.data.workout_distance.updated

Webhook event Junction sends when an existing workout distance timeseries entry is updated by a connected provider.



# daily.data.workout_duration.created
Source: https://docs.junction.com/event-catalog/daily.data.workout_duration.created

Webhook event Junction sends when a new workout duration timeseries entry is discovered from a connected provider.



# daily.data.workout_duration.updated
Source: https://docs.junction.com/event-catalog/daily.data.workout_duration.updated

Webhook event Junction sends when an existing workout duration timeseries entry is updated by a connected provider.



# daily.data.workout_stream.created
Source: https://docs.junction.com/event-catalog/daily.data.workout_stream.created

Webhook event Junction sends when a new workout stream timeseries entry is discovered from a connected provider.



# daily.data.workout_stream.updated
Source: https://docs.junction.com/event-catalog/daily.data.workout_stream.updated

Webhook event Junction sends when an existing workout stream timeseries entry is updated by a connected provider.



# daily.data.workout_swimming_stroke.created
Source: https://docs.junction.com/event-catalog/daily.data.workout_swimming_stroke.created

Webhook event Junction sends when a new swimming stroke timeseries entry is discovered from a connected provider.



# daily.data.workout_swimming_stroke.updated
Source: https://docs.junction.com/event-catalog/daily.data.workout_swimming_stroke.updated

Webhook event Junction sends when an existing swimming stroke timeseries entry is updated by a connected provider.



# daily.data.workouts.created
Source: https://docs.junction.com/event-catalog/daily.data.workouts.created

Webhook event Junction sends when a new workout entry is discovered from a connected provider.



# daily.data.workouts.updated
Source: https://docs.junction.com/event-catalog/daily.data.workouts.updated

Webhook event Junction sends when an existing workout entry is updated by a connected provider.



# historical.data.activity.created
Source: https://docs.junction.com/event-catalog/historical.data.activity.created

Webhook event Junction sends when the historical data backfill for activity summary completes for a user's provider connection.



# historical.data.afib_burden.created
Source: https://docs.junction.com/event-catalog/historical.data.afib_burden.created

Webhook event Junction sends when the historical data backfill for atrial fibrillation burden completes for a user's provider connection.



# historical.data.basal_body_temperature.created
Source: https://docs.junction.com/event-catalog/historical.data.basal_body_temperature.created

Webhook event Junction sends when the historical data backfill for basal body temperature completes for a user's provider connection.



# historical.data.blood_oxygen.created
Source: https://docs.junction.com/event-catalog/historical.data.blood_oxygen.created

Webhook event Junction sends when the historical data backfill for blood oxygen (SpO2) completes for a user's provider connection.



# historical.data.blood_pressure.created
Source: https://docs.junction.com/event-catalog/historical.data.blood_pressure.created

Webhook event Junction sends when the historical data backfill for blood pressure completes for a user's provider connection.



# historical.data.body.created
Source: https://docs.junction.com/event-catalog/historical.data.body.created

Webhook event Junction sends when the historical data backfill for body composition completes for a user's provider connection.



# historical.data.body_mass_index.created
Source: https://docs.junction.com/event-catalog/historical.data.body_mass_index.created

Webhook event Junction sends when the historical data backfill for body mass index completes for a user's provider connection.



# historical.data.body_temperature.created
Source: https://docs.junction.com/event-catalog/historical.data.body_temperature.created

Webhook event Junction sends when the historical data backfill for body temperature completes for a user's provider connection.



# historical.data.body_temperature_delta.created
Source: https://docs.junction.com/event-catalog/historical.data.body_temperature_delta.created

Webhook event Junction sends when the historical data backfill for wrist temperature delta completes for a user's provider connection.



# historical.data.caffeine.created
Source: https://docs.junction.com/event-catalog/historical.data.caffeine.created

Webhook event Junction sends when the historical data backfill for caffeine intake completes for a user's provider connection.



# historical.data.calories_active.created
Source: https://docs.junction.com/event-catalog/historical.data.calories_active.created

Webhook event Junction sends when the historical data backfill for active calories burned completes for a user's provider connection.



# historical.data.calories_basal.created
Source: https://docs.junction.com/event-catalog/historical.data.calories_basal.created

Webhook event Junction sends when the historical data backfill for basal (resting) calories completes for a user's provider connection.



# historical.data.carbohydrates.created
Source: https://docs.junction.com/event-catalog/historical.data.carbohydrates.created

Webhook event Junction sends when the historical data backfill for carbohydrate intake completes for a user's provider connection.



# historical.data.daylight_exposure.created
Source: https://docs.junction.com/event-catalog/historical.data.daylight_exposure.created

Webhook event Junction sends when the historical data backfill for daylight exposure completes for a user's provider connection.



# historical.data.distance.created
Source: https://docs.junction.com/event-catalog/historical.data.distance.created

Webhook event Junction sends when the historical data backfill for distance completes for a user's provider connection.



# historical.data.electrocardiogram.created
Source: https://docs.junction.com/event-catalog/historical.data.electrocardiogram.created

Webhook event Junction sends when the historical data backfill for electrocardiogram (ECG) completes for a user's provider connection.



# historical.data.electrocardiogram_voltage.created
Source: https://docs.junction.com/event-catalog/historical.data.electrocardiogram_voltage.created

Webhook event Junction sends when the historical data backfill for ECG voltage timeseries completes for a user's provider connection.



# historical.data.fall.created
Source: https://docs.junction.com/event-catalog/historical.data.fall.created

Webhook event Junction sends when the historical data backfill for fall detection completes for a user's provider connection.



# historical.data.fat.created
Source: https://docs.junction.com/event-catalog/historical.data.fat.created

Webhook event Junction sends when the historical data backfill for fat intake completes for a user's provider connection.



# historical.data.floors_climbed.created
Source: https://docs.junction.com/event-catalog/historical.data.floors_climbed.created

Webhook event Junction sends when the historical data backfill for floors climbed completes for a user's provider connection.



# historical.data.forced_expiratory_volume_1.created
Source: https://docs.junction.com/event-catalog/historical.data.forced_expiratory_volume_1.created

Webhook event Junction sends when the historical data backfill for forced expiratory volume (FEV1) completes for a user's provider connection.



# historical.data.forced_vital_capacity.created
Source: https://docs.junction.com/event-catalog/historical.data.forced_vital_capacity.created

Webhook event Junction sends when the historical data backfill for forced vital capacity completes for a user's provider connection.



# historical.data.glucose.created
Source: https://docs.junction.com/event-catalog/historical.data.glucose.created

Webhook event Junction sends when the historical data backfill for glucose completes for a user's provider connection.



# historical.data.handwashing.created
Source: https://docs.junction.com/event-catalog/historical.data.handwashing.created

Webhook event Junction sends when the historical data backfill for handwashing completes for a user's provider connection.



# historical.data.heart_rate_alert.created
Source: https://docs.junction.com/event-catalog/historical.data.heart_rate_alert.created

Webhook event Junction sends when the historical data backfill for heart rate alert completes for a user's provider connection.



# historical.data.heart_rate_recovery_one_minute.created
Source: https://docs.junction.com/event-catalog/historical.data.heart_rate_recovery_one_minute.created

Webhook event Junction sends when the historical data backfill for one-minute heart rate recovery completes for a user's provider connection.



# historical.data.heartrate.created
Source: https://docs.junction.com/event-catalog/historical.data.heartrate.created

Webhook event Junction sends when the historical data backfill for heart rate completes for a user's provider connection.



# historical.data.hrv.created
Source: https://docs.junction.com/event-catalog/historical.data.hrv.created

Webhook event Junction sends when the historical data backfill for heart rate variability (HRV) completes for a user's provider connection.



# historical.data.inhaler_usage.created
Source: https://docs.junction.com/event-catalog/historical.data.inhaler_usage.created

Webhook event Junction sends when the historical data backfill for inhaler usage completes for a user's provider connection.



# historical.data.insulin_injection.created
Source: https://docs.junction.com/event-catalog/historical.data.insulin_injection.created

Webhook event Junction sends when the historical data backfill for insulin injection completes for a user's provider connection.



# historical.data.lean_body_mass.created
Source: https://docs.junction.com/event-catalog/historical.data.lean_body_mass.created

Webhook event Junction sends when the historical data backfill for lean body mass completes for a user's provider connection.



# historical.data.meal.created
Source: https://docs.junction.com/event-catalog/historical.data.meal.created

Webhook event Junction sends when the historical data backfill for meal completes for a user's provider connection.



# historical.data.menstrual_cycle.created
Source: https://docs.junction.com/event-catalog/historical.data.menstrual_cycle.created

Webhook event Junction sends when the historical data backfill for menstrual cycle completes for a user's provider connection.



# historical.data.mindfulness_minutes.created
Source: https://docs.junction.com/event-catalog/historical.data.mindfulness_minutes.created

Webhook event Junction sends when the historical data backfill for mindfulness minutes completes for a user's provider connection.



# historical.data.note.created
Source: https://docs.junction.com/event-catalog/historical.data.note.created

Webhook event Junction sends when the historical data backfill for user note completes for a user's provider connection.



# historical.data.peak_expiratory_flow_rate.created
Source: https://docs.junction.com/event-catalog/historical.data.peak_expiratory_flow_rate.created

Webhook event Junction sends when the historical data backfill for peak expiratory flow rate completes for a user's provider connection.



# historical.data.profile.created
Source: https://docs.junction.com/event-catalog/historical.data.profile.created

Webhook event Junction sends when the historical data backfill for user profile completes for a user's provider connection.



# historical.data.respiratory_rate.created
Source: https://docs.junction.com/event-catalog/historical.data.respiratory_rate.created

Webhook event Junction sends when the historical data backfill for respiratory rate completes for a user's provider connection.



# historical.data.sleep.created
Source: https://docs.junction.com/event-catalog/historical.data.sleep.created

Webhook event Junction sends when the historical data backfill for sleep completes for a user's provider connection.



# historical.data.sleep_apnea_alert.created
Source: https://docs.junction.com/event-catalog/historical.data.sleep_apnea_alert.created

Webhook event Junction sends when the historical data backfill for sleep apnea alert completes for a user's provider connection.



# historical.data.sleep_breathing_disturbance.created
Source: https://docs.junction.com/event-catalog/historical.data.sleep_breathing_disturbance.created

Webhook event Junction sends when the historical data backfill for sleep breathing disturbance completes for a user's provider connection.



# historical.data.sleep_cycle.created
Source: https://docs.junction.com/event-catalog/historical.data.sleep_cycle.created

Webhook event Junction sends when the historical data backfill for sleep cycle completes for a user's provider connection.



# historical.data.stand_duration.created
Source: https://docs.junction.com/event-catalog/historical.data.stand_duration.created

Webhook event Junction sends when the historical data backfill for stand duration completes for a user's provider connection.



# historical.data.stand_hour.created
Source: https://docs.junction.com/event-catalog/historical.data.stand_hour.created

Webhook event Junction sends when the historical data backfill for stand hour completes for a user's provider connection.



# historical.data.steps.created
Source: https://docs.junction.com/event-catalog/historical.data.steps.created

Webhook event Junction sends when the historical data backfill for step count completes for a user's provider connection.



# historical.data.stress_level.created
Source: https://docs.junction.com/event-catalog/historical.data.stress_level.created

Webhook event Junction sends when the historical data backfill for stress level completes for a user's provider connection.



# historical.data.uv_exposure.created
Source: https://docs.junction.com/event-catalog/historical.data.uv_exposure.created

Webhook event Junction sends when the historical data backfill for UV exposure completes for a user's provider connection.



# historical.data.vo2_max.created
Source: https://docs.junction.com/event-catalog/historical.data.vo2_max.created

Webhook event Junction sends when the historical data backfill for VO2 max completes for a user's provider connection.



# historical.data.waist_circumference.created
Source: https://docs.junction.com/event-catalog/historical.data.waist_circumference.created

Webhook event Junction sends when the historical data backfill for waist circumference completes for a user's provider connection.



# historical.data.water.created
Source: https://docs.junction.com/event-catalog/historical.data.water.created

Webhook event Junction sends when the historical data backfill for water intake completes for a user's provider connection.



# historical.data.weight.created
Source: https://docs.junction.com/event-catalog/historical.data.weight.created

Webhook event Junction sends when the historical data backfill for body weight completes for a user's provider connection.



# historical.data.wheelchair_push.created
Source: https://docs.junction.com/event-catalog/historical.data.wheelchair_push.created

Webhook event Junction sends when the historical data backfill for wheelchair push completes for a user's provider connection.



# historical.data.workout_distance.created
Source: https://docs.junction.com/event-catalog/historical.data.workout_distance.created

Webhook event Junction sends when the historical data backfill for workout distance timeseries completes for a user's provider connection.



# historical.data.workout_duration.created
Source: https://docs.junction.com/event-catalog/historical.data.workout_duration.created

Webhook event Junction sends when the historical data backfill for workout duration timeseries completes for a user's provider connection.



# historical.data.workout_stream.created
Source: https://docs.junction.com/event-catalog/historical.data.workout_stream.created

Webhook event Junction sends when the historical data backfill for workout stream timeseries completes for a user's provider connection.



# historical.data.workout_swimming_stroke.created
Source: https://docs.junction.com/event-catalog/historical.data.workout_swimming_stroke.created

Webhook event Junction sends when the historical data backfill for swimming stroke timeseries completes for a user's provider connection.



# historical.data.workouts.created
Source: https://docs.junction.com/event-catalog/historical.data.workouts.created

Webhook event Junction sends when the historical data backfill for workout completes for a user's provider connection.



# lab_report.parsing_job.created
Source: https://docs.junction.com/event-catalog/lab_report.parsing_job.created

Webhook event Junction sends when a new lab report PDF parsing job is created to extract structured results from an uploaded document.



# lab_report.parsing_job.updated
Source: https://docs.junction.com/event-catalog/lab_report.parsing_job.updated

Webhook event Junction sends when a lab report parsing job changes status, for example when parsing completes or fails.



# labtest.appointment.created
Source: https://docs.junction.com/event-catalog/labtest.appointment.created

Webhook event Junction sends when a phlebotomy or at-home sample collection appointment is booked for a lab test order.



# labtest.appointment.updated
Source: https://docs.junction.com/event-catalog/labtest.appointment.updated

Webhook event Junction sends when a lab test appointment changes, such as a reschedule, cancellation, or status update.



# labtest.match_review.created
Source: https://docs.junction.com/event-catalog/labtest.match_review.created

Webhook event Junction sends when a new patient match review is created for a lab result awaiting identity confirmation.



# labtest.match_review.updated
Source: https://docs.junction.com/event-catalog/labtest.match_review.updated

Webhook event Junction sends when a lab test patient match review is updated, for example when an operator resolves the review.



# labtest.order.created
Source: https://docs.junction.com/event-catalog/labtest.order.created

Webhook event Junction sends when a new lab test order is placed for a user through the Junction Lab Testing API.



# labtest.order.updated
Source: https://docs.junction.com/event-catalog/labtest.order.updated

Webhook event Junction sends when a lab test order changes status, such as when it is collected, received, or completed.



# labtest.result.critical
Source: https://docs.junction.com/event-catalog/labtest.result.critical

Webhook event Junction sends when a lab result contains a value flagged as critical and requiring immediate attention.



# provider.connection.created
Source: https://docs.junction.com/event-catalog/provider.connection.created

Webhook event Junction sends when a user successfully connects a wearable or health provider through Junction Link.



# provider.connection.error
Source: https://docs.junction.com/event-catalog/provider.connection.error

Webhook event Junction sends when a provider connection enters an error state, such as when the user revokes access or credentials expire.



# provider.device.created
Source: https://docs.junction.com/event-catalog/provider.device.created

Webhook event Junction sends when a new device is registered under a user's provider connection.



# provider.device.updated
Source: https://docs.junction.com/event-catalog/provider.device.updated

Webhook event Junction sends when a device registered under a user's provider connection is updated.



# Getting Support
Source: https://docs.junction.com/home/getting-support

Contact Junction support via email or Slack, and learn what information to include when reporting issues for faster resolution.

## Support channels

When you encounter an issue which you cannot figure out, or an unexpected API error, you are
welcome to get in touch with Junction support through these channels:

* Email Support ([help@junction.com](mailto:help@junction.com))
* Junction Slack community
* Your dedicated Slack support channel (for Scale plan customers)

We appreciate as much context as possible when you raise an issue. This helps us understand your
issue, enabling a quicker investigation turnaround.

## I have an issue with...

### Anything in general

| Item                     | Remarks                                    |
| ------------------------ | ------------------------------------------ |
| Your Junction Team ID    | -                                          |
| Your Junction region     | US or EU                                   |
| The Junction User ID     | ...if the issue occurs on a specific user. |
| The Junction environment | Sandbox, Production or both                |

### Junction Mobile SDK

| Item                  | Remarks                                 |
| --------------------- | --------------------------------------- |
| Junction SDK Platform | Native, React Native, Flutter           |
| Junction SDK Version  | -                                       |
| Device OS             | Android or iOS                          |
| Device OS Version     | -                                       |
| Auth Scheme           | Junction Sign-In Token, or Team API Key |

### A Junction API endpoint

If your system has distributed tracing configured that is compliant with W3C Trace Context (e.g., OpenTelemetry tracing),
you can provide us with the Trace ID of the operation **in your system**.

A Trace ID looks like this: `0af7651916cd43dd8448eb211c80319c`.

This helps us locate the concerned API request and related contexts.

<Note>
  This works only if your distributed tracing setup would propagate Trace Context to your outbound HTTP requests — more
  specifically injecting the standards-based `traceparent` header into the requests.
</Note>

### A wearables provider connection

Before reporting an issue, we encourage you to try out the following steps:

<Steps>
  <Step title="Check the resource availability" icon="circle-question">
    The [Get User Connections](/api-reference/user/get-users-connected-providers) endpoint
    reports the resource availability of all the connections. More specifically, it tells you:

    1. What resources are available for a given user connection.
    2. Why each individual resource is available or unavailable, in terms of provider API access scopes.
  </Step>

  <Step title="Issues with historical pulls?" icon="clock-rotate-left">
    If you are having issues with historical data specifically, the [Historical Pull Introspection](/api-reference/data/introspection/historical-pulls) endpoint
    provides a track record of historical pulls of all your user connections.
  </Step>

  <Step title="Issues with data availability?" icon="sparkles">
    If you have trouble with data availability in general, the [User Resource Introspection](/api-reference/data/introspection/user-resources) endpoint
    is a live data ingestion record of all your user connections.

    <Note>
      This is inclusive of all historical data covered by the
      Historical Pull Introspection endpoint.
    </Note>
  </Step>
</Steps>

If you are not able to diagnose your issue using these tools, feel free to contact
Junction support for further assistance.


# Libraries
Source: https://docs.junction.com/home/libraries

Browse all Junction client libraries and SDKs, including typed API bindings for Java, Go, TypeScript, and Python, plus mobile SDKs for iOS and Android.

We have created a few libraries to help you integrate with the Junction API. We'll continue to add support for more libraries as we add more devices. Email [support@junction.com](mailto:support@junction.com) for specific library support.

### Typed Bindings of Junction API

These typed bindings track the Junction API OpenAPI Schema.

|                                                                      |                                |
| -------------------------------------------------------------------- | ------------------------------ |
| [junction-java](https://github.com/junction-api/junction-java)       | Junction API Java client       |
| [junction-go](https://github.com/junction-api/junction-go)           | Junction API Go client         |
| [@junction-api/sdk](https://www.npmjs.com/package/@junction-api/sdk) | Junction API TypeScript client |
| [junction-api-sdk](https://pypi.org/project/junction-api-sdk)        | Junction API Python client     |

### Mobile SDK

|                                                                      |                                     |
| -------------------------------------------------------------------- | ----------------------------------- |
| [vital-ios](https://github.com/tryVital/vital-ios)                   | Junction iOS Client                 |
| [vital-android](https://github.com/tryVital/vital-android)           | Junction Android Client             |
| [vital-flutter](https://github.com/tryVital/vital-flutter)           | Junction Flutter Client             |
| [vital-react-native](https://github.com/tryVital/vital-react-native) | Junction React Native Client        |
| [vital-connect](https://github.com/tryVital/vital-connect-rn)        | Whitelabel App using Junction's API |

### Web SDK

|                                                                  |                                     |
| ---------------------------------------------------------------- | ----------------------------------- |
| [vital-link](https://www.npmjs.com/package/@tryvital/vital-link) | React Library for initializing Link |


# Quickstart
Source: https://docs.junction.com/home/quickstart

Get started with the Junction API by setting up API keys, connecting a wearable device, and fetching your first health data in minutes.

## 1. API keys

Let's test out running Junction locally by cloning the [Quickstart app](https://github.com/tryVital/quickstart).
To create a team and get your API keys, you first need to sign up for a Junction account in the [Dashboard](https://app.junction.com).

Once registered, you can create a team by hovering over your username at the bottom of the Dashboard sidebar.
A team is associated with a region (either `EU` or `US`). The region dictates where data is stored.
You can learn more at [regions](/api-details/regions).

To create your API keys, go to the configuration section of the Dashboard.
For each region, you'll have access to two environments: Sandbox and Production.
We'll start in the Sandbox environment, so create a new Sandbox API key.

<Note>
  If you get stuck at any point in the Quickstart, help is just a click away!
  Join our Slack channel or send us a message to
  [support@junction.com](mailto:support@junction.com).
</Note>

| Environment  |                                             |                             |
| ------------ | ------------------------------------------- | --------------------------- |
| `sandbox`    | Testing, connect up to 50 live users        | api.sandbox.us.junction.com |
| `production` | Live environment to use with real customers | api.us.junction.com         |

## 2. Running Quickstart locally

Once you have your API keys, it's time to run the Junction Quickstart locally!
The instructions below will guide you through the process of cloning the [Quickstart repository](https://github.com/tryVital/quickstart),
customizing the `.env` file with your own Junction `API_KEY` and finally, building and running the app.

```bash 1. Clone quickstart and run locally theme={null}
# Note: If on Windows, run
# git clone -c core.symlinks=true https://github.com/tryVital/quickstart
# instead to ensure correct symlink behavior
git clone https://github.com/tryVital/quickstart.git

# Create .env, then fill
# out VITAL_API_KEY, VITAL_REGION (eu, us) and VITAL_ENV in .env
touch .env

# Note: must use python 3
# For virtualenv users:
# virtualenv venv
# source venv/bin/activate
poetry install

# Start the backend app
cd backend/python
source ./start.sh
```

Open a new shell and start the frontend app. Your app will be running at `http://localhost:3000`.

```bash 2. Run quickstart frontend theme={null}
# Install dependencies
cd quickstart/frontend
npm install

# Open .env.local, then fill
# out NEXT_PUBLIC_VITAL_API_KEY, NEXT_PUBLIC_VITAL_ENV and NEXT_PUBLIC_VITAL_REGION

# Start the frontend app
npm run dev

# Go to http://localhost:3000
```

## 3. Creating your first User

When retrieving data or connecting devices, Junction will require a `user_id` as input.
A `user_id` is a unique representation that we hold for a user. It allows you to fetch data for that user.
To create a user, you need to pass a unique id (`client_user_id`). This represents the user in your system. Our recommendation is
to store the Junction `user_id` in your db against the user row.

<Warning>
  Personally identifiable information (PII), such as an email address or phone number, should not be used as input for the `client_user_id` parameter.
</Warning>

Enter a new `client_user_id` and tap Create:

<img alt="quickstart" />

This can also be achieved via the API as follows.

<CodeGroup>
  ```bash Creating a Junction user (bash) theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/ \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-api-key: <YOUR_API_KEY>' \
       --data '{"client_user_id":"<YOUR_CLIENT_USER_ID>"}'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.create({ clientUserId: "<client_user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.create(client_user_id="<client_user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.user.requests.UserCreateBody;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().create(
      UserCreateBody.builder()
          .clientUserId("<client_user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.Create(context.TODO(), &junction.UserCreateBody{
      ClientUserId: "<client_user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```

  ```swift Swift theme={null}
  let user = try await VitalClient.shared.user.create(clientUserId)
  ```
</CodeGroup>

## 4. Connecting a source

A source, at Junction, is a medical device, wearable, or lab. It is a source of information for health data.
To connect a source, tap the connect button. This will launch the Junction Link Widget for that user.

Once you have entered your credentials and moved to the next screen, you have connected your first source!
You can now make API calls to retrieve data for that Source.

### How it works

As you might have noticed, you use both a server and a client-side component to access the Junction APIs.
A more detailed explanation of how linking works can be found in [link flow](/wearables/connecting-providers/link_flow).

The first step is to create a new `link_token` by making a `/link/token` request and passing in the
required configuration. This `link_token` is a short-lived, one-time use token that authenticates your
app with Junction Link, our frontend module.

```python Generating a Link Token theme={null}
# TEST BACKEND IMPLEMENTATION
from junction import Junction
from junction.environment import JunctionEnvironment
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

client = Junction(
    api_key="YOUR_API_KEY",
    environment=JunctionEnvironment.SANDBOX,
    timeout=30,
)

app = FastAPI()

app.add_middleware(  # type: ignore
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/token/{user_key}")
def get_token(user_key: str):
    return client.link.token(user_id=user_key)
```

Once you have a `link_token`, you can use it to initialize `Link`. `Link` is a drop-in client-side
module available for web, iOS, and Android that handles the authentication process.
The Quickstart uses `Link` on the web, which is a pure JavaScript integration that you trigger via your own client-side code.

```javascript Generating a Link Token theme={null}
import { Button } from "@chakra-ui/react";
import { useState, useCallback } from "react";
import { useVitalLink } from "@tryvital/vital-link";

export const LinkButton: React.FC<{ userID: string | null }> = ({ userID }) => {
  const [isLoading, setLoading] = useState(false);

  const onSuccess = useCallback((metadata) => {
    // Device is now connected.
    console.log("onSuccess", metadata);
  }, []);

  const onExit = useCallback((metadata) => {
    // User has quit the link flow.
    console.log("onExit", metadata);
  }, []);

  const onError = useCallback((metadata) => {
    // Error encountered in connecting device.
    console.log("onError", metadata);
  }, []);

  const config = {
    onSuccess,
    onExit,
    onError,
    env: "sandbox",
    region: "us",
  };

  const { open, ready, error } = useVitalLink(config);

  const handleVitalOpen = async () => {
    setLoading(true);
    const token = await getTokenFromBackend(userID);
    open(token.link_token);
    setLoading(false);
  };

  return (
    <Button
      bg="blue.400"
      color="white"
      fontSize={"12px"}
      px={2}
      py={0}
      onClick={handleVitalOpen}
      isLoading={isLoading || !ready}
      isDisabled={userID ? false : true}
    >
      Connect
    </Button>
  );
};
```

This is what your users see to connect their medical devices or wearables:

<img alt="Link specific" />

## 5. Making your first API request

We can now explore what happens when
you press the analyze button in the Quickstart to make an API call.
As an example, we'll look at the Quickstart's call to `/summary/sleep`, which retrieves sleep
summary data for a user. The request is simple and requires the Junction
`user_id`, `start_date` and `end_date`.

**Getting user sleep data**

<CodeGroup>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.sleep.get(
      "<user_id>",
      start_date="2021-01-01",
      end_date="2021-01-02",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.sleep.get({
      userId: "<user_id>",
      startDate: "2021-01-01",
      endDate: "2021-01-02",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.sleep.requests.GetSleepRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.sleep().get(
      "<user_id>",
      GetSleepRequest.builder()
          .startDate("2021-01-01")
          .endDate("2021-01-02")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  endDate := "2021-01-02"
  response, err := c.Sleep.Get(context.TODO(), &junction.GetSleepRequest{
      UserId:    "<user_id>",
      StartDate: "2021-01-01",
      EndDate:   &endDate,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```

  ```swift Swift theme={null}
  let sleepData = try await VitalClient.shared.summary.sleep(userId, startDate, endDate)
  ```
</CodeGroup>

## 6. SDKs and Libraries

We offer different SDKs so you can start building your app right away:

|                                                                      |                                                         |
| -------------------------------------------------------------------- | ------------------------------------------------------- |
| [junction-api-sdk](https://pypi.org/project/junction-api-sdk)        | Python library for calling Junction API on your backend |
| [vital-link](https://www.npmjs.com/package/@tryvital/vital-link)     | React Library for initializing Link                     |
| [@junction-api/sdk](https://www.npmjs.com/package/@junction-api/sdk) | Junction TypeScript Client                              |
| [vital-ios](https://github.com/tryVital/vital-ios)                   | Junction iOS Client                                     |
| [junction-java](https://github.com/junction-api/junction-java)       | Junction Java Client                                    |
| [junction-go](https://github.com/junction-api/junction-go)           | Junction Go Client                                      |

You can also download our [API collection](https://www.postman.com/collections/35339909-b0d080b7-3870-4aa8-a68b-a675f93a0533), or install Postman first and click the button below.

[![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/35339909-b0d080b7-3870-4aa8-a68b-a675f93a0533?action=collection%2Ffork\&source=rip_markdown\&collection-url=entityId%3D35339909-b0d080b7-3870-4aa8-a68b-a675f93a0533%26entityType%3Dcollection%26workspaceId%3Ddd82502b-0c9d-4fe4-9760-73a54bc2b8bf)

## Next Steps

Congratulations, you have completed the Junction Quickstart!

There are a few directions you can go in now:

<Card title="Junction Link" icon="link" href="/wearables/connecting-providers/introduction">
  A client-side component your users will interact with in order to link their
  accounts with Junction. It allows you to access their accounts via the Junction API.
</Card>

<Card title="Junction SDKs" icon="toolbox" href="/wearables/sdks/installation">
  Native toolkits to integrate Junction into iOS, Android, and Flutter
</Card>

<Card title="Junction Webhooks" icon="circle-nodes" href="/webhooks/introduction">
  Webhooks are a way to receive data from Junction. We frequently poll to receive
  data from the various providers.
</Card>


# Junction
Source: https://docs.junction.com/home/welcome

Integrate 300+ wearables, streamline lab operations, and build your own lab testing experience spanning all 50 states – with a single API.

<CardGroup>
  <Card title="Lab Testing" icon="flask" href="/lab/overview/introduction">
    Access a nationwide lab network. In-person, at-home, or hybrid methods to maximize completion rates.
  </Card>

  <Card title="Junction Sense" icon="sensor" href="/sense/overview">
    Automatically transforms raw activity and biometric data from 300+ connected devices into structured, aggregated datasets that update as new data arrives.
  </Card>

  <Card title="Device Data Providers" icon="gears" href="/wearables/providers/introduction">
    Connect to 300+ devices, and receive data in Junction standardized schemas.
  </Card>
</CardGroup>

<CardGroup>
  <Card title="Junction API" icon="seedling" href="/api-details/junction-api">
    User management, Device connection and data access, Lab Testing orders and results, Junction Sense Query and Data.
  </Card>

  <Card title="Junction Management API" icon="building" href="/api-details/junction-management-api">
    Programmatically manage all Junction regional and global resources in your organization, including Junction Sense Continuous Queries.
  </Card>
</CardGroup>

## Popular Topics

<CardGroup>
  <Card title="Quickstart" icon="bolt" href="/home/quickstart">
    The Quickstart walk-through gives you an overview of how to integrate with
    Junction. By the end of this guide, you'll have a working app with both
    backend and frontend.
  </Card>

  <Card title="Apple HealthKit" icon="apple" href="/wearables/guides/apple-healthkit">
    Learn how to integrate with Apple HealthKit through Junction Mobile SDKs,
    available in Native iOS, React Native and Flutter.
  </Card>

  <Card title="Android Health Connect" icon="android" href="/wearables/guides/android-health-connect">
    Learn how to integrate with Android Health Connect through Junction Mobile
    SDKs, available in Native Android, React Native and Flutter.
  </Card>

  <Card title="Junction Link Widget" icon="link" href="/wearables/connecting-providers/introduction">
    Junction Link enables your users to connect their account with their
    wearable data providers. Learn how to integrate the Link Widget for a
    no-fuss starter integration.
  </Card>

  <Card title="Junction Link API" icon="link" href="/wearables/connecting-providers/custom_widget">
    Learn how to use the Junction Link API to build your own Link Widget.
  </Card>

  <Card title="Timestamps and Time Zones" icon="clock" href="/wearables/providers/timestamps-and-time-zones">
    Learn about timestamp and time zone handling of wearable device data.
  </Card>

  <Card title="Webhooks & Events" icon="radar" href="/webhooks/introduction">
    Junction can push wearable device data as webhook events to you as soon as
    they are discovered. Learn about the delivery stages, event structure as
    well as the advanced ETL Pipeline options for high volume needs.
  </Card>

  <Card title="Synthetic Data" icon="parachute-box" href="/wearables/providers/test_data">
    Learn how to use Demo Connections — which emit synthetic data mimicking
    selected providers — as an alternative way to test your Junction
    integration.
  </Card>
</CardGroup>


# Communications
Source: https://docs.junction.com/lab/at-home-phlebotomy/communications

Configure SMS and email notifications sent to patients during each status change in the at-home phlebotomy order lifecycle.

Communication for patients is done via email or SMS for At-Home Phlebotomy orders.
Customers have the following options when setting this up:

* `Default` - Email and SMS communications are enabled.
* `SMS Only` - Only SMS communication is enabled.
* `Disable` - All communications from Junction are disabled.

Each option has a different set of content and status changes, depending on what triggered them.

<Info>You can enable or disable SMS and Email communications individually through the [**Junction Dashboard**](https://app.junction.com/), under the Team Settings section.</Info>

## Default Communications

### SMS Messages

A table of the **Order Status** and **default SMS** is provided below:

| Order Status            | Default Message                                                                                                                                                |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requisition_created`   | *"Hey **patient\_first\_name**, it's time to book your at-home phlebotomy draw. You can book a slot using the following link **booking\_link**."*              |
| `appointment_scheduled` | *"Your appointment with the phlebotomist has been booked at **date**! You can reschedule or cancel using the following link **booking\_link**."*               |
| `appointment_cancelled` | *"Hey, your at-home-phlebotomy appointment at **date** for the **team\_name** has been cancelled. You can rebook using the following link **booking\_link**."* |
| `draw_completed`        | *"Your at-home phlebotomy draw is complete! We're delivering your sample to the lab for processing."*                                                          |
| `completed`             | *"The lab has finished processing your blood sample, your results should be ready soon :)"*                                                                    |
| `cancelled`             | *"Hey, your order for the **team\_name** at-home-phlebotomy appointment has been cancelled. If this is by accident, please contact support."*                  |

<Info>SMS Texts are customizable, and can be enabled or disabled individually.</Info>

### Emails

For emails, the following table describes what information each email contains for each Order Status:

| Order Status            | Email Content Description                                                                                                                          |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requisition_created`   | An email with the booking link for **Junction Booking Widget** will be sent to the patient.                                                        |
| `appointment_scheduled` | An email confirming the appointment date and time will be sent to the patient, with the possibility of rescheduling or cancelling the appointment. |
| `appointment_cancelled` | An email confirming the cancellation of the appointment is sent, providing the possibility of scheduling a new appointment.                        |

<Info>Emails can be white-labeled and sent from your own domain.</Info>

## Scheduling Appointments Before A Requisition Has Been Created

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

If the ability to [`schedule appointments before a requisition has been created`](/lab/at-home-phlebotomy/order-appointment-lifecycle#scheduling-appointments-before-a-requisition-has-been-created) is enabled for your team, all appointment-related messages
will be triggered by the **appointment** status, not the **order** status. All non-appointment-related messages will be triggered by the **order** status, e.g.,
`requisition_created`, `draw_completed`, etc. (see above section).

### SMS Messages

A table of the **Appointment Status** and **default SMS** is provided below:

| Appointment Status | Default Message                                                                                                                                                |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `confirmed`        | *"Your appointment with the phlebotomist has been booked at **date**! You can reschedule or cancel using the following link **booking\_link**."*               |
| `cancelled`        | *"Hey, your at-home-phlebotomy appointment at **date** for the **team\_name** has been cancelled. You can rebook using the following link **booking\_link**."* |

### Emails

For emails, the following table describes what information each email contains for each Appointment Status:

| Appointment Status | Email Content Description                                                                                                                          |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `confirmed`        | An email confirming the appointment date and time will be sent to the patient, with the possibility of rescheduling or cancelling the appointment. |
| `cancelled`        | An email confirming the cancellation of the appointment is sent, providing the possibility of scheduling a new appointment.                        |

## Disable Communications

In this case, no communications will be sent from Junction. You have the ability to produce completely customized communications
using the [`Webhook events`](/lab/at-home-phlebotomy/webhooks) described previously.


# Order and Appointment Lifecycle
Source: https://docs.junction.com/lab/at-home-phlebotomy/order-appointment-lifecycle

Reference for all order and appointment lifecycle statuses in the at-home phlebotomy flow, from order creation through results delivery.

The **At-Home Phlebotomy** orders are composed of two different lifecycles, the **Order** lifecycle and
the **Appointment** lifecycle, detailed in the following sections.

## Order Lifecycle

As discussed in [`Lab Test Lifecycle - Statuses`](/lab/workflow/lab-test-lifecycle#statuses),
each lab testing modality has the following format:

`[HIGH-LEVEL STATUS].[TEST MODALITY].[LOW-LEVEL STATUS]`

For each modality, there can be multiple **low-level statuses**, for **At-Home Phlebotomy** the
possible low-level statuses are:

* `ordered`: Junction received the order, stored it into our system, and started processing it asynchronously.
* `requisition_created`: An order requisition form was validated and created with the partner laboratory, making the order available to be carried out.
* `requisition_bypassed`: An order requisition form wasn't created when the order was placed with us because it already existed.
* `appointment_pending`: An appointment was placed in Junction's system for the order, but doesn't have a scheduled date.
* `appointment_scheduled`: An appointment was scheduled or rescheduled for the order.
* `draw_completed`: The phlebotomy appointment was completed and the blood was drawn successfully.
* `appointment_cancelled`: The appointment was cancelled by either the patient, Junction, or you.
* `partial_results`: The laboratory has started making partial results available.
* `completed`: The laboratory processed the blood sample and final results are available.
* `sample_error`: The collected sample was unprocessable by the lab.
* `cancelled`: The order was cancelled by either the patient, Junction, or you.

The Finite State Machine that defines the possible transitions for the low-level statuses described above is illustrated
in the following diagram.

<Frame>
  <img />
</Frame>

<br />

<Info>In **sandbox**, there is no async transition from the `ordered` state to the `requisition_created` state. This must be **manually triggered** via the **Junction Dashboard**.</Info>

## Appointment Lifecycle

The appointment lifecycle is separate from the order lifecycle, and it corresponds to a single appointment.
The possible statuses are defined as follows:

* `pending`: An appointment was placed in the system, and is pending updates from the phlebotomy service.
* `reserved`: The time slot has been reserved for the appointment, but the scheduling confirmation is waiting on the creation of the order requisition.
* `scheduled`: The appointment has been scheduled or rescheduled.
* `in-progress`: The phlebotomist is on their way to the patient's address.
* `cancelled`: The appointment was cancelled by either the patient, Junction, or you.
* `completed`: The phlebotomy appointment was completed and the blood was drawn successfully.

The Finite State Machine that defines the possible transitions for the appointment statuses described above is illustrated
in the following diagram.

<Frame>
  <img />
</Frame>

The events are related to a single appointment. An order can have multiple existing appointments in Junction's system, although only one appointment will be considered active
and returned when using the [GET Appointment endpoint](/api-reference/lab-testing-at-home-phlebotomy/get-appointment).

## Scheduling Appointments Before A Requisition Has Been Created

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

When this feature is enabled on your Team, you can create an At-Home Phlebotomy appointment even before the requisition has been created for
the corresponding Order (i.e., reaching the `requisition_created` status).

This allows you to build an experience that can order the lab test *and* book an appointment consecutively, without having to wait on the
asynchronous requisition creation process that may take an indeterminate amount of time.

### API Journey

<Steps>
  <Step title="Creating an appointment">
    You create an Appointment for an Order which does not yet have a requisition.

    For an [Appointment Ready Tier](/lab/at-home-phlebotomy/service-tiers) service, the Appointment starts with the `pending` status and immediately moves to the `reserved` status.

    For an [Appointment Request Tier](/lab/at-home-phlebotomy/service-tiers) service, the Appointment starts with the `pending`
    status. Once the service has penciled in the time slot, the Appointment moves to the `reserved` status — if the order
    requisition still has not yet been created by then.

    <Note>
      If the order requisition has been created on or before the time slot confirmation, the Appointment moves directly to the
      `confirmed` status. The `reserved` status would be skipped.
    </Note>
  </Step>

  <Step title="Waiting for Requisition Creation">
    The requisition is being created asynchronously.
  </Step>

  <Step title="Requisition has been created">
    The requisition has now been created.

    For all types of Appointments:

    1. The Order will move through the `requisition_created` and `appointment_pending` Order statuses in quick succession.

    You will receive two `labtest.order.updated` events, one each for the two status transitions.

    For all [Appointment Ready Tier](/lab/at-home-phlebotomy/service-tiers) Appointments and all the penciled
    [Appointment Request Tier](/lab/at-home-phlebotomy/service-tiers) Appointments:

    1. The Order will move further to the `appointment_scheduled` Order status in quick succession.
    2. The Appointment will move to the `confirmed` status automatically, which corresponds to the `scheduled` appointment event.

    You will additionally receive a `labtest.order.updated` event and a `labtest.appointment.updated` event for the status transition.
  </Step>
</Steps>

## Cancelling An Appointment

If an Appointment is cancelled **before** a requisition has been created:

* The Appointment will move to the `cancelled` status; and
* There is no change to the Order, which will remain in the `ordered` status.

If an Appointment is cancelled **after** a requisition has been created:

1. The Appointment will move to the `cancelled` status; and
2. The Order will move from the `appointment_pending` or `appointment_scheduled` status to the `appointment_cancelled` status.

### Auto-cancellation of appointments without requisition

In cases where appointments can be scheduled before a requisition has been created, the appointment will be automatically cancelled by Junction if the following happens:

1. More than 8 hours have passed since the order was created, and no requisition has been received.
2. There are fewer than 2 hours until the appointment start time, and no requisition has been received.

<Note>
  The 8-hour auto-cancellation rule does not apply to [scheduled orders](/lab/workflow/scheduled-orders#scheduled-orders).
  Scheduled orders are fulfilled on their `activate_by` date, so Junction does not cancel their appointments based solely on
  the fact that more than 8 hours have passed since order creation without a requisition.
</Note>

When booking an appointment with Getlabs, we strongly recommend that the appointment is booked at least 48 hours in advance to allow sufficient time for the requisition to be received and sent to the phlebotomist.
Getlabs automatically reschedules appointments that do not have a requisition form around 24 hours before the appointment start time. They also inform the patient about the rescheduling, directly via SMS.
Restricting the scheduling of appointments to at least 48 hours in advance allows Junction to handle the auto-cancellation of the appointment, which triggers an appointment cancelled webhook.
You can then use this for patient communications, rather than relying on Getlabs to do so.


# Overview
Source: https://docs.junction.com/lab/at-home-phlebotomy/overview

Overview of the at-home phlebotomy modality where a phlebotomist visits a patient at home to draw blood for lab testing.

At-home phlebotomy tests are one of our offered modalities.
This modality is focused on patients that don't want to go to a Laboratory,
but instead want a phlebotomist to come to their home or office to carry out the phlebotomy draw.
The phlebotomist is responsible for bringing the required equipment and tubes and then delivering those to our partner Laboratories.

## Ordering Flow overview

To achieve this, a high-level overview of the process is defined as:

* An order is placed in Junction's system through our Dashboard or API.
* The order is added to a background queue and additional checks are made before a test requisition is created with the chosen Laboratory.
* After the requisition is created, some form of communication is carried out with the patient so they can book an appointment with our phlebotomy partners.
* On the day of the appointment, our partner phlebotomist will contact the patient directly to confirm that everything is correct for carrying out the appointment.
* The phlebotomist goes to the appointment, draws the patient's blood and then delivers it to the Laboratory.
* The Laboratory processes the patient's blood sample and generates the required results.
* Junction exposes the results as soon as they are available, both as PDF and structured data via API.

## Access Notes and Appointment Notes

There are two ways to provide instructions for the phlebotomist:

### Access Notes

Set `access_notes` on the patient address when creating an order. These describe how to physically reach the patient's location — gate codes, parking instructions, entrance details, etc. Access notes are automatically forwarded to the phlebotomy provider on every booking and reschedule.

### Appointment Notes

Set `appointment_notes` in the booking, request, or reschedule request body. These are per-appointment special instructions — for example, "Please bring photo ID" or "Patient prefers left arm". Appointment notes can be changed on each reschedule and are stored on the appointment itself.

## Constraints

* Phlebotomy appointments are not supported for patients under 18 years of age.
* The same patient can't have more than one active appointment with a specific **Phlebotomy provider**.
* The addresses provided when fetching the appointment availability slots must be reachable by the phlebotomist and must contain a street number and unit (if applicable).

To find out more details, see [`Order and Appointment Lifecycle`](/lab/at-home-phlebotomy/order-appointment-lifecycle), [`Communications`](/lab/at-home-phlebotomy/communications) and [`Webhooks`](/lab/at-home-phlebotomy/webhooks).


# Phlebotomy Service Tiers
Source: https://docs.junction.com/lab/at-home-phlebotomy/service-tiers

Compare the two phlebotomy service tiers: Appointment Ready for synchronous scheduling and Appointment Request for asynchronous assignment.

Junction offers multiple Tiers of phlebotomy services, with different coverage capabilities:

* **Appointment Ready:** An appointment is booked in Junction using the patient's address and the appointment's date and time. The scheduling is completely synchronous and fully controlled by Junction's customers through our API.
* **Appointment Request:** An appointment is requested through the Junction system using the patient's address. A phlebotomist will eventually assign themselves to the appointment and define the appointment's date and time with the patient.

The following Providers are available for each Tier:

|                     | Getlabs | Phlebfinders (Beta) |
| :------------------ | :-----: | :-----------------: |
| Appointment Ready   |    X    |                     |
| Appointment Request |         |          X          |

<Info>`appointment-request` appointments will start in the `pending` status, and won't have any time or date information.</Info>

## High Level flow for Appointment Scheduling

<Frame>
  <img />
</Frame>

<br />

The recommended high-level flow for selecting an appointment at Junction is:

* Place an At-Home Phlebotomy order with the [`POST /v3/order`](/api-reference/lab-testing/create-order) endpoint.
* Wait for the `Requisition Ready` status updates through our Webhooks.
* Fetch Provider data via the [`GET /v3/order/area/info`](/api-reference/lab-testing/area-info) endpoint, the response payload should look like this:

```json theme={null}
{
  "zip_code": "85004",
  "phlebotomy": {
    "is_served": true,
    "providers": [
      {
        "name": "getlabs",
        "tier": ["appointment-ready"]
      },
      {
        "name": "phlebfinders",
        "tier": ["appointment-request"]
      }
    ]
  }
}
```

* Select the Provider that best fits your needs.
  * If you use the [`POST /v3/order/{order_id}/phlebotomy/appointment/book`](/api-reference/lab-testing-at-home-phlebotomy/appointment-booking) endpoint, an `appointment-ready` provider is chosen on your behalf.
  * If you use the [`POST /v3/order/{order_id}/phlebotomy/appointment/request`](/api-reference/lab-testing-at-home-phlebotomy/appointment-request) endpoint, you must select a provider that offers an `appointment-request` tier.
* Wait for the `Appointment Webhooks` and `Order Webhooks` described in the [Webhooks](/lab/at-home-phlebotomy/webhooks) section.


# Webhooks
Source: https://docs.junction.com/lab/at-home-phlebotomy/webhooks

Reference for webhook events triggered during at-home phlebotomy order and appointment lifecycle changes, with example payloads.

The following webhook events are of interest when placing an At-Home Phlebotomy order. These are
described in detail in the following sections.

## Order webhook events

Based on the status present in [`Order and Appointment Lifecycle - Order Lifecycle`](/lab/at-home-phlebotomy/order-appointment-lifecycle#order-lifecycle),
Junction will trigger two kinds of webhook events, [`labtest.order.created`](/event-catalog/labtest.order.created) and
[`labtest.order.updated`](/event-catalog/labtest.order.updated).

The `labtest.order.created` event is triggered when an order is created in the system,
having the `ordered` status, and all subsequent status changes will trigger a `labtest.order.updated` event in the system.

<Note>The `partial_results` status does not trigger a Webhook unless specifically requested from Junction.</Note>

The webhook payload body will have the following information if the Order is in the `appointment_scheduled` status:

```json Phlebotomy Order Updated theme={null}
{
  "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
  "team_id": "6353bcab-3526-4838-8c92-063fa760fb6b",
  "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "patient_details": { "dob": "2020-01-01", "gender": "male" },
  "patient_address": {
    "receiver_name": "John Doe",
    "first_line": "123 Main St.",
    "second_line": "Apt. 208",
    "city": "San Francisco",
    "state": "CA",
    "zip": "91189",
    "country": "United States",
    "phone_number": "+1123456789"
  },
  "details": {
    "type": "at_home_phlebotomy",
    "data": {
      "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
      "appointment_id": "d55210cc-3d9f-4115-8262-5013f700c7be",
      "created_at": "2020-01-01T00:00:00Z",
      "updated_at": "2020-01-01T00:00:00Z"
    }
  },
  "sample_id": "123456789",
  "notes": "This is a note",
  "created_at": "2020-01-01T00:00:00Z",
  "updated_at": "2020-01-01T00:00:00Z",
  "status": "collecting_sample",
  "events": [
    {
      "id": 1,
      "created_at": "2022-01-01T00:00:00Z",
      "status": "received.at_home_phlebotomy.ordered"
    },
    {
      "id": 2,
      "created_at": "2022-01-02T00:00:00Z",
      "status": "received.at_home_phlebotomy.requisition_created"
    },
    {
      "id": 3,
      "created_at": "2022-01-03T00:00:00Z",
      "status": "collecting_sample.at_home_phlebotomy.appointment_scheduled"
    }
  ],
  "origin": "initial",
  "order_transaction": {
    "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
    "status": "active",
    "orders": [
      {
        "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "low_level_status": "appointment_scheduled",
        "low_level_status_created_at": "2022-01-03T00:00:00Z",
        "origin": "initial"
      }
    ]
  }
}
```

## Appointment webhook events

Based on the status present in [`Order and Appointment Lifecycle - Appointment Lifecycle`](/lab/at-home-phlebotomy/order-appointment-lifecycle#appointment-lifecycle),
Junction will trigger the [`labtest.appointment.updated`](/event-catalog/labtest.appointment.updated) webhook event.

<Info>
  The `labtest.appointment.updated` event is triggered for all possible appointment statuses, and is the **recommended** way of integrating to fetch all
  **At-Home Phlebotomy** status updates, together with the [Order Webhooks](#order-webhook-events).
</Info>

<Info>
  If the ability to [schedule appointments before a requisition has been created](/lab/at-home-phlebotomy/order-appointment-lifecycle#scheduling-appointments-before-a-requisition-has-been-created) is enabled for your team, and you intend to send patient communications for appointment updates, use the `labtest.appointment.updated` event. See more on communications for this feature [here](/lab/at-home-phlebotomy/communications#scheduling-appointments-before-a-requisition-has-been-created).
</Info>

The webhook payload body may have the following information if the appointment is in the `scheduled` status, after a **reschedule** has happened:

```json Phlebotomy Appointment Updated theme={null}
{
  "event_type": "labtest.appointment.updated",
  "data": {
    "id": "06c2c65b-74a0-4f25-a4a9-44f796296355",
    "user_id": "acf79a82-0c2c-4ca0-998b-378931793905",
    "order_id": "1ed9c8d7-e1b4-4d61-8123-0f99de5ae99a",
    "order_transaction_id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
    "address": {
      "first_line": "123 Main St.",
      "second_line": "Apt. 208",
      "city": "San Francisco",
      "state": "CA",
      "zip_code": "91189",
      "country": "United States"
    },
    "location": {
      "lng": -122.4194155,
      "lat": 37.7749295
    },
    "start_at": "2022-01-01T00:00:00",
    "end_at": "2022-01-01T00:00:00",
    "iana_timezone": "America/New_York",
    "type": "phlebotomy",
    "provider": "getlabs",
    "status": "pending",
    "event_status": "scheduled",
    "provider_id": "123",
    "can_reschedule": true,
    "event_data": {
      "origin": "patient",
      "is_reschedule": true
    },
    "events": [
      {
        "created_at": "2022-01-01T00:00:00Z",
        "data": null,
        "status": "scheduled"
      },
      {
        "created_at": "2022-01-01T00:00:00Z",
        "data": {
          "origin": "patient",
          "is_reschedule": true
        },
        "status": "scheduled"
      }
    ]
  }
}
```

<br />

<Info>The `event_data` field contains relevant information regarding the current appointment status, and may be specific for each `provider`.</Info>


# Communications
Source: https://docs.junction.com/lab/on-site-collection/communications

Configure SMS and email notifications sent to patients during each status change in the on-site collection order lifecycle.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

Communication for patients is done via email or SMS for On-site collection orders.
Customers have the following options when setting this up:

* `Default` - Email and SMS communications are enabled.
* `SMS Only or Email Only` - Only SMS or Email communication is enabled.
* `Disable` - All communications from Junction are disabled.

Each option has a different set of content and status changes, depending on what triggered them.

<Info>You can enable or disable SMS and Email communications individually through the [**Junction Dashboard**](https://app.junction.com/), under the Team Settings section.</Info>

## Default Communications

### SMS Messages

A table of the **Order Status** and **default SMS** is provided below:

| Order Status          | Default Message                                                                                                                                   |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ordered`             | *"Hi, , your order for the  on-site collection has been placed! We'll provide you with updates on the status of your order via text messages :)"* |
| `requisition_created` | *"Hey , it's time to visit your local collection center. Please check the email you just received for instructions."*                             |
| `draw_completed`      | *"Your on-site collection is complete! We're delivering your sample to the lab for processing."*                                                  |
| `completed`           | *"Your results have finished processing and should be ready soon."*                                                                               |
| `cancelled`           | *"Hey, your order for the  on-site collection has been cancelled. If this is by accident, please contact support."*                               |

<Info>SMS Texts are customizable, and can be enabled or disabled individually.</Info>

### Emails

For emails, the following table describes what information each email contains for each Order Status:

| Order Status          | Email Content Description                                                                              |
| --------------------- | ------------------------------------------------------------------------------------------------------ |
| `requisition_created` | An email with confirmation of the partner Lab and additional instructions will be sent to the patient. |

<Info>Emails can be customized and sent from your own domain.</Info>

## Disable Communications

In this case, no communications will be sent from Junction. You have the ability to produce completely customized communications
using the [`Webhook events`](/lab/on-site-collection/webhooks) described previously.


# Order Lifecycle
Source: https://docs.junction.com/lab/on-site-collection/order-lifecycle

Reference for all order lifecycle statuses in the on-site collection flow, from order placement through draw completion and results.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

The **On-site Collection** orders are composed of one lifecycle, the **Order** lifecycle, detailed in the following section.

## Order Lifecycle

As discussed in [`Lab Test Lifecycle - Statuses`](/lab/workflow/lab-test-lifecycle#statuses),
each lab testing modality has the following format:

`[HIGH-LEVEL STATUS].[TEST MODALITY].[LOW-LEVEL STATUS]`

For on-site collections, this will look like:

`[HIGH-LEVEL STATUS].on_site_collection.[LOW-LEVEL STATUS]`

For each modality, there can be multiple **low-level statuses**, for **On-site Collection** the
possible low-level statuses are:

* `ordered`: Junction received the order, stored it into our system, and started processing it asynchronously.
* `requisition_created`: An order requisition form was validated and created with the partner laboratory, making the order available to be carried out.
* `requisition_bypassed`: An order requisition form wasn't created when the order was placed with us because it already existed.
* `draw_completed`: The phlebotomy was completed and the blood was drawn successfully.
* `partial_results`: The laboratory has started making partial results available.
* `completed`: The laboratory processed the blood sample and final results are available.
* `cancelled`: The order was cancelled by either the patient, Junction, or you.

This closely models that of walk-in tests. Refer to that [order lifecycle](/lab/walk-in/order-lifecycle#order-lifecycle) for a diagrammatic explanation.


# Overview
Source: https://docs.junction.com/lab/on-site-collection/overview

Overview of the on-site collection modality for blood draws at customer-managed locations like clinics and wellness events.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

On-site collections are one of our offered modalities. Their locations are customer-managed settings where blood draws occur outside of traditional lab-owned patient service centers — such as primary care clinics, employer health centers, or event-based wellness pop-ups.

## Ordering Flow overview

To achieve this, a high-level overview of the process is defined as:

* An order is placed in Junction's system through our Dashboard or API.
* The order is added to a background queue and additional checks are made before a test requisition is created with the chosen Laboratory.
* After the requisition is created, some form of communication is carried out with the patient.
* After that, the patient can go at any time they want to the draw location to have their blood drawn.
* The Laboratory processes the patient's blood sample and generates the required results.
* Junction exposes the results as soon as they are available, both as PDF and structured data via API.

To find out more details, see [`Order Lifecycle`](/lab/on-site-collection/order-lifecycle), [`Communications`](/lab/on-site-collection/communications) and [`Webhooks`](/lab/on-site-collection/webhooks).


# Webhooks
Source: https://docs.junction.com/lab/on-site-collection/webhooks

Reference for webhook events triggered during on-site collection order lifecycle changes, with example payload structures.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

The following webhook events are of interest when placing an On-site collection order. These are
described in detail in the following sections.

## Order webhook events

Based on the status present in [`Order Lifecycle`](/lab/on-site-collection/order-lifecycle),
Junction will trigger two kinds of webhook events, [`labtest.order.created`](/event-catalog/labtest.order.created) and
[`labtest.order.updated`](/event-catalog/labtest.order.updated).

The `labtest.order.created` event is triggered when an order is created in the system,
having the `ordered` status, and all subsequent status changes will trigger a `labtest.order.updated` event in the system.

<Note>The `partial_results` status does not trigger a Webhook unless specifically requested from Junction.</Note>

The webhook payload body will have the following information if the Order is in the `completed` status:

```json On-site Collection Order Updated theme={null}
{
  "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
  "team_id": "6353bcab-3526-4838-8c92-063fa760fb6b",
  "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "patient_details": { "dob": "2020-01-01", "gender": "male" },
  "patient_address": {
    "receiver_name": "John Doe",
    "first_line": "123 Main St.",
    "second_line": "Apt. 208",
    "city": "San Francisco",
    "state": "CA",
    "zip": "91189",
    "country": "United States",
    "phone_number": "+1123456789"
  },
  "details": {
    "type": "on_site_collection",
    "data": {
      "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
      "created_at": "2020-01-01T00:00:00Z",
      "updated_at": "2020-01-01T00:00:00Z"
    }
  },
  "sample_id": "123456789",
  "notes": "This is a note",
  "created_at": "2020-01-01T00:00:00Z",
  "updated_at": "2020-01-01T00:00:00Z",
  "status": "completed",
  "events": [
    {
      "id": 1,
      "created_at": "2022-01-01T00:00:00Z",
      "status": "received.on_site_collection.ordered"
    },
    {
      "id": 2,
      "created_at": "2022-01-02T00:00:00Z",
      "status": "received.on_site_collection.requisition_created"
    },
    {
      "id": 3,
      "created_at": "2022-01-03T00:00:00Z",
      "status": "collecting_sample.on_site_collection.draw_completed"
    },
    {
      "id": 4,
      "created_at": "2022-01-04T00:00:00Z",
      "status": "sample_with_lab.on_site_collection.partial_results"
    },
    {
      "id": 5,
      "created_at": "2022-01-05T00:00:00Z",
      "status": "completed.on_site_collection.completed"
    }
  ],
  "origin": "initial",
  "order_transaction": {
    "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
    "status": "completed",
    "orders": [
      {
        "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
        "created_at": "2022-01-01T00:00:00Z",
        "updated_at": "2022-01-05T00:00:00Z",
        "low_level_status": "completed",
        "low_level_status_created_at": "2022-01-05T00:00:00Z",
        "origin": "initial"
      }
    ]
  }
}
```


# Compendium Search
Source: https://docs.junction.com/lab/overview/compendium-search

Search the lab test compendium to find canonical tests and crosswalk them to available lab-specific test candidates.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

`POST /v3/compendium/search` searches the compendium in two modes:

* `canonical`: find and rank canonical tests only.
* `crosswalk`: select one canonical test, then map it into per-lab candidates.

## Request body

```json theme={null}
{
  "mode": "canonical | crosswalk",
  "query": "string (optional in crosswalk, required in canonical)",
  "loinc_set_hash": "string (crosswalk only)",
  "labs": ["labcorp", "quest", "bioreference", "sonora_quest"],
  "include_related": true,
  "limit": 3
}
```

### Validation rules

#### Canonical mode

* `query` is required and must be non-empty.
* `loinc_set_hash` is not allowed.
* `limit` must be `1..10`.

#### Crosswalk mode

* Exactly one of `query` or `loinc_set_hash` is required.
* `limit` must be `1..20`.
* `include_related` controls whether related canonical candidates are returned.

If validation fails, the API returns HTTP `422`.

## Search modes

### 1) `canonical`

Purpose: return ranked canonical tests only.

Behavior:

* Returns:
  * `selected_canonical`: top candidate (or `null`).
  * `canonical_candidates`: ranked list (up to `limit`).
* `per_lab` and `related` are empty in this mode.

### 2) `crosswalk`

Purpose: choose one canonical test and expand it across labs.

Entry points:

* By text query: pick best canonical from query.
* By `loinc_set_hash`: load canonical directly by hash.

Behavior:

* If no canonical is found, response has:
  * `selected_canonical = null`
  * `canonical_candidates = []`
  * no per-lab candidates.
* If canonical is found, returns:
  * one `selected_canonical` and same item in `canonical_candidates`
  * `per_lab`: candidates grouped by lab slug
  * `related`: related canonical tests (if `include_related=true`)

## Canonical tests

"Canonical tests" are normalized, cross-lab test concepts.

They are used to:

* normalize query intent (`display_name`, `aliases`, `loinc_codes`, etc.)
* create one canonical anchor (`selected_canonical`)
* crosswalk into concrete provider/lab tests (`per_lab`)

Canonical ranking combines match quality and popularity score. The selected labs filter which per-lab candidates are returned in crosswalk mode.

## Output schema

```json theme={null}
{
  "mode": "canonical | crosswalk",
  "selected_canonical": {
    "loinc_set_hash": "string",
    "display_name": "string",
    "aliases": ["string"],
    "loinc_codes": ["string"],
    "provider_ids": ["string"],
    "loinc_components": ["string"],
    "loinc_groups": ["string"],
    "popularity_score": 0.0,
    "confidence": 0.0
  },
  "canonical_candidates": [
    {
      "loinc_set_hash": "string",
      "display_name": "string",
      "aliases": ["string"],
      "loinc_codes": ["string"],
      "provider_ids": ["string"],
      "loinc_components": ["string"],
      "loinc_groups": ["string"],
      "popularity_score": 0.0,
      "confidence": 0.0
    }
  ],
  "per_lab": {
    "quest": [
      {
        "marker_id": 0,
        "lab_id": 7,
        "lab_slug": "quest",
        "name": "string",
        "result_names": ["string"],
        "provider_id": "string",
        "loinc_set_hash": "string",
        "loinc_codes": ["string"],
        "loinc_components": ["string"],
        "loinc_groups": ["string"],
        "relation": "exact | subset | superset | overlap | suggested",
        "confidence": 0.0,
        "reason_codes": ["EXACT_LOINC_SET | SUBSET | SUPERSET | OVERLAP | NAME_MATCH | SYNONYM_MATCH"],
        "marker_popularity_score": 0.0
      }
    ]
  },
  "related": [
    {
      "canonical": {
        "loinc_set_hash": "string",
        "display_name": "string",
        "aliases": ["string"],
        "loinc_codes": ["string"],
        "provider_ids": ["string"],
        "loinc_components": ["string"],
        "loinc_groups": ["string"],
        "popularity_score": 0.0,
        "confidence": 0.0
      },
      "relation": "exact | subset | superset | overlap | suggested",
      "confidence": 0.0,
      "reason_codes": ["EXACT_LOINC_SET | SUBSET | SUPERSET | OVERLAP | NAME_MATCH | SYNONYM_MATCH"]
    }
  ]
}
```

Notes:

* `confidence` is model/service-generated and relative to result quality.
* Omitting `labs` defaults to all supported labs (`labcorp`, `quest`, `bioreference`, `sonora_quest`).

## Example requests

### Canonical mode

```bash theme={null}
curl -X POST "$BASE_URL/v3/compendium/search" \
  -H "Content-Type: application/json" \
  -H "x-vital-api-key: $API_KEY" \
  -d '{
    "mode": "canonical",
    "query": "hemoglobin a1c",
    "labs": ["quest", "labcorp"],
    "limit": 5
  }'
```

### Crosswalk mode by query

```bash theme={null}
curl -X POST "$BASE_URL/v3/compendium/search" \
  -H "Content-Type: application/json" \
  -H "x-vital-api-key: $API_KEY" \
  -d '{
    "mode": "crosswalk",
    "query": "comprehensive metabolic panel",
    "labs": ["quest", "bioreference"],
    "include_related": true,
    "limit": 3
  }'
```

### Crosswalk mode by canonical hash

```bash theme={null}
curl -X POST "$BASE_URL/v3/compendium/search" \
  -H "Content-Type: application/json" \
  -H "x-vital-api-key: $API_KEY" \
  -d '{
    "mode": "crosswalk",
    "loinc_set_hash": "f0a1b2c3d4",
    "labs": ["labcorp", "quest"],
    "include_related": false,
    "limit": 3
  }'
```

For endpoint schema and try-it behavior, see the API reference page: [Compendium Search](/api-reference/lab-testing/compendium/search).


# Ordering Idempotency
Source: https://docs.junction.com/lab/overview/idempotency

Use the X-Idempotency-Key header when creating lab orders to safely retry requests without duplicating orders on connection failures.

The API supports idempotency for safely retrying requests without accidentally performing the same operation twice. When creating an order via the [POST /v3/order](/api-reference/lab-testing/create-order), you can supply an `X-Idempotency-Key` header. Then, if a connection error occurs, you can safely repeat the request without risk of creating a second order.

This works by saving the resulting status code and body of the first request made for any given idempotency key, regardless of whether it succeeds or fails. Subsequent requests with the same key return the same result.

A client generates an idempotency key, which is a unique key that the server uses to recognize subsequent retries of the same request. How you create unique keys is up to you, but we suggest using V4 UUIDs, or another random string with enough entropy to avoid collisions.

You can remove keys from the system automatically after they're at least 24 hours old. We generate a new request if a key is reused after the original is pruned.

We save results only after the execution of an endpoint begins. If incoming parameters fail validation, or the request conflicts with another request that's executing concurrently, we don't save the idempotent result because the API endpoint does not initiate the execution. You can retry these requests.


# Insurance Ordering
Source: https://docs.junction.com/lab/overview/insurance

Configure patient insurance and place lab test orders with commercial insurance billing.

Junction supports lab test orders billed to commercial insurance. Availability depends on the lab account, patient state, and tests selected for the order.

## Commercial insurance workflow

Complete these steps in order:

1. [Create a user](/api-reference/user/create-user), or use an existing user, and ensure the user has patient demographics. Creating a user establishes the Junction user ID but does not create the demographics required for insurance. Use [Update User Demographics](/api-reference/user/upsert-info) to add or update them.
2. [Check insurance availability](#check-insurance-availability) for the intended lab account and patient state.
3. [Create insurance for the user](/api-reference/user/create-insurance). Junction associates the insurance with that user's demographics.
4. [Create the order](/api-reference/lab-testing/create-order) for the same user with `billing_type` set to `commercial_insurance` and valid diagnosis codes in `icd_codes`.

<Note>
  Create the user's insurance before creating the order. `is_primary` defaults to `true`, and Junction uses the user's most recently created primary insurance record for commercial insurance ordering. When the insurance details change, create a new primary insurance record before placing another order.
</Note>

## Create user insurance

Use [`POST /v2/user/{user_id}/insurance`](/api-reference/user/create-insurance) to create an insurance record. The endpoint creates a new record; it does not update an existing one. The user must already have demographics, including an address, because Junction links the insurance to that patient record.

Insurance details can change over time. Keep the user's insurance current to reduce the risk of a denied claim.

```json theme={null}
{
  "payor_code": "UNITE",
  "member_id": "123456789",
  "group_id": "123",
  "relationship": "Self",
  "insured": {
    "first_name": "John",
    "last_name": "Doe",
    "dob": "1999-01-01",
    "gender": "Male",
    "address": {
      "first_line": "123 Main Street",
      "second_line": null,
      "zip_code": "85004",
      "state": "AZ",
      "city": "Phoenix"
    },
    "phone_number": "+16025550123",
    "email": "john@example.com"
  },
  "is_primary": true
}
```

`group_id`, `second_line`, and `guarantor` are optional. A `guarantor` is required when `relationship` is not `Self`.

### Insured, guarantor, and relationship

The `insured` object identifies the person covered by the insurance plan and is always required. The `relationship` value describes the patient's relationship to the policyholder and accepts `Self`, `Spouse`, or `Other`.

When `relationship` is not `Self`, provide the financially responsible party in `guarantor`. If you provide a guarantor when `relationship` is `Self`, Junction stores it, but some lab partners may not accept it.

### Payor code

Partner labs use different identifiers for insurance companies. Junction standardizes these identifiers so you can use one Junction `payor_code` across supported labs.

Use [Search Payor Code](/api-reference/lab-testing/insurance/search-payor-get) to find the insurance company and use the returned `payor_code` when creating insurance. You can search by insurance company name or by a supported external provider's payor code, such as Change Healthcare or Availity.

<Note>
  [Contact Junction Customer Support](/home/getting-support#getting-support) for assistance if the payor information you need is missing.
</Note>

```json theme={null}
[
  {
    "payor_code": "AARPA",
    "name": "AARP",
    "aliases": ["AARP"],
    "org_address": {
      "first_line": "PO BOX 740819",
      "second_line": null,
      "country": "US",
      "zip": "30374",
      "city": "ATLANTA",
      "state": "GA"
    }
  }
]
```

## Check insurance availability

Not every lab account, state, or test supports commercial insurance. Review the [billing types configured for the lab account](/lab/overview/lab-accounts#billing-types), then call [Area Info](/api-reference/lab-testing/area-info) for the patient's zip code and confirm that the intended lab includes `commercial_insurance` in `supported_bill_types`.

If you intend to use a specific lab account, pass its `lab_account_id` to Area Info and provide the same [`lab_account_id`](/api-reference/lab-testing/create-order#body-lab-account-id-one-of-0) when creating the order. This ensures the availability check uses that account's billing configuration.

```json theme={null}
{
  "zip_code": "85007",
  "central_labs": {
    "labcorp": {
      "patient_service_centers": {
        "within_radius": 15,
        "radius": "25"
      },
      "supported_bill_types": [
        "commercial_insurance"
      ]
    }
  }
}
```

## Create the order

Create the order for the same `user_id` used to create the insurance. Include all fields required for a standard order, then set:

```json theme={null}
{
  "billing_type": "commercial_insurance",
  "icd_codes": ["<diagnosis_code>"]
}
```

### Billing type

Orders use `client_bill` by default. Set `billing_type` to `commercial_insurance` to use the user's latest primary insurance record.

### Diagnosis codes

Commercial insurance orders require at least one valid diagnosis code. Use [Search ICD Code](/api-reference/lab-testing/insurance/search-diagnosis) to find supported codes.

## Common errors

Junction rejects a commercial insurance order when:

* The selected lab account does not support `commercial_insurance`.
* The patient's state does not support commercial insurance for the selected lab account.
* The selected test or one of its markers is not available for commercial insurance in the patient's state.
* `icd_codes` is missing or contains an invalid diagnosis code.
* The user does not have patient demographics or a primary insurance record.

## Medicare and Medicaid location inference

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

The closed beta can infer the appropriate Medicare or Medicaid plan from the patient's address when you [create user insurance on the beta channel](/api-reference/user/create-insurance-beta):

* Use `MEDFED` for Medicare.
* Use `MAIDFED` for Medicaid.

For example, submitting `MEDFED` for a patient who lives in Arizona creates the insurance record with the applicable Arizona Medicare plan payor code.

This beta applies only to Medicare and Medicaid location inference. It does not apply to commercial insurance ordering as a whole.


# Introduction
Source: https://docs.junction.com/lab/overview/introduction

Introduction to the Junction Lab Testing API for ordering lab tests, managing results, and integrating with physician networks and partner labs.

Junction's lab test API allows digital health companies to carry out at-home lab testing. Companies can use the API to order a variety of tests, using multiple sampling methods.

We partner with CLIA and CAP certified labs across the United States that cover a range of different diagnostic tests. For example, lipid panels, metabolic profiles, hormone tests, and more. We collect these test samples via at-home kits, or phlebotomy.

### Features

Our lab test API gives you an end-to-end experience, from ordering a test all the way to receiving results, all with one API call!

What we offer:

* Multiple [test modalities](/lab/overview/testing-modalities) through one unified API.
* 50-state physician network - you can use your own physician if you have one, or we will automatically assign one from our network.
* Updates throughout the test lifecycle - we automatically send you webhook updates and SMS updates to your patients so that you both never miss an update.
* Test results: we will update you once the test results are ready. In case of abnormal results, we also provide follow-ups through our physician network.
* 1:1 support: after production launch, you will have direct access to Slack and our engineers.

### Start integrating

You can sign up today for our sandbox environment. The [quickstart](/lab/overview/quickstart) guide will help you get from zero to ordering tests in less than 30 minutes! Once the integration is ready, switching to production will just be a matter of switching API keys.

### Production launch

As a first step, we need to define what type of testing you want to offer to your users. We will have an introduction call to go over the test, projected volumes, customizations for kits and other questions you might have. Once we have all this information, we will begin setting up the test kits for distribution. Once the integration is complete, you will have access to the dashboard to start ordering tests via our API.

On [this page](https://tryvital.io/labs), you can check out some examples of the test panels we offer, and book an introductory call with us.

### Support

We will assist you throughout the whole integration with Junction. After launch, you will have a dedicated account manager with access to Slack, and 1:1 support with our engineers.

### Coverage

Our lab test API is available throughout the US. This is our current coverage by test modality:

* [At-home test kits](/lab/overview/testing-modalities#at-home-test-kits): 49 states (all excluding NY).
* [Walk-in tests](/lab/overview/testing-modalities#walk-in-tests): 49 states (all excluding NY).
* [At-home phlebotomy](/lab/overview/testing-modalities#at-home-phlebotomy): 35 states at the moment. Full coverage coming soon.
* [On-site collection](/lab/overview/testing-modalities#on-site-collection): In beta testing.

<Info>
  To find out more about our coverage, you can use the [area info endpoint](/api-reference/lab-testing/area-info) - this endpoint takes a zip code and tells you whether that area is covered by our service or not. Or you can contact us at [support@junction.com](mailto:support@junction.com) for more info.
</Info>


# Lab Accounts
Source: https://docs.junction.com/lab/overview/lab-accounts

Manage lab accounts that link your team to partner laboratories for order routing and result delivery configuration.

Lab accounts contain the configuration Junction needs to place orders and receive results from a specific lab. Each customer-owned lab account belongs to an Org and can be linked to one or more Teams. Junction platform accounts are managed by Junction and are not owned by a customer Org.

A Team can have multiple lab accounts linked, including multiple accounts for the same lab, but each order uses only one lab account.

## Ordering with a lab account ID

<Note>
  You only need to provide `lab_account_id` if you have your own account with a core lab. If your Team uses Junction's platform accounts, you do not need to provide it when placing an order.
</Note>

If you have your own core lab account, provide [`lab_account_id`](/api-reference/lab-testing/create-order#body-lab-account-id-one-of-0) when you want an order to be placed with that account rather than Junction's platform account. This makes your intended account explicit and ensures that Junction uses that lab account's configuration when routing the order. Your lab account must be active, linked to your Team, and associated with the lab selected for the order.

The relevant fields in the [Create Order](/api-reference/lab-testing/create-order) request are:

```json theme={null}
{
  "lab_account_id": "<lab_account_id>",
  "order_set": {
    "lab_test_ids": ["<lab_test_id>"]
  }
}
```

Before placing an order, use the [Get Lab Accounts (Team-level)](/api-reference/lab-testing/lab_accounts) endpoint to retrieve the lab accounts available to your Team and select the appropriate `id`.

### How Junction selects a lab account when `lab_account_id` is omitted

When `lab_account_id` is omitted from a [Create Order](/api-reference/lab-testing/create-order) request, Junction evaluates the lab accounts linked to your Team for the lab selected by the order:

| Lab accounts linked to your Team for the selected lab | Account selection                                                                                           |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| No lab accounts are linked                            | Junction uses its platform account when one is available to your Team for that lab.                         |
| Exactly one active lab account is linked              | Junction uses your active linked account.                                                                   |
| More than one active lab account is linked            | Junction does not select an account because it cannot determine which of your accounts you intended to use. |
| Lab accounts are linked, but none is active           | Junction does not fall back to its platform account.                                                        |

<Warning>
  If more than one active lab account is linked to your Team for the selected lab, the order may be rejected unless you provide `lab_account_id`. Junction cannot place the order if no active account is available.
</Warning>

## Billing types

Each lab account defines the billing types it supports and the states where each type is available. Each lab account object returned by [Get Lab Accounts (Team-level)](/api-reference/lab-testing/lab_accounts) includes an `allowed_billing` field such as:

```json theme={null}
{
  "allowed_billing": {
    "client_bill": ["AZ", "CA", "NY"],
    "commercial_insurance": ["AZ", "CA"]
  }
}
```

Each key is a supported `billing_type`, and its value lists the patient states where that billing type is available for the account. If a billing type is absent, the lab account does not support it. If the patient's state is absent from its list, that billing type is not available for the account in that state.

The `billing_type` in a [Create Order](/api-reference/lab-testing/create-order) request must be supported by the lab account used for the order. For the additional requirements associated with `commercial_insurance`, see [Insurance Ordering](/lab/overview/insurance).

## Retrieving and managing lab accounts

Use the Team-level endpoint to retrieve the accounts available to your Team for ordering. Use the Org-level endpoint to retrieve accounts across your Org and review which Teams are linked to each account.

| Topic         | [Org-level Get Lab Accounts](/api-reference/org-management/lab-accounts/get-lab-accounts) | [Team-level Get Lab Accounts](/api-reference/lab-testing/lab_accounts) |
| ------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| Scope         | Returns lab accounts for the Org, across Teams                                            | Returns lab accounts available to the current Team                     |
| Typical user  | Org admin or platform integrator                                                          | Team-scoped integration                                                |
| Best use case | Org-wide visibility and management                                                        | Team-specific runtime operations                                       |
| Team linkage  | Shows which Teams are linked to each account                                              | Returns only accounts linked to the current Team                       |

Manage Team linkage through the [Update Lab Account Teams (Org-level)](/api-reference/org-management/lab-accounts/update-lab-account-teams) endpoint. There is no Team-level endpoint for updating lab account linkage.

You can also view lab accounts tied to your Org on the [Lab Interface Requests page of your Org Config](https://app.junction.com/org/lab-interface-requests) in the Junction dashboard.

<Note>
  [Contact Junction Customer Support](/home/getting-support#getting-support) to create a lab account or resolve a lab account issue.
</Note>


# Patient Service Centers and Appointments
Source: https://docs.junction.com/lab/overview/locations

Find and verify nearby Patient Service Centers (PSCs) for walk-in lab tests using the Area Info and PSC Info API endpoints.

# Finding the Nearest Serviceable Patient Service Centers

Verifying if a particular zip code is serviceable is an important step, as not all labs have patient service centers within a state or within a reasonable distance.

The recommended high-level flow for verifying PSC availability at Junction is:

1. Fetch PSC location data via the `GET` [Area Info](/api-reference/lab-testing/area-info) endpoint, the response payload should look like this:

   ```python theme={null}
   {
     "zip_code": "85004",
     "central_labs": {
       "labcorp": {
           "within_radius": 5, # the number of PSCs within radius of provided zip code
           "radius": "25", # miles
           "capabilities": ["stat", "appointment_scheduling_with_lab"] # aggregate list of capabilities provided by PSCs within the radius
       }
     }
     ...
   }
   ```
2. Then you can query the `GET` [PSC Info](/api-reference/lab-testing/psc-info) endpoint for specific information on the PSCs within the radius of the provided zip code, which will look like this:

   ```json theme={null}
   {
     "lab_id": 27,
     "slug": "labcorp",
     "patient_service_centers": [
       {
         "metadata": {
           "name": "LABCORP",
           "state": "AZ",
           "city": "Phoenix",
           "zip_code": "85006",
           "first_line": "1300 N 12th St",
           "second_line": "Ste 300",
           "phone_number": "480-878-3988",
           "fax_number": "844-346-5903",
           "hours": null
         },
         "distance": "25",
         "site_code": "ABC",
         "capabilities": ["stat", "appointment_scheduling_with_lab"]
       },
     ]
   }
   ```

<Note>
  A **capability** is a specific service that a PSC provides, for example, STAT testing or appointment scheduling.
</Note>

# Booking appointments at Patient Service Centers

<Warning>
  Currently only available for Quest lab locations.
</Warning>

## Appointment scheduling capability

The ability to schedule appointments is designated with the `capabilities` field in the response from the `GET` [Area Info](/api-reference/lab-testing/area-info) or `GET` [PSC Info](/api-reference/lab-testing/psc-info) endpoints as:

* `appointment_scheduling_via_junction`: Indicates that the PSC allows scheduling appointments via Junction's API.
* `appointment_scheduling_with_lab`: Indicates that the PSC allows scheduling appointments directly with the lab, either online or via phone. This capability also indicates that scheduling appointments with this location is **not** available through Junction's API.

If neither scheduling capability is present, that means the PSC either does not offer appointment scheduling, or its scheduling availability is unknown.

<Warning>
  At this time, Arizona Quest lab locations cannot be scheduled through Junction's API. These labs are operated by Sonora Quest, and their scheduling service does not currently support third-party API scheduling.
</Warning>

## Appointment Availability

Use the `POST` [PSC Appointment Availability](/api-reference/lab-testing/psc-scheduling/appointment-psc-availability) endpoint to obtain the available slots. You can provide the `site_code` you obtain from the `GET` [PSC Info](/api-reference/lab-testing/psc-info) endpoint, or supply a zip code directly.

* If you provide a zip code, a maximum of 3 PSC locations will be displayed.

Note that since this endpoint can return availability for multiple PSCs, there may be multiple time zones associated with the returned data.

* ⚠️ The individual start and end times are in UTC, and each location has its own `iana_timezone` key, which should be used to convert these start and end dates to the correct time zone.
* The overarching `timezone` key will always be `null` in this endpoint.

## Booking

<Warning>
  Patient Service Centers have access to the booked appointment, which can be cancelled or updated outside of our system. When this occurs, the lab does not expose this to us, and so we do not have visibility, and it will not be reflected in our system or API.
</Warning>

In order to book these appointments, you can use the `POST` [PSC Appointment Availability](/api-reference/lab-testing/psc-scheduling/appointment-psc-availability) endpoint to find available appointment slots. You can then [book](/api-reference/lab-testing/psc-scheduling/appointment-psc-booking), [reschedule](/api-reference/lab-testing/psc-scheduling/appointment-psc-rescheduling), or [cancel](/api-reference/lab-testing/psc-scheduling/appointment-psc-cancelling) with Junction as many times as you want.

To allow the patient to track their appointment individually, we expose the `external_id` parameter in the appointment endpoints and webhooks.

* This code is the unique identifier for the appointment at the provider and should be made available to the patient.
* We provide it to the patient via [SMS communications](/lab/walk-in/communications) for the appointment.

### External ID Format

The `external_id` field contains the provider's unique appointment identifier:

| Provider | Format        | Example  | Description                                            |
| -------- | ------------- | -------- | ------------------------------------------------------ |
| Quest    | 6-letter code | `ABCDEF` | Used by Quest to identify appointments in their system |


# Order and Follow-up Physician
Source: https://docs.junction.com/lab/overview/physicians

Choose between using Junction's physician network, your own physicians, or a hybrid approach for lab test ordering and result review.

For an order to be valid/complete, both the request and the results need to be validated by a physician. As such, Junction provides three flows:

## 1. Order and Results through Junction Physician Network

The validation of both the order and the results is done by Junction's physician network.

For each order, Junction's physician network validates the requisition and, when the results are available, they are uploaded to Junction's physician network to be evaluated for abnormal/critical results.
On the physician's judgment, the patient may receive a phone call regarding their results.

## 2. Order and Results with Customer Physician Network

In this flow, the Customer must specify a `physician` when making an order request. Both the order and the results are the responsibility of the Customer's chosen physician.

## 3. Order with Junction Physician Network and Results with Customer Physician Network

In this flow, the Customer does not specify a `physician` when making an order request - they use Junction's physician network.

However, the results aren't uploaded to Junction's physician network, and it's the Customer's physician's responsibility to validate the results and do the follow-ups.

<Warning>
  When there are critical results, Junction's physician network will always be notified.
</Warning>


# Quickstart
Source: https://docs.junction.com/lab/overview/quickstart

Step-by-step guide to placing your first lab test order through the Junction API, from user creation to order submission.

We have carefully designed Junction's API to ensure a seamless and hassle-free experience for developers. You can get up and running with just a few lines of code. In this guide, we will walk you through the essential steps to order your first lab test.

### What you need

* If you haven't done so yet, you can get API keys by signing up for a Junction account in the [Dashboard](https://app.junction.com). Detailed instructions are available [here](/home/quickstart#1-api-keys).
* One of our [client libraries](/home/libraries). The lab testing API is available for the [Python](https://pypi.org/project/junction-api-sdk/) and [TypeScript](https://www.npmjs.com/package/@junction-api/sdk) SDKs. If none of those fit your use case, you can always directly call the API. Email [support@junction.com](mailto:support@junction.com) for specific library support.

In the following, replace `{{BASE_URL}}` with your team's [environment URL](/api-details/junction-api#environments).

### (1) Creating a user

In the context of our API, a user refers to the patient who will be undergoing the lab test. All you need to provide is a string identifying the user on your side (`client_user_id`). The response to this call returns the userId on our side, which you'll be able to use for future requests.

You can find more details in our [API quickstart guide](/home/quickstart#3-creating-your-first-user).

<CodeGroup>
  ```bash Creating a Junction user (bash) theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/user/ \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-api-key: <YOUR_API_KEY>' \
       --data '{"client_user_id":"<YOUR_CLIENT_USER_ID>"}'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.user.create({ clientUserId: "<client_user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.user.create(client_user_id="<client_user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.user.requests.UserCreateBody;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.user().create(
      UserCreateBody.builder()
          .clientUserId("<client_user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.User.Create(context.TODO(), &junction.UserCreateBody{
      ClientUserId: "<client_user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

### (2) Listing available tests

To retrieve the set of lab tests you have access to, use the `/v3/lab_tests` API endpoint. In Sandbox, you will already have access to a default set of tests. Once you're ready to head to production, we can [set up a call](/lab/overview/introduction#production-launch) and get you ready for launch!

<CodeGroup>
  ```bash Listing available tests (bash) theme={null}
  curl --request GET \
       --url {{BASE_URL}}/v3/lab_tests/ \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <YOUR_API_KEY>' \
       --header 'Content-Type: application/json'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.get()
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.get();
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().get();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.Get(context.TODO(), nil)
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

This will return a list of all the available tests to you. They include a description, turnaround time and price:

```json Response theme={null}
{
  "lab_tests": [
    {
      "id": "e2eaa385-a311-4f17-b33f-2165e3d24dd9",
      "name": "Lipids Panel",
      "description": "Cholesterol tests",
      "sample_type": "dried blood spot",
      "method": "testkit",
      "price": 10.0,
      "is_active": true,
      "lab": {
          "slug": "USSL",
          "name": "US Specialty Lab",
          "first_line_address": "123 Main St",
          "city": "New York",
          "zipcode": "10001",
      },
      "markers": [
          {
              "name": "Thyroid Stimulating Hormone",
              "slug": "tsh",
              "description": "",
              "min_value": 100,
              "max_value": 200,
              "unit": "fg/L"
          }
      ],
    }
  ]
}
```

You can then use the test `id` when creating an order.

### (3) Placing an order

Ordering a test for your user is as simple as making an API call. Ensure all patient name fields (`first_name`, `last_name`, `receiver_name`) follow our [name validation requirements](/lab/workflow/order-requirements#patient-name-validation):

<CodeGroup>
  ```bash Ordering a test (bash) theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v3/order/ \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json' \
       --data '
  {
    "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "patient_details": {
      "dob": "2022-07-06T22:20:26.796Z",
      "gender": "male | female",
      "email": "test@test.com"
    },
    "patient_address": {
      "receiver_name": "John Doe",
      "street": "Hazel Road",
      "street_number": "102",
      "city": "San Francisco",
      "state": "CA",
      "zip": "91789",
      "country": "U.S.",
      "phone_number": "+14158180852"
    },
    "lab_test_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "physician": {
      "first_name": "John",
      "last_name": "Doe",
      "email": "john@doe.com",
      "npi": "123456789",
      "licensed_states": ["CA", "NY"],
      "created_at": "2022-07-06T22:20:26.796Z",
      "updated_at": "2022-07-06T22:20:26.796Z"
    }
  }
  '
  ```

  ```python Python theme={null}
  from junction import Gender, Junction, PatientAddressWithValidation, PatientDetailsWithValidation
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.create_order(
      user_id="<user_id>",
      lab_test_id="<lab_test_id>",
      patient_details=PatientDetailsWithValidation(
          first_name="John",
          last_name="Doe",
          dob="2020-01-01",
          gender=Gender.MALE,
          phone_number="+1123456789",
          email="email@email.com",
      ),
      patient_address=PatientAddressWithValidation(
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
          phone_number="+1123456789",
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.createOrder({
      userId: "<user_id>",
      labTestId: "<lab_test_id>",
      patientDetails: {
          firstName: "John",
          lastName: "Doe",
          dob: "2020-01-01",
          gender: "male",
          phoneNumber: "+1123456789",
          email: "email@email.com",
      },
      patientAddress: {
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
          phoneNumber: "+1123456789",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.CreateOrderRequestCompatible;
  import com.junction.api.types.Gender;
  import com.junction.api.types.PatientAddressWithValidation;
  import com.junction.api.types.PatientDetailsWithValidation;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().createOrder(
      CreateOrderRequestCompatible.builder()
          .userId("<user_id>")
          .patientDetails(PatientDetailsWithValidation.builder()
              .firstName("John")
              .lastName("Doe")
              .dob("2020-01-01")
              .gender(Gender.MALE)
              .phoneNumber("+1123456789")
              .email("email@email.com")
              .build())
          .patientAddress(PatientAddressWithValidation.builder()
              .firstLine("123 Main St.")
              .city("San Francisco")
              .state("CA")
              .zip("91189")
              .country("US")
              .build())
          .labTestId("<lab_test_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  labTestId := "<lab_test_id>"
  response, err := c.LabTests.CreateOrder(context.TODO(), &junction.CreateOrderRequestCompatible{
      UserId:    "<user_id>",
      LabTestId: &labTestId,
      PatientDetails: &junction.PatientDetailsWithValidation{
          FirstName:   "John",
          LastName:    "Doe",
          Dob:         "2020-01-01",
          Gender:      junction.GenderMale,
          PhoneNumber: "+1123456789",
          Email:       "email@email.com",
      },
      PatientAddress: &junction.PatientAddressWithValidation{
          FirstLine:   "123 Main St.",
          City:        "San Francisco",
          State:       "CA",
          Zip:         "91189",
          Country:     "US",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

```json Response theme={null}
{
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "patient_details": {"dob": "2020-01-01", "gender": "male"},
        "patient_address": {
            "receiver_name": "John Doe",
            "first_line": "123 Main St.",
            "second_line": "Apt. 208",
            "city": "San Francisco",
            "state": "CA",
            "zip": "91189",
            "country": "United States",
            "phone_number": "+1123456789",
        },
        "details": {
            "type": "testkit",
            "data": {
                "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
                "shipment": {
                    "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
                    "outbound_tracking_number": "<outbound_tracking_number>",
                    "outbound_tracking_url": "<outbound_tracking_url>",
                    "inbound_tracking_number": "<inbound_tracking_number>",
                    "inbound_tracking_url": "<inbound_tracking_url>",
                    "outbound_courier": "usps",
                    "inbound_courier": "usps",
                    "notes": "<notes>",
                    "created_at": "2020-01-01T00:00:00.000Z",
                    "updated_at": "2020-01-01T00:00:00.000Z",
                },
                "created_at": "2020-01-01T00:00:00Z",
                "updated_at": "2020-01-01T00:00:00Z",
            },
        },
        "diagnostic_lab_test": {
            "name": "Lipids Panel",
            "description": "Cholesterol test",
            "method": "testkit",
        },
        "sample_id": "123456789",
        "notes": "This is a note",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "status": "collecting_sample",
        "events": [
            {
                "id": 1,
                "created_at": "2022-01-01T00:00:00Z",
                "status": "received.testkit.ordered",
            }
    ],
}
```

As demonstrated in this Quickstart Guide, integrating with our API is straightforward, allowing you to access a wide range of lab tests with minimal effort. Placing an order for a lab test is as simple as making a request to the appropriate endpoint. With our user-friendly design and clear documentation, you can harness the power of our clinical test API to enhance your application and deliver a seamless experience to your users.


# Testing in Sandbox
Source: https://docs.junction.com/lab/overview/sandbox

Test lab test ordering in the Junction sandbox environment with lifecycle simulation, pre-configured tests, and collection method restrictions.

To enable customers to simulate the ordering process in the sandbox environment, Junction provides simulated results and a way to transition an order through its [lifecycle](/lab/workflow/lab-test-lifecycle).

## Creating Lab Tests

Every created team comes with 4 standard `Lab Tests` to allow the customer to test all 4 collection modalities:

* **Test Kit**: At-home collection using shipped test kits
* **Walk-in Test**: Patient visits a Patient Service Center (PSC) location
* **At-Home Phlebotomy**: A mobile phlebotomist visits the patient's location
* **On-Site Collection**: Collection at the customer's facility

However, since most customers choose to have their own custom lab tests, you should create your own tests using the [API](/lab/workflow/create-test). This is encouraged, as Junction generates results based on the associated markers. More on this in the [results section](/lab/overview/sandbox#results).

## Lifecycle

When an order is placed in the sandbox environment, it is "stuck" in the `ordered` state and will not progress unless triggered to do so. This can be done through the dashboard or through the [API](/api-reference/lab-testing/simulate-order).

Initiating the simulation process will progress the order through the expected success states defined in the [lifecycle section](/lab/workflow/lab-test-lifecycle), triggering all expected webhooks, emails, and SMS messages.

## Collection Method Restrictions

### At-Home Phlebotomy

<Warning>
  When testing At-Home Phlebotomy appointments, there are only two specific zip codes allowed:

  * **85004** (Phoenix, AZ) - Uses Getlabs provider
    * And only one specific address: `West Lincoln Street, Phoenix, AZ 85004, USA`
  * **54650** - Uses PhlebFinders provider
</Warning>

### Quest PSC Appointments

In the sandbox, Quest appointment slots are generated automatically with these patterns:

**Availability:**

* Same day: Next hour until 5 PM EST (if before 4 PM)
* Future days: 7 AM - 12 PM EST
* 20-minute intervals for all slots

<Warning>
  **Timezone:** All sandbox appointments use `America/New_York` timezone regardless of location (production uses actual PSC timezones).
</Warning>

We have a feature request in our backlog to support more representative sandbox appointment slots, including handling time zones specific to locations. We don't have a timeline for this right now, but if you'd like to be notified when it is implemented, please contact our support team.

## Results

To allow customers to test, Junction generates fake results for each lab test based on the [expected markers](/lab/results/result-formats#expected-results).

The generated JSON results will cycle through all possible [result types](/lab/results/result-formats#resulttype):

* `numeric`: Numerical values with reference ranges
* `comment`: Textual commentary results
* `range`: Range-based results (e.g., "\<50")

<Warning>
  **Important Notes:**

  * Generated values might not match the expected unit
  * **PDF results are not dynamically generated** - Junction returns an example PDF that does not reflect the ordered test
  * Account numbers for shipping labels return a hardcoded value `1234567890` in sandbox
</Warning>

## Customizing the Response

You can customize the end response of the results via the body of the [Simulate Order](/api-reference/lab-testing/simulate-order) endpoint.

There are three avenues of customization currently:

### 1. Interpretation

Supplying the `interpretation` field will set the result's status to the supplied value. If you set it to critical, then the critical result workflow will be triggered.

```json theme={null}
{
  "interpretation": "critical"
}
```

Available interpretations:

* `normal`: Standard normal results (default)
* `abnormal`: Results outside normal ranges
* `critical`: Triggers critical result notifications and workflows

### 2. Result Types

This field controls what result types will be generated. Omitting it will cycle through all available types.

```json theme={null}
{
  "result_types": ["numeric", "comment"]
}
```

Available result types:

* `numeric`: Numerical results with reference ranges
* `comment`: Text-based results and observations
* `range`: Range-based results

### 3. Missing Results

Supplying this field will generate missing biomarkers in the result to simulate incomplete lab processing.

```json theme={null}
{
  "has_missing_results": true
}
```

This simulates scenarios where some biomarkers fail processing or are unavailable, which is common in real-world lab operations.

## Environment-Specific Behavior

### Sandbox vs Production Differences

| Feature              | Sandbox                                      | Production                                |
| -------------------- | -------------------------------------------- | ----------------------------------------- |
| Order Progression    | Manual trigger required                      | Automatic, based on lab processing times. |
| PDF Results          | Static example PDF                           | Dynamic lab-generated PDFs                |
| Account Numbers      | Hardcoded `1234567890`                       | Real lab account numbers                  |
| Phlebotomy Locations | Restricted to specific zip codes             | Full coverage area                        |
| Quest PSC Slots      | Mock slots, `America/New_York` timezone only | Real Quest API integration                |
| Appointment Times    | Restricted availability (see above)          | Based on actual PSC availability          |

## Testing Recommendations

1. **Use Custom Lab Tests**: Create tests with your specific biomarkers for realistic result simulation
2. **Test All Collection Methods**: Verify each modality works with your integration
3. **Test Error Scenarios**: Use missing results and different interpretations
4. **Test Appointment Workflows**: Book, reschedule, and cancel appointments to verify full flow


# Testing Modalities
Source: https://docs.junction.com/lab/overview/testing-modalities

Compare the four lab testing modalities: at-home test kits, at-home phlebotomy, walk-in tests, and on-site collection.

By integrating with our API, you will have access to the following testing modalities:

* At-home test kit.
* At-home phlebotomy visit.
* Walk-in test visit.
* On-site collection visit (in closed beta).

## At-home test kits

This involves sending a kit to a patient's home. They will then be able to collect their specimen and send it back to our lab networks via mail.

At-home testing kits are comprised of a number of components such as:

<div>
  <div>
    * Mailer Box
    * Blade Lancets
    * Plasters
    * Gauze
    * ADX Card
    * Blood collection card
    * Saliva Tubes
    * Collection Supplies
    * Instruction Booklet

    Additional components can be included in the test kit box. To fully customize the kit, just contact us at [support@junction.com](mailto:support@junction.com).
  </div>

  <img />
</div>

## At-home phlebotomy

At-home phlebotomy requires a phlebotomist to visit a patient's home and carry out the phlebotomy draw. The sample is then delivered to the lab by the phlebotomist.

The flow for at-home phlebotomy orders is as follows:

* [Place an order](/lab/overview/quickstart) via the API.
* The order is processed in the background. For at-home phlebotomy, this means generating the requisition form for the lab test.
* Once the requisition form is ready, a webhook update and an email to the patient are sent. The email will let them know they can now book an appointment.
* The patient goes through Junction's appointment booking dashboard, where they can select a location and time from the available slots.
* Once the appointment takes place and the sample is drawn, it will be taken to the lab.
* Once the lab analyzes the results, the order is complete and the results can be [fetched](/lab/results/result-formats).

## Walk-in tests

Walk-in tests require the patient to visit a lab location (often referred to as a Patient Service Center or PSC)—such as Quest, Labcorp, or BioReference—to have their sample collected. A requisition form is needed to perform the test, which Junction's API generates if the patient meets the required conditions.

This workflow closely mirrors the at-home phlebotomy flow, except that appointment scheduling is not required. With a "walk-in" test, a patient can simply *walk in* to a PSC or they can schedule an appointment (Quest only).

## On-site collection

On-site collection closely mirrors the walk-in test flow. Scheduling an appointment is not necessary. On-site collections are currently in beta testing.

<Info>
  Expanded support for on-site or in-clinic draws coming soon!
</Info>


# Turnaround Times
Source: https://docs.junction.com/lab/overview/turnaround-times

Review turnaround time estimates for lab test results at the marker, panel, and order levels with 95th and 99th percentile metrics.

For a comprehensive Dashboard-focused overview of turnaround times, check out [the product guide](https://support.junction.com/articles/9137043704-turnaround-time). This page focuses on the API aspects.

We have two different turnaround time metrics:

1. Common turnaround time which 95% of orders should fall within.
2. Worst-case turnaround time which 99% of orders should fall within.

Furthermore, each operates on three distinct levels:

1. Markers
2. Panels
3. Orders

<Note>
  Turnaround times are calculated from historical data. For some tests, we may not yet have enough data to make a determination on its turnaround time. We won't include turnaround times for these tests in our API responses.
</Note>

<Warning>
  We recalculate expected turnaround times regularly using the latest live data. Values might change dramatically from time to time to reflect real-world logistical circumstances.
</Warning>

## Marker turnaround times

Marker turnaround times are based on historical orders of each marker. If a marker has been ordered enough such that we can make a statistically viable determination on its average turnaround time, we will include these in payloads containing marker data. For example, the response body for [retrieving markers for a lab test](/api-reference/lab-testing/lab-test-markers) includes the common and worst-case number of days to receive results for that marker after collection time. If we don't have enough data for that marker, those values will be null.

## Panel turnaround times

Panels comprise one or more markers, and the turnaround time for the panel is the largest turnaround time among its constituent markers. Just like in marker response bodies, lab test response bodies return the common and worst-case turnaround times (in days) for the panel. You can see this when [getting all available tests](/api-reference/lab-testing/tests-paginated), for example. If any of the markers in the panel have null turnaround times, then the panel also has a null turnaround time.

## Order turnaround times

An order's turnaround time is the same as its panel, so we don't repeat that information in the top-level order response body. You can still find that information in the `lab_test` object within the response body if you need it. Instead, the top-level body includes two related fields:

1. Expected result by date
2. Worst-case result by date

These will be null unless the order's lab test has a non-null turnaround time *and* the sample collection has already been performed. The time of sample collection is what we add to the panel turnaround times to determine the expected and worst-case result by dates. [Retrieving an order](/api-reference/lab-testing/get-order) is an example of where this is included.


# Lab Report Parsing
Source: https://docs.junction.com/lab/report-parsing/overview

Extract structured biomarker data from PDF and image lab reports using the Lab Report Parsing API with automatic LOINC code matching.

Junction's Lab Report Parsing API converts lab report files into structured JSON. You can upload PDF, JPEG, and PNG reports from external laboratories, patient-uploaded records, or historical chart archives, then retrieve extracted metadata, results, reference ranges, interpretations, and LOINC matches.

Lab Report Parsing is separate from Junction's ordered lab test workflow. It does not place an order, collect a sample, or request a result from a lab. It reads an existing report document and returns the data Junction can extract from that document.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

## When to Use It

Use Lab Report Parsing when you already have a completed lab report file and need to make it usable in your application.

Common use cases include:

* Patient uploads of lab reports from outside providers
* Historical data imports from PDFs or scanned records
* Multi-lab result aggregation using LOINC as a normalization layer
* Backfilling structured results before a user starts ordering through Junction

If the result came from a Junction lab order, use the order [result format documentation](/lab/results/result-formats) and [results endpoints](/api-reference/lab-testing/results/get-results) instead.

## Workflow

The parsing workflow is asynchronous:

1. Upload one or more report files to create a parsing job.
2. Junction validates and stages the uploaded file, then queues the parsing job.
3. Junction extracts report metadata and lab results, then attempts to match extracted results to LOINC codes.
4. Your application receives a webhook or polls the job endpoint.
5. When the job is `completed`, the `data` object contains parsed metadata and results.

```mermaid theme={null}
sequenceDiagram
    participant App as Your App
    participant API as Junction API
    participant Parser as Parser Service

    App->>API: POST /lab_report/v1/parser/job
    API->>API: Validate and stage file
    API->>Parser: Queue parsing job
    API-->>App: { id, status: "upload_pending" }
    Parser->>Parser: Extract results and match LOINCs
    API-->>App: Webhook: lab_report.parsing_job.updated
    App->>API: GET /lab_report/v1/parser/job/{job_id}
    API-->>App: { status: "completed", data: {...} }
```

## Uploading Reports

Create a parsing job with the [Create Lab Report Parser Job](/api-reference/lab-testing/lab-report-parsing/post-lab-report-parser-job) endpoint. The request is `multipart/form-data` and requires:

| Field                | Required | Description                                                                              |
| -------------------- | -------- | ---------------------------------------------------------------------------------------- |
| `file`               | Yes      | One lab report file, or multiple image files for one report.                             |
| `user_id`            | Yes      | Junction user ID to associate with the parsed report.                                    |
| `needs_human_review` | No       | Set to `true` to request manual review where enabled for your team. Defaults to `false`. |

Supported file formats and upload limits:

| Upload         | Supported formats | Limit                                          |
| -------------- | ----------------- | ---------------------------------------------- |
| Single file    | PDF, JPEG, or PNG | 10 MB                                          |
| Multiple files | JPEG and PNG only | Up to 8 files, 10 MB per file, 20 MiB combined |
| PDF pages      | PDF upload        | Up to 20 pages                                 |

When you upload multiple image files, Junction merges them into a single PDF before sending the report to the parser. Multi-file uploads cannot include PDFs. Junction validates both the declared `Content-Type` and the file's magic bytes, so spoofed or corrupted files are rejected before parsing starts.

```bash cURL theme={null}
curl --request POST \
     --url {{BASE_URL}}/lab_report/v1/parser/job \
     --header 'accept: application/json' \
     --header 'x-vital-api-key: <YOUR_API_KEY>' \
     --header 'Content-Type: multipart/form-data' \
     --form 'file=@/path/to/lab_report.pdf' \
     --form 'user_id=<USER_ID>' \
     --form 'needs_human_review=false'
```

The create response returns the job immediately. At this point, `data` is `null` because parsing has not completed.

```json Response theme={null}
{
  "id": "8eb0217f-4683-4a3c-adca-faf95ac65739",
  "status": "upload_pending",
  "failure_reason": null,
  "data": null,
  "needs_human_review": false,
  "is_reviewed": false
}
```

## Job Statuses

The `status` field describes the state of the parsing job.

| Status           | Description                                                                                                 |
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
| `upload_pending` | Job was created and the file upload is being finalized. This is the status returned by the create endpoint. |
| `started`        | The file was uploaded and parsing is in progress.                                                           |
| `completed`      | Parsing completed and results are available in `data`.                                                      |
| `failed`         | Parsing failed. Check `failure_reason` for more information.                                                |

For parser jobs, `failure_reason` commonly includes:

| Failure reason     | Meaning                                                                                      |
| ------------------ | -------------------------------------------------------------------------------------------- |
| `invalid_input`    | The parser determined that the document is not a lab report containing medical test results. |
| `not_english`      | The report language is not supported.                                                        |
| `processing_error` | Junction could not process the report because of an internal parser or provider failure.     |

<Note>
  Parser status and failure reason enums are non-exhaustive. Store unknown values safely and avoid hard-failing if Junction adds a new value or returns `failure_reason: null`.
</Note>

### Upload Validation Errors

Some invalid uploads are rejected synchronously by the create endpoint instead of becoming failed parser jobs. These errors return an HTTP error response and do not produce a completed async parsing result.

| Upload issue                                      | Response behavior                  |
| ------------------------------------------------- | ---------------------------------- |
| Unsupported file type                             | `400` error before parsing starts. |
| Empty file                                        | `400` error before parsing starts. |
| Declared `Content-Type` does not match file bytes | `400` error before parsing starts. |
| File is larger than the upload limit              | `413` error before parsing starts. |
| PDF is corrupted, truncated, or unreadable        | `400` error before parsing starts. |
| PDF exceeds the page limit                        | `400` error before parsing starts. |

## Human Review

Set `needs_human_review` to `true` to mark the job as requiring manual review where enabled for your team. Human review is useful for high-impact workflows, low-quality scans, complex multi-page reports, or reports where your application needs a higher-confidence extraction path.

Human review is not enabled for every team by default. Contact your account manager before depending on it in production.

If your team is not enabled for human review, creating a job with `needs_human_review=true` returns a `400` response:

```json Error theme={null}
{
  "detail": "Human review is not supported yet for your team please contact support"
}
```

The response includes two review fields:

| Field                | Meaning                                                                              |
| -------------------- | ------------------------------------------------------------------------------------ |
| `needs_human_review` | Whether the job was submitted with a manual-review request.                          |
| `is_reviewed`        | Whether manual review has been completed, where a manual-review workflow is enabled. |

## Parsed Output

When `status` is `completed`, `data` contains:

| Field      | Description                                                                              |
| ---------- | ---------------------------------------------------------------------------------------- |
| `metadata` | Patient and report-level metadata extracted from the document.                           |
| `results`  | Array of extracted lab results. Each item represents one reported marker or observation. |

Example completed response:

```json Response theme={null}
{
  "id": "8eb0217f-4683-4a3c-adca-faf95ac65739",
  "status": "completed",
  "failure_reason": null,
  "data": {
    "metadata": {
      "patient_first_name": "Jane",
      "patient_last_name": "Doe",
      "dob": "1990-01-01",
      "gender": "female",
      "lab_name": "Acme Labs",
      "date_reported": "2025-01-01",
      "date_collected": "2024-12-30",
      "specimen_number": "ABC123"
    },
    "results": [
      {
        "test_name": "Glucose",
        "value": "90",
        "type": "numeric",
        "units": "mg/dL",
        "min_reference_range": 70,
        "max_reference_range": 99,
        "source_panel_name": "CMP",
        "sample_type": "serum_plasma_blood",
        "measurement_kind": "direct",
        "sensitivity": "unknown",
        "loinc_match_status": "auto_match",
        "loinc_matches": [
          {
            "loinc_code": "2345-7",
            "loinc_name": "Glucose [Mass/volume] in Serum or Plasma",
            "display_name": "Glucose",
            "aliases": [],
            "confidence_score": 0.99
          }
        ],
        "interpretation": "normal",
        "is_above_max_range": false,
        "is_below_min_range": false
      }
    ]
  },
  "needs_human_review": false,
  "is_reviewed": false
}
```

### Metadata

`metadata` is extracted from the document header and surrounding report content when present.

| Field                | Description                                                          |
| -------------------- | -------------------------------------------------------------------- |
| `patient_first_name` | Patient first name from the report.                                  |
| `patient_last_name`  | Patient last name from the report.                                   |
| `dob`                | Patient date of birth as printed or normalized from the report.      |
| `gender`             | Extracted patient gender normalized to `male`, `female`, or `other`. |
| `lab_name`           | Name of the lab or reporting organization.                           |
| `date_reported`      | Date the report was issued.                                          |
| `date_collected`     | Date the specimen was collected.                                     |
| `specimen_number`    | Lab specimen, accession, or sample identifier.                       |

Not every report contains every metadata field. Treat patient names, date of birth, lab name, report dates, and specimen number as nullable. Treat unknown or unsupported gender values as `other`.

### Result Fields

Each `data.results[]` item contains the extracted value and associated context.

| Field                 | Description                                                                                                        |
| --------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `test_name`           | Normalized marker or observation name derived from the report.                                                     |
| `value`               | Result value as a string. See [Value and Type](#value-and-type).                                                   |
| `type`                | Result shape, such as `numeric`, `range`, or `comment`.                                                            |
| `units`               | Units extracted from the report, when available.                                                                   |
| `min_reference_range` | Numeric lower bound extracted from the reference range, when available.                                            |
| `max_reference_range` | Numeric upper bound extracted from the reference range, when available.                                            |
| `source_panel_name`   | Panel name associated with the result, when available.                                                             |
| `sample_type`         | Specimen type, such as `serum_plasma_blood`, `urine`, `saliva`, `stool`, `capillary_blood`, `other`, or `unknown`. |
| `measurement_kind`    | Whether the result appears to be `direct`, `calculated`, `ratio`, or `unknown`.                                    |
| `sensitivity`         | Sensitivity classification when the report indicates it.                                                           |
| `interpretation`      | Parsed or inferred interpretation. Possible values are `normal`, `abnormal`, `critical`, or `unknown`.             |
| `is_above_max_range`  | Whether the result is above `max_reference_range`, when this can be determined.                                    |
| `is_below_min_range`  | Whether the result is below `min_reference_range`, when this can be determined.                                    |
| `loinc_match_status`  | LOINC matching state: `auto_match`, `needs_review`, or `no_match`.                                                 |
| `loinc_matches`       | Candidate LOINC matches with confidence scores.                                                                    |

## Value and Type

`data.results[].value` is always returned as a string. Do not assume it can always be parsed as a number.

For `type: "numeric"`, `value` should be a number encoded as a string, such as `"5"` or `"5.0"`. For other result types, `value` can contain comparators, text, boolean-like values, durations, percentages, or ratios. Use `type` before deciding how to parse or display the value.

| Type         | Example `value`                                                  | Notes                                                                                                                                                                  |
| ------------ | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `numeric`    | `"90"`, `"1e-3"`                                                 | Numeric result encoded as a string. Use `units` for measurement units.                                                                                                 |
| `range`      | `"<5"`, `">=10"`, `"≤1e-3"`, `"3-7"`                             | May include comparators, approximate values, scientific notation, or lower-to-upper range notation. Do not parse as a plain float.                                     |
| `comment`    | `"Positive"`, `"See note"`                                       | Textual result or observation.                                                                                                                                         |
| `boolean`    | `"true"`, `"false"`, `"yes"`, `"no"`, `"positive"`, `"negative"` | Boolean or presence-style findings can vary by report wording.                                                                                                         |
| `duration`   | `"3h"`, `"12 min"`, `"10:00"`                                    | Duration-like values are preserved as strings because reports can use different formats.                                                                               |
| `percentage` | `"5.4%"`                                                         | Inferred when the value includes `%`. If the value is `"5.4"` and `units` is `%`, the result may be returned as `numeric`; read `value`, `type`, and `units` together. |
| `ratio`      | `"97/100"`                                                       | Slash-form ratios are inferred when `type` is not already supplied. Other ratio-like formats, such as colon-form titers, may be returned as `comment`.                 |

<Warning>
  The parser output is optimized to preserve what was reported. Store the raw `value` string and derive typed values in your application only after checking `type`, `units`, and reference range fields.
</Warning>

The parser-specific `type` values are related to, but not identical to, the order result `ResultType` values documented in [Result Formats](/lab/results/result-formats#resulttype). Parser results currently include `numeric`, `range`, `comment`, `boolean`, `duration`, `percentage`, and `ratio`.

When `type` is not supplied by extraction, Junction infers it from `value`. For example, `"<50"`, `"≥2000"`, and `"10-20"` are inferred as `range`; `"20%"` is inferred as `percentage`; `"97/100"` is inferred as `ratio`; and unrecognized text is inferred as `comment`.

## Reference Ranges and Interpretation

The parser may extract `min_reference_range` and `max_reference_range` as numeric bounds when the report includes a parseable reference range. It may also return:

* `interpretation`: possible values are `normal`, `abnormal`, `critical`, or `unknown`.
* `is_above_max_range`: whether the result is above the extracted maximum
* `is_below_min_range`: whether the result is below the extracted minimum

These fields depend on the quality and structure of the source report. Some reports include clear numeric bounds; others include textual ranges, age-specific ranges, sex-specific ranges, comments, or formatting that cannot be normalized into numeric bounds.

For numeric values, Junction can infer whether the result is above or below the extracted numeric reference bounds. For comparator range values, Junction only sets range flags when the comparator is conclusive. For example, `">2000"` with a max reference range of `1100` is above range, but `">500"` with the same max is inconclusive.

If a value is conclusively outside the extracted bounds, `interpretation` is `abnormal`. Numeric values without an out-of-range flag are interpreted as `normal`. Non-numeric and inconclusive range values are interpreted as `unknown` unless the parser extracted a more specific interpretation.

If you need custom boundary logic, read the result `value`, `type`, `units`, and reference range fields together. For general order-result reference range guidance, see [Reference Range](/lab/results/reference-range-parsing).

## LOINC Matching

Junction attempts to match extracted results to LOINC codes so you can compare markers across different labs and report formats.

Each `loinc_matches[]` item includes:

| Field              | Description                                |
| ------------------ | ------------------------------------------ |
| `loinc_code`       | LOINC code, such as `2345-7`.              |
| `loinc_name`       | Official or normalized LOINC name.         |
| `display_name`     | Display label for the match.               |
| `aliases`          | Alternate names associated with the match. |
| `confidence_score` | Relative score for this LOINC candidate.   |

`confidence_score` is a matching score for the candidate LOINC code. It is not an accuracy score for the extracted lab result, patient metadata, units, reference ranges, or interpretation. A high score means Junction's LOINC matcher found a stronger candidate for that result row than lower-scored candidates; it does not prove that the source document was parsed correctly.

In most integrations, use `loinc_match_status` for workflow decisions instead of building your own thresholds on `confidence_score`. Store the score for debugging, audit, or support workflows, but avoid using it as a clinical-confidence or result-accuracy signal.

Use `loinc_match_status` to decide how much review your workflow needs:

| Status         | Meaning                                                     |
| -------------- | ----------------------------------------------------------- |
| `auto_match`   | Junction found a likely LOINC match.                        |
| `needs_review` | Junction found possible matches, but review is recommended. |
| `no_match`     | Junction could not identify a match.                        |

<Warning>
  LOINC matches are not guaranteed for every extracted result. Your integration should handle `loinc_matches: null`, an empty match list, low confidence scores, `needs_review`, `no_match`, and future match statuses.
</Warning>

## Webhooks

Subscribe to parser events to avoid polling.

| Event                            | Trigger                                                        |
| -------------------------------- | -------------------------------------------------------------- |
| `lab_report.parsing_job.created` | A new parsing job was created.                                 |
| `lab_report.parsing_job.updated` | A parsing job changed status, including completion or failure. |

Webhook payloads include the user, team, and parsing job:

```json Webhook payload theme={null}
{
  "event_type": "lab_report.parsing_job.updated",
  "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "client_user_id": "7cbd6f62-0d22-4e5f-b7fd-bc4ee5c3fd8d",
  "team_id": "6353bcab-3526-4838-8c92-063fa760fb6b",
  "data": {
    "id": "8eb0217f-4683-4a3c-adca-faf95ac65739",
    "status": "completed",
    "failure_reason": null,
    "data": {
      "metadata": {
        "patient_first_name": "Jane",
        "patient_last_name": "Doe",
        "dob": "1990-01-01",
        "gender": "female",
        "lab_name": "Acme Labs",
        "date_reported": "2025-01-01",
        "date_collected": "2024-12-30",
        "specimen_number": "ABC123"
      },
      "results": [
        {
          "test_name": "Glucose",
          "value": "90",
          "type": "numeric",
          "units": "mg/dL",
          "min_reference_range": 70,
          "max_reference_range": 99,
          "loinc_match_status": "auto_match",
          "loinc_matches": [
            {
              "loinc_code": "2345-7",
              "loinc_name": "Glucose [Mass/volume] in Serum or Plasma",
              "display_name": "Glucose",
              "aliases": [],
              "confidence_score": 0.99
            }
          ],
          "interpretation": "normal",
          "is_above_max_range": false,
          "is_below_min_range": false
        }
      ]
    },
    "needs_human_review": false,
    "is_reviewed": false
  }
}
```

For webhook delivery behavior, retries, and event structure, see [Webhooks](/webhooks/introduction).

## Sandbox Limits

Sandbox lab report parsing has a team-level report limit. The error response includes the configured limit for your team. For example, a team limited to 150 reports receives:

```json Error theme={null}
{
  "detail": "Sandbox lab report parsing is limited to 150 reports. Please upgrade your contract to continue."
}
```

This is a sandbox usage limit, not a file-level validation error. Retrying the same request, creating more users, or changing the report file does not reset the limit. Contact your account manager or [support@junction.com](mailto:support@junction.com) if you need the limit increased for higher-volume testing or production access.

## Integration Guidance

Build your parser integration defensively:

* Keep the original report file or a pointer to it in your system for audit and reprocessing workflows.
* Store `data.results[].value` as a string, even when `type` is `numeric`.
* Treat parser enums as non-exhaustive and log unknown `status`, `type`, `failure_reason`, `sample_type`, `measurement_kind`, and `loinc_match_status` values.
* Do not require LOINC matches to be present before displaying the extracted result to users.
* Review low-confidence or `needs_review` LOINC matches before using them for clinical decisioning, cohort logic, or automated recommendations.
* Expect null metadata and null reference range fields when the source report does not include parseable values.
* Use webhooks for normal processing and keep polling as a fallback for missed events or manual support flows.

## API Reference

* [Create Lab Report Parser Job](/api-reference/lab-testing/lab-report-parsing/post-lab-report-parser-job)
* [Get Lab Report Parser Job](/api-reference/lab-testing/lab-report-parsing/get-lab-report-parser-job)
* [Lab Report Parsing Job Created](/event-catalog/lab_report.parsing_job.created)
* [Lab Report Parsing Job Updated](/event-catalog/lab_report.parsing_job.updated)


# Critical Results
Source: https://docs.junction.com/lab/results/critical-results

Understand how Junction categorizes lab result markers as normal, abnormal, or critical, and receive webhook alerts for critical findings.

When our Lab partners return results to Junction, they may return specific interpretations for each Marker:

* `normal`, the marker value is inside the lab reference range.
* `abnormal`, the marker value is outside the lab reference range.
* `critical`, the marker value falls in the critical range for that marker, which can be life-threatening.

These values are returned in an `interpretation` field for results, both in each individual marker and the overall results object:

```json Results Object theme={null}
{
  "metadata": {
    "age": 19,
    "dob": "18/08/1993",
    "clia_number": "12331231",
    "patient": "Bob Smith",
    "provider": "Dr. Jack Smith",
    "laboratory": "LabCorp",
    "date_reported": "2020-01-01",
    "date_collected": "2022-02-02",
    "specimen_number": "123131",
    "date_received": "2022-01-01",
    "status": "final",
    "interpretation": "critical"
  },
  "results": [
    {
      "name": "Hemoglobin",
      "slug": "hemoglobin",
      "unit": "g/dL",
      "notes": null,
      "value": 100.2,
      "timestamp": null,
      "max_range_value": 17.7,
      "min_range_value": 13.0,
      "is_above_max_range": true,
      "is_below_min_range": false,
      "interpretation": "critical"
    },
    {
      "name": "Sodium",
      "slug": "unknown",
      "unit": "mmol/L",
      "notes": null,
      "value": 136,
      "timestamp": null,
      "max_range_value": 144,
      "min_range_value": 134,
      "is_above_max_range": false,
      "is_below_min_range": false,
      "interpretation": "normal"
    }
  ]
}
```

Besides the `interpretation` field, there is also the `status` field, which can assume the values of `final` or `partial`, as critical results are reported as soon as they're available; not all tests are guaranteed to have been processed by the lab.

For critical results, the laboratory will contact a physician, so they can provide further information to the patient. The physician called is the Junction-assigned physician. They act as the ordering physician when placing the order. After the call is made, and Junction has received the results from the laboratory, a `labtest.result.critical` event is sent. The webhook payload contains the following information:

```json Critical Results Webhook Payload theme={null}
{
  "data": {
    "created_at": "2023-09-04T19:02:33.042533+00:00",
    "updated_at": "2023-09-04T19:02:47.106778+00:00",
    "order_id": "93ae03a7-626f-4cf4-802b-1fb6b3c339c7",
    "sample_id": "W4123LKB3VTL",
    "status": "final",
    "interpretation": "critical",
    "team_id": "482gh249-cd9d-4049-bb42-db5682cdf1d2",
    "user_id": "db5d34d5-bf7b-4e25-b119-9asd53694f45"
  },
  "event_type": "labtest.result.critical"
}
```

After you receive the webhook, you can use the [`GET /order/{order_id}/result`](/api-reference/lab-testing/results/get-results) endpoint to fetch the raw results.

You can test the webhook event by using the [Junction Dashboard](https://app.junction.com) Webhooks section,
where you can register an endpoint and subscribe to the `labtest.result.critical` event.
The following image shows what the screen should look like after you have an endpoint registered
and have selected the `Testing` section inside the endpoint configurations.

<img />

You can then click the `Send Example` button and should receive an HTTP call in the registered URL.


# Follow-ups
Source: https://docs.junction.com/lab/results/follow-up

Learn how Junction's physician network handles follow-up communications with patients who receive abnormal lab test results.

When the test results are ready, you will receive a `labtest.order.updated` webhook where the order has a `completed` status. That means the results are ready and can be [fetched](/lab/results/result-formats).

In case of abnormal results, our physician will determine if a call with the patient is required, and if so, they will follow up to figure out the next steps. Please keep in mind that the follow-up call with the patient only happens for orders processed by our physician network. If you provided your own physician when you placed the order, you will be responsible for following up with your patients.


# Galleri Multi-Cancer Early Detection Results (GRAIL)
Source: https://docs.junction.com/lab/results/galleri-results

Understand the unique result structure for the Galleri multi-cancer early detection test, which returns coded enum values instead of numeric biomarkers.

## Overview

The Galleri test (by GRAIL) is a multi-cancer early detection blood test available exclusively through the Quest lab provider. Unlike standard lab biomarkers that return numeric values with reference ranges, Galleri results use coded values (enums) to indicate cancer detection status, predicted cancer signal origin, and anatomical subcategories.

## Result Structure Comparison

### Standard Numeric Biomarker Result

Typical lab results for biomarkers like cholesterol follow this structure:

```json theme={null}
{
  "name": "Total Cholesterol",
  "slug": "total-cholesterol",
  "type": "numeric",
  "result": "195",
  "value": 195,
  "unit": "mg/dL",
  "reference_range": "< 200",
  "min_range_value": null,
  "max_range_value": 200,
  "is_above_max_range": false,
  "is_below_min_range": false,
  "interpretation": "normal",
  "timestamp": "2024-01-15T10:30:00Z",
  "loinc": "2093-3",
  "loinc_slug": "cholesterol-total-serum-plasma",
  "provider_id": "001347",
  "notes": null,
  "source_sample_id": "12345678"
}
```

Notice the `type` is `"numeric"`, the `result` contains a numeric string, `value` contains the actual number, and `unit` specifies the measurement unit.

### Galleri Result - No Cancer Signal Detected

When no cancer signal is detected, you receive a single result:

```json theme={null}
{
  "name": "Galleri Multi-Cancer Early Detection",
  "slug": "galleri-cancer-signal",
  "type": "coded_value",
  "result": "NOT_DETECTED",
  "value": -1,
  "unit": null,
  "reference_range": null,
  "min_range_value": null,
  "max_range_value": null,
  "is_above_max_range": null,
  "is_below_min_range": null,
  "interpretation": "normal",
  "timestamp": "2024-01-15T10:30:00Z",
  "loinc": null,
  "loinc_slug": null,
  "provider_id": "86038987",
  "notes": null,
  "source_sample_id": "12345678"
}
```

### Galleri Results - Cancer Signal Detected

When a cancer signal is detected, you receive **multiple related biomarker results** that provide detailed information about the detection:

#### 1. Cancer Signal Detection

```json theme={null}
{
  "name": "Galleri Cancer Signal",
  "slug": "galleri-cancer-signal",
  "type": "coded_value",
  "result": "DETECTED",
  "value": -1,
  "unit": null,
  "reference_range": null,
  "min_range_value": null,
  "max_range_value": null,
  "is_above_max_range": null,
  "is_below_min_range": null,
  "interpretation": "critical",
  "timestamp": "2024-01-15T10:30:00Z",
  "loinc": null,
  "loinc_slug": null,
  "provider_id": "86038987",
  "notes": null,
  "source_sample_id": "12345678"
}
```

#### 2. Cancer Signal Origin

```json theme={null}
{
  "name": "Galleri Cancer Signal Origin",
  "slug": "galleri-cancer-origin",
  "type": "coded_value",
  "result": "HEAD_AND_NECK",
  "value": -1,
  "unit": null,
  "reference_range": null,
  "min_range_value": null,
  "max_range_value": null,
  "is_above_max_range": null,
  "is_below_min_range": null,
  "interpretation": "critical",
  "timestamp": "2024-01-15T10:30:00Z",
  "loinc": null,
  "loinc_slug": null,
  "provider_id": "86038987_1",
  "notes": null,
  "source_sample_id": "12345678"
}
```

#### 3. Cancer Signal Subcategory

```json theme={null}
{
  "name": "Galleri Cancer Signal Subcategory",
  "slug": "galleri-cancer-subcategory",
  "type": "coded_value",
  "result": "OROPHARYNX_HYPOPHARYNX_NASOPHARYNX_LARYNX_LIP_AND_ORAL_CAVITY_INCLUDING_ORAL_TONGUE_NASAL_CAVITY_PARANASAL_SINUSES_MAJOR_SALIVARY_GLANDS",
  "value": -1,
  "unit": null,
  "reference_range": null,
  "min_range_value": null,
  "max_range_value": null,
  "is_above_max_range": null,
  "is_below_min_range": null,
  "interpretation": "critical",
  "timestamp": "2024-01-15T10:30:00Z",
  "loinc": null,
  "loinc_slug": null,
  "provider_id": "86038987_2",
  "notes": null,
  "source_sample_id": "12345678"
}
```

#### 4. Additional Information (Comment)

```json theme={null}
{
  "name": "Galleri Additional Information",
  "slug": "galleri-comment-1",
  "type": "comment",
  "result": "Squamous Cell Signal of Head and Neck, Lung, and Esophagus",
  "value": -1,
  "unit": null,
  "reference_range": null,
  "min_range_value": null,
  "max_range_value": null,
  "is_above_max_range": null,
  "is_below_min_range": null,
  "interpretation": "critical",
  "timestamp": "2024-01-15T10:30:00Z",
  "loinc": null,
  "loinc_slug": null,
  "provider_id": "86038987_3",
  "notes": "Squamous Cell Signal of Head and Neck, Lung, and Esophagus",
  "source_sample_id": "12345678"
}
```

## Key Differences from Standard Biomarkers

Understanding these differences is crucial for proper integration:

### Result Type

* **Standard biomarkers**: `type` is `"numeric"` or `"range"`
* **Galleri results**: `type` is `"coded_value"` or `"comment"`

### Result Field

* **Standard biomarkers**: Contains numeric values as strings (e.g., `"195"`, `"<1.2"`)
* **Galleri results**: Contains enum constant names in uppercase with underscores (e.g., `"DETECTED"`, `"HEAD_AND_NECK"`)

### Value Field

* **Standard biomarkers**: Contains actual numeric value (e.g., `195`)
* **Galleri results**: Always `-1` (placeholder value; actual information is in the `result` field)

<Warning>
  The `value` field is deprecated and will eventually be removed. For Galleri results, always use the `result` field to extract meaningful information.
</Warning>

### Unit Field

* **Standard biomarkers**: Contains measurement units (e.g., `"mg/dL"`, `"mmol/L"`)
* **Galleri results**: Always `null`

### Interpretation Field

* **Standard biomarkers**: Can be `"normal"`, `"abnormal"`, or `"critical"` based on reference ranges
* **Galleri results**: `"normal"` when not detected, `"critical"` when cancer signal detected

### Result Multiplicity

* **Standard biomarkers**: Typically one result per biomarker tested
* **Galleri positive results**: Multiple related results (signal + origin + subcategory + comments)

### Provider ID Pattern

Galleri results use a consistent `provider_id` pattern to distinguish between the different related results:

* `"86038987"` - Base cancer signal detection result
* `"86038987_1"` - Cancer signal origin
* `"86038987_2"` - Cancer signal subcategory
* `"86038987_3"` - Additional information/comments

## Enum Value Reference

### Galleri Cancer Signal

**Description**: Indicates whether a cancer signal was detected in the Galleri test.

**Field**: `BiomarkerResult.result` when `type` is `"coded_value"` and `provider_id` is `"86038987"`

**Possible Values**:

```
DETECTED
NOT_DETECTED
```

### Galleri Cancer Origin

**Description**: When a cancer signal is detected, indicates the predicted tissue of origin for the cancer signal. This represents the general anatomical location or cell lineage where the cancer may have originated.

**Field**: `BiomarkerResult.result` when `type` is `"coded_value"` and `provider_id` is `"86038987_1"`

**Possible Values** (23 total):

```
ANUS
BLADDER_UROTHELIAL_TRACT
BREAST
CERVIX
COLON_RECTUM
HEAD_AND_NECK
HEMATOPOIETIC_AND_LYMPHOID_ORGANS
KIDNEY
LIVER_BILE_DUCT
LUNG
MELANOCYTE_CONTAINING_TISSUES_SKIN
NEUROENDOCRINE_CELLS_OF_LUNG_OR_OTHER_ORGANS
LYMPHOID_LINEAGE
MELANOCYTIC_LINEAGE
MYELOID_LINEAGE
OVARY
PANCREAS_GALLBLADDER
PLASMA_CELL_LINEAGE
PROSTATE
BONE_AND_SOFT_TISSUE
THYROID_GLAND
STOMACH_ESOPHAGUS
UTERUS
```

### Galleri Cancer Subcategory

**Description**: Provides a more specific anatomical classification of the predicted cancer signal origin, with additional detail about the specific organs or tissue types involved.

**Field**: `BiomarkerResult.result` when `type` is `"coded_value"` and `provider_id` is `"86038987_2"`

**Possible Values** (23 total):

```
ANUS
BLADDER_RENAL_PELVIS_URETER_URETHRA
BREAST
CERVIX
COLON_RECTUM_APPENDIX
OROPHARYNX_HYPOPHARYNX_NASOPHARYNX_LARYNX_LIP_AND_ORAL_CAVITY_INCLUDING_ORAL_TONGUE_NASAL_CAVITY_PARANASAL_SINUSES_MAJOR_SALIVARY_GLANDS
BONE_MARROW_PRIMARY_AND_SECONDARY_LYMPHOID_TISSUE_LYMPH_NODES_EXTRANODAL_LYMPHOID_TISSUE_SPLEEN_THYMUS
KIDNEY
LIVER_INTRAHEPATIC_BILE_DUCT
LUNG_BRONCHUS
MELANOCYTE_CONTAINING_TISSUES_SKIN_UVEA_AND_OTHER_ORGANS_SKIN_AND_MUCOSAL_TISSUE
NEUROENDOCRINE_CELLS_OF_LUNG_OR_OTHER_ORGANS
LYMPHOID_LINEAGE
MELANOCYTIC_LINEAGE
MYELOID_LINEAGE
OVARY_FALLOPIAN_TUBE_PRIMARY_PERITONEUM
PANCREAS_EXTRAHEPATIC_BILE_DUCT_GALLBLADDER
PLASMA_CELL_LINEAGE
PROSTATE
SKELETAL_MUSCLE_AND_OTHER_CONNECTIVE_TISSUE_VASCULAR_TISSUE_BONE_AND_CARTILAGE
THYROID_GLAND
STOMACH_ESOPHAGUS
UTERUS
```

## Integration Tips

### Identifying Galleri Results

Galleri results can be identified in the API response by:

1. The `type` field will be `"coded_value"` or `"comment"`
2. The `provider_id` will start with `"86038987"`
3. The slug values will be: `"galleri-cancer-signal"`, `"galleri-cancer-origin"`, or `"galleri-cancer-subcategory"`

### Handling Multiple Related Results

When a cancer signal is detected, you will receive multiple related biomarker results that should be grouped and displayed together. The `provider_id` field distinguishes between them:

* `"86038987"` - The main cancer signal detection result
* `"86038987_1"` - The predicted origin of the cancer signal
* `"86038987_2"` - The specific subcategory with detailed anatomical information
* `"86038987_3"`, `"86038987_4"`, etc. - Additional information and comments


# Reference Range
Source: https://docs.junction.com/lab/results/reference-range-parsing

Parse the reference_range string field in lab results to determine boundary inclusivity and build custom result interpretation logic.

<Note>
  The Junction API already returns the `interpretation` field (e.g., "normal", "abnormal", "critical") in the [`BiomarkerResult`](/lab/results/result-formats#biomarkerresult) object. This documentation is intended for teams that want to build their own interpretation logic and need to parse the `reference_range` field along with `min_range_value` and `max_range_value` to determine whether results are within normal limits.
</Note>

The `reference_range` field in Order results contains a string representation of the normal range for a lab test result. This field is critical for determining whether a result is within normal limits, but it requires parsing to understand the inclusivity/exclusivity of boundary values.

## Why Parsing is Needed

The `min_range_value` and `max_range_value` fields contain the boundary numbers from the lab, but they **do not indicate whether those boundaries are inclusive or exclusive**. This information is only available in the `reference_range` string, which requires parsing to determine the correct comparison operator.

### Example

For a result with:

* `reference_range`: `"<5.7"`
* `max_range_value`: `5.7`
* `min_range_value`: None
* `result`: `"5.7"`

Without knowing the inclusivity, you cannot determine if `5.7` is normal or abnormal. The boundary value `5.7` is the same whether the range is `<=5.7` (inclusive, normal) or `<5.7` (exclusive, abnormal). You must parse the `reference_range` string to know which operator to use.

## Reference Range Formats

The `reference_range` field can appear in several formats:

### 1. Comparison Operators

Single-bound comparisons using standard operators:

* `<5` - Less than 5 (exclusive upper bound)
* `<=10.5` - Less than or equal to 10.5 (inclusive upper bound)
* `>3` - Greater than 3 (exclusive lower bound)
* `>=7.25` - Greater than or equal to 7.25 (inclusive lower bound)

### 2. Range Notation

Two-bound ranges using hyphen notation:

* `1-10` - Between 1 and 10 (both inclusive)
* `3.5-7.8` - Between 3.5 and 7.8 (both inclusive)

**Note:** Range notation (with hyphen) typically indicates both bounds are inclusive.

### 3. Alternative Comparison Syntax

Some labs use alternative syntax:

* `< OR = 9.9` - Less than or equal to 9.9 (inclusive upper bound)
* `> OR = 2` - Greater than or equal to 2 (inclusive lower bound)

These are case-insensitive (`< or = 9.9` is equivalent).

## Parsing Strategy

Parse the `reference_range` string to extract:

* Lower bound value (if present)
* Upper bound value (if present)
* Whether each bound is inclusive or exclusive

The parsed information, combined with `min_range_value` and `max_range_value`, determines the correct comparison operators for evaluating whether results are within the normal range.

## Example Implementation

Here's an example implementation showing how to parse the `reference_range` field:

<Note>
  This example uses Python's `re` module for regular expression matching.
</Note>

```python theme={null}
import re

def parse_reference_range(reference_range_string):
    # Pattern 1: Handle comparison operators: <5, <=10.5, >3, >=7.25
    comparison_match = re.match(r"([<>]=?)(\d+(\.\d+)?)", reference_range_string)
    if comparison_match:
        comparison_operator, boundary_value, _ = comparison_match.groups()
        boundary_value_float = float(boundary_value)
        
        if comparison_operator in ["<", "<="]:
            # Upper bound: < or <=
            return {
                "lower_bound": None,
                "upper_bound": boundary_value_float,
                "lower_included": False,
                "upper_included": comparison_operator == "<=",  # <= is inclusive, < is exclusive
            }
        else:  # > or >=
            # Lower bound: > or >=
            return {
                "lower_bound": boundary_value_float,
                "upper_bound": None,
                "lower_included": comparison_operator == ">=",  # >= is inclusive, > is exclusive
                "upper_included": False,
            }
    
    # Pattern 2: Handle range notation: 1-10, 3.5-7.8
    range_match = re.match(r"(\d+(\.\d+)?)-(\d+(\.\d+)?)", reference_range_string)
    if range_match:
        lower_bound_str, _, upper_bound_str, _ = range_match.groups()
        return {
            "lower_bound": float(lower_bound_str),
            "upper_bound": float(upper_bound_str),
            "lower_included": True,  # Range notation is always inclusive on both sides
            "upper_included": True,
        }
    
    # Pattern 3: Handle alternative comparison syntax: < OR = 9.9, > OR = 2
    alternative_match = re.match(r"([<>]) OR = (\d+(\.\d+)?)", reference_range_string, re.IGNORECASE)
    if alternative_match:
        comparison_operator, boundary_value, _ = alternative_match.groups()
        boundary_value_float = float(boundary_value)
        
        if comparison_operator == "<":
            # < OR = means <= (inclusive upper bound)
            return {
                "lower_bound": None,
                "upper_bound": boundary_value_float,
                "lower_included": False,
                "upper_included": True,
            }
        else:  # >
            # > OR = means >= (inclusive lower bound)
            return {
                "lower_bound": boundary_value_float,
                "upper_bound": None,
                "lower_included": True,
                "upper_included": False,
            }
    
    # If no pattern matches, return empty bounds
    return {
        "lower_bound": None,
        "upper_bound": None,
        "lower_included": False,
        "upper_included": False,
    }
```

<Warning>
  This code should be used as a starting point and not directly used in production. It demonstrates parsing logic for the formats shown in this documentation, but may not handle all edge cases or variations that labs might use. For example, it does not cover signed ranges (e.g., `-5.0 - +2.0`) or other format variations. You should thoroughly test and extend this implementation based on your specific needs.
</Warning>

## Parsing Examples

Here are examples of what the above parsing function returns for different `reference_range` formats:

### Example 1: Exclusive Upper Bound

```python theme={null}
reference_range_string = "<5.7"
parsed = parse_reference_range(reference_range_string)
# Returns: {
#     "lower_bound": None,
#     "upper_bound": 5.7,
#     "lower_included": False,
#     "upper_included": False  # < means exclusive
# }
```

### Example 2: Inclusive Upper Bound

```python theme={null}
reference_range_string = "<=5.7"
parsed = parse_reference_range(reference_range_string)
# Returns: {
#     "lower_bound": None,
#     "upper_bound": 5.7,
#     "lower_included": False,
#     "upper_included": True  # <= means inclusive
# }
```

### Example 3: Range with Both Bounds

```python theme={null}
reference_range_string = "2.6-24.9"
parsed = parse_reference_range(reference_range_string)
# Returns: {
#     "lower_bound": 2.6,
#     "upper_bound": 24.9,
#     "lower_included": True,  # Range notation is inclusive
#     "upper_included": True
# }
```

### Example 4: Lower Bound Only

```python theme={null}
reference_range_string = ">3.0"
parsed = parse_reference_range(reference_range_string)
# Returns: {
#     "lower_bound": 3.0,
#     "upper_bound": None,
#     "lower_included": False,  # > means exclusive
#     "upper_included": False
# }
```

### Example 5: Alternative Syntax

```python theme={null}
reference_range_string = "< OR = 9.9"
parsed = parse_reference_range(reference_range_string)
# Returns: {
#     "lower_bound": None,
#     "upper_bound": 9.9,
#     "lower_included": False,
#     "upper_included": True  # < OR = means <= (inclusive)
# }
```


# Result Formats
Source: https://docs.junction.com/lab/results/result-formats

Retrieve lab test results in two formats: raw PDF reports from partner labs and structured JSON data through the Junction API.

Junction's API returns results in two different formats.

* `PDF`
* `JSON`

## PDF Results

We return the raw results in PDF form that we receive directly from our partner labs. This can be retrieved as follows:

```bash Get order results PDF theme={null}
curl --request GET \
     --url {{BASE_URL}}/v3/order/{order_id}/result/pdf \
     --header 'Accept: application/json' \
     --header 'Content-Type: application/pdf' \
     --header 'x-vital-api-key: <your_api_key>'
```

<Note>
  Some laboratories do not issue PDFs for partial results, and only provide PDF reports once all results are finalized.
</Note>

An example result:

<img />

## JSON Results

We also return the parsed results in JSON format, so you can use them to generate your own forms.

These results are returned in a structured format, which you can find [here](/api-reference/lab-testing/results/get-results).

<Warning>The `results` field, according to the spec, can return either a `list[BiomarkerResult]` or an untyped `dict`. This is due to backwards compatibility, and you can disregard the untyped `dict`.</Warning>

### Result Status

The `status` field can be one of the following:

1. `ResultStatus.PARTIAL` - The results are partial.
   Labs can return results before all biomarkers are available.
   Junction makes these results available to you as soon as we receive them, but does not send a webhook notification for `partial` results.

   This means that if you probe the API for results, you might get a `partial` result, even if there was no webhook for a `labtest.order.updated` event.

   This is done due to the possibility of [critical values](/lab/results/critical-results) in the results.

2. `ResultStatus.FINAL` - The results are complete.
   This means that all biomarkers are available, and the results are final. You will receive a `labtest.order.updated` webhook notification for this event.

### BiomarkerResult

A `BiomarkerResult` has the following definition:

```python theme={null}
name: str
slug: str
value: float  # deprecated
result: str
type: ResultType
unit: str | None
timestamp: datetime | None
notes: str | None
reference_range: str | None
min_range_value: float | None
max_range_value: float | None
is_above_max_range: bool | None
is_below_min_range: bool | None
interpretation: str = Interpretation.NORMAL
loinc: str | None
loinc_slug: str | None
provider_id: str | None
source_markers: List[ParentBiomarkerData] | None 
```

#### ResultType

Results can fall into one of the following categories:

1. `ResultType.NUMERIC` - A numeric result, e.g., `1.2`

   In this case, the `result` field will be a string representation of the number, and the `value` field will be a float representation of the number.

2. `ResultType.RANGE` - A range result, e.g., `<1.2`

   In this case, the `result` field will be a string representation of the range value, and the `value` field will be `-1`.

   Note that you will also find the `<1.2` value in the `notes` field.

   A range result will always be a value following the pattern `^([<>]=?\d*(\.\d+)?|(\d*(\.\d+)?-\d*(\.\d+)?))$`.

3. `ResultType.COMMENT` - A text result, e.g., `Positive`

   In this case, the `result` field will be a string representation of the text, and the `value` field will be `-1`.

   Note that you will also find the `Positive` value in the `notes` field.

4. `ResultType.CODED_VALUE` - A coded value result using enum constants, e.g., `DETECTED`, `HEAD_AND_NECK`

   In this case, the `result` field will be an enum constant name, and the `value` field will be `-1`.

   This type is used for specialized tests like the Galleri multi-cancer early detection test. See the [Galleri Results](/lab/results/galleri-results) documentation for detailed information.

<Warning>The `value` field is deprecated and will eventually be removed.</Warning>

#### Interpretation

Interpretation is a string value that can be one of the following:

1. `Interpretation.NORMAL` - The result is within normal parameters.
2. `Interpretation.ABNORMAL` - The result is outside of normal parameters.
3. `Interpretation.CRITICAL` - The result is outside of critical parameters.
   In this case, refer to the [critical values](/lab/results/critical-results) section.

#### Standardization - LOINC

It's possible to test the same biomarkers across different laboratories. For these to match, we use the [LOINC](https://loinc.org/) standard.

In the `BiomarkerResult` object, you can see two fields `loinc_slug` and `loinc`. These fields refer to the LOINC standard. Customers should use this standard, so it's possible to match results across different laboratories.
You can expect that the `slug` field is what the laboratory returns to us - and the `loinc_slug` is the standardized version.

An example:

| Lab     | Slug            | LOINC  | LOINC Slug                  |
| ------- | --------------- | ------ | --------------------------- |
| Labcorp | hdl-cholesterol | 2085-9 | cholesterol-in-hdl-mass-vol |
| USSL    | hdl             | 2085-9 | cholesterol-in-hdl-mass-vol |

As you can see, the same biomarker `HDL Cholesterol` can have different slugs across different laboratories. However, it's represented by the same LOINC value.

<Warning>
  LOINC codes may be missing in some results due to our LOINC compendium still being expanded and updated, labs providing data in formats our system does not recognize, or labs not including LOINC codes in their data at all. We are actively improving both the completeness of our compendium and our ability to interpret data variations, but missing LOINCs can still occur. Integrations should be built to handle cases where LOINC codes are not guaranteed.
</Warning>

#### Expected Results

When ordering a `lab_test`, you can see which `markers` each test orders. These can either be `panels` composed of multiple `biomarkers` or just individual `biomarkers`.

This means that a `lab_test` with only one associated `marker`, such as `Lipid Panel`, can return multiple `result markers`. We call these expected results.
Each `marker` can thus be composed of multiple `expected results` which match to a `loinc`.

As an example, here's the expected results for the `Lipid Panel` marker:

```json theme={null}
"expected_results":[
   {
      "id":1108,
      "name":"VLDL Cholesterol Cal",
      "slug":"vldl-cholesterol-cal",
      "lab_id":6,
      "provider_id":"011919",
      "loinc":{
         "id":5062,
         "name":"Cholesterol in VLDL Calc [Mass/Vol]",
         "slug":"cholesterol-in-vldl-calc-mass-vol",
         "code":"13458-5",
         "unit":"mg/dL"
      }
   },
   {
      "id":1109,
      "name":"Cholesterol, Total",
      "slug":"cholesterol-total",
      "lab_id":6,
      "provider_id":"001065",
      "loinc":{
         "id":11940,
         "name":"Cholesterol [Mass/Vol]",
         "slug":"cholesterol-mass-vol",
         "code":"2093-3",
         "unit":"mg/dL"
      }
   },
   {
      "id":1110,
      "name":"HDL Cholesterol",
      "slug":"hdl-cholesterol",
      "lab_id":6,
      "provider_id":"011817",
      "loinc":{
         "id":11858,
         "name":"Cholesterol in HDL [Mass/Vol]",
         "slug":"cholesterol-in-hdl-mass-vol",
         "code":"2085-9",
         "unit":"mg/dL"
      }
   },
   {
      "id":1112,
      "name":"Triglycerides",
      "slug":"triglycerides",
      "lab_id":6,
      "provider_id":"001172",
      "loinc":{
         "id":16384,
         "name":"Triglyceride [Mass/Vol]",
         "slug":"triglyceride-mass-vol",
         "code":"2571-8",
         "unit":"mg/dL"
      }
   },
   {
      "id":1113,
      "name":"LDL Chol Calc (NIH)",
      "slug":"ldl-chol-calc-nih",
      "lab_id":6,
      "provider_id":"012059",
      "loinc":{
         "id":5060,
         "name":"Cholesterol in LDL Calc [Mass/Vol]",
         "slug":"cholesterol-in-ldl-calc-mass-vol",
         "code":"13457-7",
         "unit":"mg/dL"
      }
   }
]
```

You can use this information to verify if the final results are composed of all expected results.

In order to obtain this data, you can use the following endpoints:

1. [GET /v3/lab\_tests/markers](/api-reference/lab-testing/biomarkers)

   This allows you to search markers based on laboratory or name.

2. [GET /v3/lab\_tests/\{id}/markers](/api-reference/lab-testing/lab-test-markers)

   This allows you to see all markers associated with a lab test and its expected results.

#### Source Markers

As mentioned above, a marker can be composed of one or more results. This means that if you order a `Lipid Panel`, there will be no `Lipid Panel` result returned, but instead a series of markers that originate from the `Lipid Panel`.

Junction identifies the source marker via the `source_markers` field.

```json theme={null}
    {
      "name": "Sex Horm Binding Glob, Serum",
      "slug": "sex-horm-binding-glob-serum",
      "value": 30.4,
      "result": "30.4",
      "type": "numeric",
      "unit": "nmol/L",
      "timestamp": "2024-10-31T09:08:00+00:00",
      "notes": "Final",
      "min_range_value": 24.6,
      "max_range_value": 122,
      "is_above_max_range": false,
      "is_below_min_range": false,
      "interpretation": "normal",
      "loinc": "13967-5",
      "loinc_slug": "sex-hormone-binding-globulin-moles-vol",
      "provider_id": "082016",
      "source_markers": [
        {
          "marker_id": 229,
          "name": "Testosterone Free, Profile I",
          "slug": "testosterone-free-profile-i",
          "provider_id": "140226"
        }
      ]
    },
```

When Junction cannot identify the source, then this field will be `null`, indicating that this is an unsolicited result.
There may also be more than one `source` marker, if there are two or more ordered markers that contain the same underlying result, using the same testing method.

#### Missing Results

At times, labs will make mistakes, and expected results will be missing. Junction identifies these and parses them into a separate structure, named `missing_results`.

This data has the following format:

```python theme={null}
name: str
slug: str
inferred_failure_type: FailureType
note: str | None = None
loinc: str | None = None
loinc_slug: str | None = None
provider_id: str | None = None
source_markers: List[ParentBiomarkerData] | None = None
```

`inferred_failure_type` is the Junction-assigned error type.
The error type is inferred from the comments received from the lab. They are to help assess possible root causes of missing results, and aid the customer in identifying issues in aggregate. The way that we infer these error types is subject to change as we continue to refine and achieve a more granular understanding of failure modes.

1. `quantity_not_sufficient_failure`

   The lab could not process this result due to an insufficient quantity of collected sample. This could be due to the patient refusing to collect more, the phlebotomist being unable to collect the proper volume of blood from the patient, or the phlebotomist not collecting all of the sample they were meant to collect.

2. `collection_process_failure`

   This is indicative of potential failures to follow the entire phlebotomy process, often immediately following the collection. For example, improper centrifugation of the collected sample or improper refrigeration. While these are the most likely causes, there are other reasons why sample quality may have been degraded.

3. `drop_off_failure`

   This speaks to a specific form of collection process failures. Specifically, the sample was not received by the lab in proper condition. Possible issues correspond to improper freezing, refrigeration, or exposure to excessive transport delay.

4. `internal_lab_failure`

   This speaks to failures that are most likely to have happened internally at the lab. This includes issues such as misplacing a collected sample, or errors that could not best be attributed to any external cause.

5. `order_entry_failure`

   The test was not performed because it was not properly ordered at the lab.

6. `non_failure`

   This speaks to a failure that should not impact the patient. For example, it may indicate that a duplicate test was ordered.

7. `unknown_failure`

   This is a failure that could not be properly attributed to any specific failure mode. Potentially the results were simply left out and Junction was not provided any additional information.

8. `patient_condition_failure`

   This speaks to a failure to result due to specifics of the patient's physical condition. For example, the patient may have had some food very high in fats immediately prior to a collection. It is possible that improper storage and handling can cause samples to fail in ways that appear to be a patient condition failure.

9. `missing_result_calc_failure`

   This failure indicates that a calculated field is missing because the underlying tests required to perform the calculation were either unable to be processed, or yielded a result outside of the allowable parameters to perform the calculation.

10. `missing_demo_aoe_calc_failure`

Some results may require additional information — such as age — reported in AOE (ask on order entry) in order to be properly calculated.


# Communications
Source: https://docs.junction.com/lab/testkits/communications

Configure SMS notifications sent to patients during each status change in the at-home testkit order lifecycle.

Communication for patients is done via SMS for At-home Testkit orders.
Customers have the following options when setting this up:

* `Default` - SMS communications are enabled.
* `Disable` - All communications from Junction are disabled.

Each option has a different set of content and status changes, depending on what triggered them.

<Info>You can enable or disable SMS individually through the [**Junction Dashboard**](https://app.junction.com/), under the Team Settings section.</Info>

## Default Communications

### SMS Messages

A table of the **Order Status** and **default SMS** is provided below:

| Order Status       | Default Message                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ordered`          | Hey **receiver\_name**, your order for the **team\_name** health kit has been placed! We'll update you with where it is in the process via messages :) |
| `transit_customer` | Your order for the **team\_name** test kit is in transit, and should be arriving by **eta**.                                                           |
| `out_for_delivery` | Your order for the **team\_name** test kit is out for delivery, and should be arriving by today.                                                       |
| `delivered_to_lab` | Your test kit has arrived at our labs. We're processing your sample now!                                                                               |
| `completed`        | The lab has finished processing your test kit, your results should be ready soon :)                                                                    |
| `cancelled`        | Hey, your order for the **team\_name** test kit has been cancelled. If this is by accident, please contact support.                                    |

<Info>SMS Texts are customizable, and can be enabled or disabled individually.</Info>

### Emails

We do not send emails for At-home Testkits.

## Disable Communications

In this case, no communications will be sent from Junction. You have the ability to produce completely customized communications
using the [`Webhook events`](/lab/testkits/webhooks) described previously.


# Order Lifecycle
Source: https://docs.junction.com/lab/testkits/order-lifecycle

Reference for all order lifecycle statuses in the at-home testkit flow, including shipping, sample collection, and lab processing stages.

The **At-Home Testkit** can be composed of two distinct **Order** lifecycles, which we detail below.

## Order Lifecycle

As discussed in [`Lab Test Lifecycle - Statuses`](/lab/workflow/lab-test-lifecycle#statuses),
each lab testing modality has the following format:

`[HIGH-LEVEL STATUS].[TEST MODALITY].[LOW-LEVEL STATUS]`

For each modality, there can be multiple **low-level statuses**, for **At-Home Testkit** the
possible low-level statuses are:

### Registered Testkits

* `ordered`: Junction received the order, stored it into our system, and started processing it asynchronously.
* `requisition_created`: An order requisition form was validated and created with the partner laboratory, making the order available to be carried out.
* `requisition_bypassed`: An order requisition form wasn't created when the order was placed with us because it already existed.
* `transit_customer`: The testkit is shipped and in transit to the customer.
* `out_for_delivery`: The testkit is out for delivery.
* `with_customer`: The testkit is delivered to the customer.
* `transit_lab`: The customer has sent the testkit back to the lab.
* `delivered_to_lab`: The lab has received the testkit.
* `failure_to_deliver_to_customer`: The shipping company was unable to deliver the testkit to the customer.
* `failure_to_deliver_to_lab`: The shipping company was unable to deliver the testkit to the lab.
* `problem_in_transit_customer`: The shipping company encountered a problem while delivering the testkit to the customer.
* `problem_in_transit_lab`: The shipping company encountered a problem while delivering the testkit to the lab.
* `lab_processing_blocked`: The lab encountered an issue while processing the sample.
* `sample_error`: The collected sample was unprocessable by the lab.
* `completed`: The laboratory processed the blood sample and final results are available.
* `cancelled`: The order was cancelled by either the patient, Junction, or you.

The Finite State Machine that defines the possible transitions for the low-level statuses described above is illustrated
in the following diagram.

<Frame>
  <img />
</Frame>

### Registrable Testkits

* `ordered`: Junction received the order, stored it into our system, and started processing it asynchronously.
* `awaiting_registration`: Order is created but no user has been registered yet.
* `transit_customer`: The testkit is shipped and in transit to the customer.
* `out_for_delivery`: The testkit is out for delivery.
* `with_customer`: The testkit is delivered to the customer.
* `registered`: Order has been registered, and a requisition will be created.
* `requisition_created`: An order requisition form was validated and created with the partner laboratory, making the order available to be carried out.
* `transit_lab`: The customer has sent the testkit back to the lab.
* `delivered_to_lab`: The lab has received the testkit.
* `failure_to_deliver_to_customer`: The shipping company was unable to deliver the testkit to the customer.
* `failure_to_deliver_to_lab`: The shipping company was unable to deliver the testkit to the lab.
* `problem_in_transit_customer`: The shipping company encountered a problem while delivering the testkit to the customer.
* `problem_in_transit_lab`: The shipping company encountered a problem while delivering the testkit to the lab.
* `lab_processing_blocked`: The lab encountered an issue while processing the sample.
* `sample_error`: The collected sample was unprocessable by the lab.
* `completed`: The laboratory processed the blood sample and final results are available.
* `cancelled`: The order was cancelled by either the patient, Junction, or you.

The Finite State Machine that defines the possible transitions for the low-level statuses described above is illustrated
in the following diagram.

<Frame>
  <img />
</Frame>

<br />

<Info>In **sandbox**, there is no async transition from the `ordered` state to the `requisition_created` state. This must be **manually triggered** via the **Junction Dashboard**.</Info>


# Overview
Source: https://docs.junction.com/lab/testkits/overview

Overview of the at-home testkit modality where patients receive a kit by mail, collect their own sample, and return it to the lab.

At-home Testkits are one of our offered modalities.
This modality involves sending a kit to a patient's home, where the patient will collect their own sample and send the kit to our Lab network through mail.

Within this modality, we provide two types of testkits, those which are pre-registered to a user, and those which need to be [registered by the patient upon receiving the kit](/lab/workflow/order-registrable-testkit).

## Ordering Flow overview

To achieve this, a high-level overview of the process is defined as:

* An order is placed in Junction's system through our Dashboard or API.
* The order is added to a background queue and additional checks are made before the kit is shipped to the provided address.
* Once the order shipment is created, a requisition is generated.
* After the requisition is created, some form of communication is carried out with the patient.
* If the kit is not registered, then the user must register the kit before results can be provided.
* The patient receives the kit, collects their sample, and sends the kit back through mail.
* The Laboratory processes the patient's blood sample and generates the required results.
* Junction exposes the results as soon as they are available, both as PDF and structured data via API.

## Constraints

* If the testkit is unregistered, then the user must register it in order for the results to be processed.

To find out more details, see [`Order Lifecycle`](/lab/testkits/order-lifecycle), [`Communications`](/lab/testkits/communications) and [`Webhooks`](/lab/testkits/webhooks).


# Testkit Status Details
Source: https://docs.junction.com/lab/testkits/status-details

Reference for testkit order status details, including lab processing blocks and sample errors returned by the API.

## Overview

When a testkit order enters a problem or blocked state, the API surfaces additional detail beyond the order event status. This page describes the named status details and what they mean.

<Warning>
  **Please note**: To ensure future compatibility, avoid exhaustive matching on status detail values. New values may be introduced over time. Handle unknown values gracefully using a default case.
</Warning>

***

## Lab Processing

These status details apply after the lab has received the sample but cannot proceed with processing. Most are recoverable if the missing information is provided before the specimen expires.

| Status Detail                       | Description                                                                                     | Permanence | Detection                       |
| ----------------------------------- | ----------------------------------------------------------------------------------------------- | ---------- | ------------------------------- |
| **Missing date of collection**      | Lab received the sample but the collection date is absent.                                      | Temporary  | Programmatic (lab notification) |
| **Missing documentation / ID form** | Lab received the sample but required paperwork is missing.                                      | Temporary  | Programmatic (lab notification) |
| **Demographic mismatch**            | Name or date of birth on the specimen does not match the order. Requires patient confirmation.  | Temporary  | Programmatic (lab notification) |
| **NIS (Not In System)**             | Lab cannot match the specimen to a known order. Requires ops investigation. May be recoverable. | Temporary  | Programmatic (lab notification) |

### API field values — Lab Processing

| Problem                         | `status`                 | `status_detail`                       |
| ------------------------------- | ------------------------ | ------------------------------------- |
| Missing date of collection      | `lab_processing_blocked` | `date_of_collection_unspecified`      |
| Missing documentation / ID form | `lab_processing_blocked` | `demographic_information_unspecified` |
| Demographic mismatch            | `lab_processing_blocked` | `demographic_information_mismatch`    |
| NIS (Not In System)             | `lab_processing_blocked` | —                                     |

***

## Sample Errors

These status details indicate the collected sample could not be processed by the lab. All are final — a new kit and recollection are required.

| Status Detail                             | Description                                                                                 | Detection                       |
| ----------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------- |
| **QNS (Quantity Not Sufficient)**         | Insufficient blood volume was collected.                                                    | Programmatic (lab notification) |
| **Contaminated specimen**                 | Sample was contaminated during collection or transit.                                       | Programmatic (lab notification) |
| **Specimen too old / stability exceeded** | Too much time elapsed between collection and processing — sample has degraded.              | Programmatic (lab notification) |
| **Specimen mislabeled**                   | Identification discrepancy between the specimen and the requisition. Results are discarded. | Programmatic (lab notification) |
| **Specimen lost by lab**                  | Lab reports the specimen was lost after receipt.                                            | Programmatic (lab notification) |
| **Partial results**                       | Lab could only process a subset of ordered tests. Remaining tests may require recollection. | Programmatic (lab notification) |

### API field values — Sample Errors

| Problem                               | `status`       | `status_detail`                  |
| ------------------------------------- | -------------- | -------------------------------- |
| QNS (Quantity Not Sufficient)         | `sample_error` | `sample_quantity_not_sufficient` |
| Contaminated specimen                 | `sample_error` | `sample_contaminated`            |
| Improper collection                   | `sample_error` | `sample_improper_collection`     |
| Specimen hemolyzed                    | `sample_error` | `sample_hemolyzed`               |
| Specimen too old / stability exceeded | `sample_error` | `sample_stability_exceeded`      |
| Specimen mislabeled                   | `sample_error` | —                                |


# Webhooks
Source: https://docs.junction.com/lab/testkits/webhooks

Reference for webhook events triggered during at-home testkit order lifecycle changes, with example payload structures.

The following webhook events are of interest when placing an At-Home Testkit order. These are
described in detail in the following sections.

## Order webhook events

Based on the status present in [`Order Lifecycle`](/lab/testkits/order-lifecycle),
Junction will trigger two kinds of webhook events, [`labtest.order.created`](/event-catalog/labtest.order.created) and
[`labtest.order.updated`](/event-catalog/labtest.order.updated).

The `labtest.order.created` event is triggered when an order is created in the system,
having the `ordered` status, and all subsequent status changes will trigger a `labtest.order.updated` event in the system.

The webhook payload body will have the following information if the Order is in the `requisition_created` status:

```json Testkit Order Updated theme={null}
{
   "data":{
      "created_at":"2023-09-01T18:02:41.210495+00:00",
      "details":{
         "data":{
            "created_at":"2023-09-01T18:02:41.247443+00:00",
            "id":"3c046d74-347e-4e28-9e0d-c5b720a8e219",
            "shipment":{
               "id":"ae0323d4-2c18-4aea-a0e7-d73b377315b1",
               "inbound_courier":null,
               "inbound_tracking_number":null,
               "inbound_tracking_url":null,
               "notes":null,
               "outbound_courier":null,
               "outbound_tracking_number":null,
               "outbound_tracking_url":null
            },
            "updated_at":"2023-09-01T18:02:42.186312+00:00"
         },
         "type":"testkit"
      },
      "events":[
         {
            "created_at":"2023-09-01T18:02:41.271401+00:00",
            "id":4056,
            "status":"received.testkit.ordered"
         },
         {
            "created_at":"2023-09-01T18:05:12.618156+00:00",
            "id":4057,
            "status":"received.testkit.requisition_created"
         }
      ],
      "health_insurance_id":null,
      "id":"e1c380c1-7df4-487f-869e-f1be0193ca25",
      "lab_test":{
         "fasting":false,
         "id":"0cb9f34f-c3df-4a13-8ca1-19429a82611b",
         "is_active":true,
         "is_delegated":false,
         "lab":null,
         "markers":null,
         "method":"testkit",
         "name":"Female General Wellness",
         "price":45,
         "sample_type":"dried_blood_spot",
         "slug":"general_wellness_female_002"
      },
      "notes":null,
      "patient_address":{
        ...
      },
      "patient_details":{
        ...
      },
      "physician":{
         ...
      },
      "priority":false,
      "requisition_form_url":null,
      "sample_id": "some_id",
      "shipping_details":null,
      "status":"received",
      "team_id":"f07f59d2-2903-4bcd-a2ac-3e87fa47c4bc",
      "updated_at":"2023-09-01T18:02:41.210495+00:00",
      "user_id":"f6cdf185-6815-4b2b-9482-798b75168689",
      "origin": "initial",
      "order_transaction": {
         "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
         "status": "active",
         "orders": [
            {
               "id": "e1c380c1-7df4-487f-869e-f1be0193ca25",
               "created_at": "2023-09-01T18:02:41.210495+00:00",
               "updated_at": "2023-09-01T18:02:41.210495+00:00",
               "low_level_status": "requisition_created",
               "low_level_status_created_at": "2023-09-01T18:05:12.618156+00:00",
               "origin": "initial"
            }
         ]
      }
   },
   "event_type":"labtest.order.updated"
}
```


# Communications
Source: https://docs.junction.com/lab/walk-in/communications

Configure SMS and email notifications sent to patients during each status change in the walk-in lab test order lifecycle.

Communication for patients is done via email or SMS for Walk-in orders.
Customers have the following options when setting this up:

* `Default` - Email and SMS communications are enabled.
* `SMS Only or Email Only` - Only SMS or Email communication is enabled.
* `Disable` - All communications from Junction are disabled.

Each option has a different set of content and status changes, depending on what triggered them.

<Info>
  You can enable or disable SMS and Email communications individually through the [**Junction Dashboard**](https://app.junction.com/), under the Team Settings section.
</Info>

## Default Communications

### SMS Messages

A table of the **Order Status** and **default SMS** is provided below:

| Order Status            | Default Message                                                                                                                                                                                                                                 |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ordered`               | *`"Hi, {patient_first_name}, your order for the {team_name} walk-in-test has been placed! We'll provide you with updates on the status of your order via text messages :)."`*                                                                   |
| `requisition_created`   | *`"Hey {patient_first_name}, it's time to visit your local {lab_name} center. Please check the email you just received for instructions."`*                                                                                                     |
| `appointment_scheduled` | *`"Your appointment with the lab has been booked at {date} over at {address}! Here's your appointment key: {appointment_key}"`* <Note>   This message is sent only for Quest orders and Quest appointments placed through Junction API. </Note> |
| `appointment_cancelled` | *`"Hey, your lab appointment at {date} for {team_name} has been cancelled."`* <Note>   This message is sent only for Quest orders and Quest appointments placed through Junction API. </Note>                                                   |
| `redraw_available`      | *`"Hi {patient_first_name}, your {team_name} results have come back with at least one missing biomarker. We recommend a redraw to complete your lab testing. We will send you an email shortly with more information."`*                        |
| `completed`             | *`"Your results have finished processing, your results should be ready soon."`*                                                                                                                                                                 |
| `cancelled`             | *`"Hey, your order for the {team_name} walk-in-test has been cancelled. If this is by accident, please contact support."`*                                                                                                                      |

<Info>
  SMS Texts are customizable, and can be enabled or disabled individually.
</Info>

### Emails

For emails, the following table describes what information each email contains for each Order Status:

| Order Status          | Email Content Description                                                                                                |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `requisition_created` | An email with confirmation of the partner Lab and additional instructions will be sent to the patient.                   |
| `redraw_available`    | An email with confirmation of the partner Lab, missing biomarker(s) and redraw instructions will be sent to the patient. |

<Info>
  Emails can be customized and sent from your own domain.
</Info>

## Scheduling Appointments Before A Requisition Has Been Created

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

If the ability to [`schedule appointments before a requisition has been created`](/lab/walk-in/order-lifecycle#scheduling-appointments-before-a-requisition-has-been-created) is enabled for your team, all appointment-related messages
will be triggered by the **appointment** status, not the **order** status. All non-appointment-related messages will be triggered by the **order** status, e.g.,
`requisition_created`, `completed`, etc. (see above section).

### SMS Messages

A table of the **Appointment Status** and **default SMS** is provided below:

| Appointment Status | Default Message                                                                                                                                                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `confirmed`        | *`"Your appointment with the lab has been booked at {date} over at {address}! Here's your appointment key: {appointment_key}"`* <Note>   This message is sent only for Quest orders and Quest appointments placed through Junction API. </Note> |
| `cancelled`        | *`"Hey, your lab appointment at {date} for {team_name} has been cancelled."`* <Note>   This message is sent only for Quest orders and Quest appointments placed through Junction API. </Note>                                                   |

### Emails

No emails are sent for this feature.

## Disable Communications

In this case, no communications will be sent from Junction. You have the ability to produce completely customized communications
using the [`Webhook events`](/lab/walk-in/webhooks) described previously.


# Order and Appointment Lifecycle
Source: https://docs.junction.com/lab/walk-in/order-lifecycle

Learn the order and appointment statuses, state transitions, and scheduling flows for Walk-In Phlebotomy lab tests.

The **Walk-In Test** orders are composed of two different lifecycles, the **Order** lifecycle and the **Appointment** lifecycle, detailed in the following sections.

## Order Lifecycle

As discussed in [`Lab Test Lifecycle - Statuses`](/lab/workflow/lab-test-lifecycle#statuses), each lab testing modality has the following format:

`[HIGH-LEVEL STATUS].[TEST MODALITY].[LOW-LEVEL STATUS]`

For each modality, there can be multiple **low-level statuses**, for **Walk-In Phlebotomy** the possible low-level statuses are:

* `ordered`: Vital received the order, stored it into our system, and started processing it asynchronously.
* `requisition_created`: An order requisition form was validated and created with the partner laboratory, making the order available to be carried out.
* `requisition_bypassed`: An order requisition form wasn't created when the order was placed with us because it already existed.
* `appointment_pending`: An appointment was placed in Vital's system for the order, but doesn't have a scheduled date.
* `appointment_scheduled`: An appointment was scheduled or rescheduled for the order.
* `appointment_cancelled`: The appointment was cancelled, by either the patient, Vital or you.
* `partial_results`: The laboratory has started making partial results available.
* `redraw_available`: The result has been marked as final but is missing biomarker results due to lab error.
* `completed`: The laboratory processed the blood sample and final results are available.
* `sample_error`: The collected sample was unprocessable by the lab.
* `cancelled`: The order was cancelled by either the patient, Vital or you.

The Finite State Machine that defines the possible transitions for the low-level statuses described above is illustrated in the following diagram.

<Frame>
  <img alt="" />
</Frame>

<br />

<Info>
  In **sandbox**, there is no async transition from the `ordered` state to the `requisition_created` state. This must be **manually triggered** via the **Junction Dashboard**.
</Info>

## Appointment Lifecycle

The appointment lifecycle is separate from the order lifecycle, and it corresponds to a single appointment. The possible statuses are defined as follows:

* `pending`: An appointment was placed in the system, and is pending updates from the phlebotomy service.
* `scheduled`: An appointment was scheduled or rescheduled.

The Finite State Machine that defines the possible transitions for the appointment statuses described above is illustrated in the following diagram.

<Frame>
  <img alt="" />
</Frame>

The events are related to a single appointment. An order can have multiple existing appointments in Vital's system, although only one appointment will be considered active and returned when using the [GET Appointment endpoint](/api-reference/lab-testing/psc-scheduling/get-psc-appointment).

## Scheduling Appointments Before A Requisition Has Been Created

<Note>
  This feature is available upon request. To enable, please get in touch with your Customer Success Manager.
</Note>

When this feature is enabled on your Team, you can create a Patient Service Center (PSC) appointment even before the requisition has been created for the corresponding Order (i.e., reaching the `requisition_created` status).

This allows you to build an experience that can order the lab test *and* book an appointment consecutively, without having to wait on the asynchronous requisition creation process that may take an indeterminate amount of time.

### API Journey

<Steps>
  <Step title="Creating an appointment">
    You create an Appointment for an Order which does not yet have a requisition.

    The Appointment starts with the `reserved` status.

    <Note>
      If the order requisition has been created on or before the time slot confirmation, the Appointment moves directly to the `confirmed` status. The `reserved` status will be skipped.
    </Note>
  </Step>

  <Step title="Waiting for Requisition Creation">
    The requisition is being created asynchronously.
  </Step>

  <Step title="Requisition has been created">
    The requisition has now been created.

    1. The Order will move through the `requisition_created`, `appointment_pending` and `appointment_scheduled` Order statuses in quick succession.
    2. The Appointment will move to the `confirmed` status automatically, which corresponds to the `scheduled` appointment event.

    You will receive three `labtest.order.updated` events, one each for the three Order status transitions. You will also receive a `labtest.appointment.updated` event for the Appointment status transition.
  </Step>
</Steps>

## Cancelling An Appointment

If an Appointment is cancelled **before** a requisition has been created:

* The Appointment will move to the `cancelled` status; and
* There is no change to the Order, which will remain in the `ordered` status.

If an Appointment is cancelled **after** a requisition has been created:

1. The Appointment will move to the `cancelled` status; and
2. The Order will move from the `appointment_pending` or `appointment_scheduled` status to the `appointment_cancelled` status.

### Auto-cancellation of appointments without requisition

In cases where appointments can be scheduled before a requisition has been created, the appointment will be automatically cancelled by Junction if the following happens:

1. More than 8 hours have passed since the order was created, and no requisition has been received.
2. There are fewer than 2 hours until the appointment start time, and no requisition has been received.

<Note>
  The 8-hour auto-cancellation rule does not apply to [scheduled orders](/lab/workflow/scheduled-orders#scheduled-orders). Scheduled orders are fulfilled on their `activate_by` date, so Junction does not cancel their appointments based solely on the fact that more than 8 hours have passed since order creation without a requisition.
</Note>

## Arizona Scheduling

<Note>
  This feature is available upon request. To enable, please get in touch with your Customer Success Manager.
</Note>

When this feature is enabled on your Team, you can book appointments with Quest in Arizona (Sonora Quest).

### API Journey

<Steps>
  <Step title="Creating an appointment">
    You create an Appointment for an Order to an Arizona PSC.

    The Appointment starts with the `reserved` status.
  </Step>

  <Step title="Waiting for Appointment Confirmation">
    The appointment is being created asynchronously.
  </Step>

  <Step title="Appointment has been created">
    The appointment has now been created.

    1. The Order will move through the `appointment_pending` and `appointment_scheduled` Order statuses in quick succession.
    2. The Appointment will move to the `confirmed` status automatically, which corresponds to the `scheduled` appointment event.

    You will receive a `labtest.appointment.updated` event for the Appointment status transition. You will also receive two `labtest.order.updated` events, one each for the two Order status transitions.
  </Step>
</Steps>

<Info>
  Arizona Scheduling uses separate Order and Appointment lifecycles. Appointment status changes are delivered through [`labtest.appointment.updated`](/event-catalog/labtest.appointment.updated). The Order status updates for `appointment_pending` and `appointment_scheduled` are emitted only after Sonora Quest confirms the appointment. If you use this feature, make sure your webhook endpoint is subscribed to appointment-level events.
</Info>

### Appointment Booking Failure

If we are unable to book the Appointment asynchronously, then:

* The Appointment will move to the `cancelled` status.
* Junction will send a `labtest.appointment.updated` event for the cancelled Appointment.
* There is no change to the Order, which will remain in its last status.

### Cancellation and Rescheduling

Cancellation and rescheduling for Arizona should be done directly by the patient, via the email they receive from Sonora Quest. It is not possible to cancel/reschedule via Junction.


# Overview
Source: https://docs.junction.com/lab/walk-in/overview

Overview of the walk-in test modality where patients visit a laboratory Patient Service Center to have their blood drawn.

Walk-in tests are one of our offered modalities.
This modality is focused on patients that are able to go to a Laboratory to have their blood drawn.

## Ordering Flow overview

To achieve this, a high-level overview of the process is defined as:

* An order is placed in Junction's system through our Dashboard or API.
* The order is added to a background queue and additional checks are made before a test requisition is created with the chosen Laboratory.
* After the requisition is created, some form of communication is carried out with the patient so they can go to the assigned partner Laboratory.
* After that, the patient can go at any time they want to the laboratory to have their blood drawn.
* The Laboratory processes the patient's blood sample and generates the required results.
* Junction exposes the results as soon as they are available, both as PDF and structured data via API.

To find out more details, see [`Order Lifecycle`](/lab/walk-in/order-lifecycle), [`Communications`](/lab/walk-in/communications) and [`Webhooks`](/lab/walk-in/webhooks).


# API Endpoints and Errors
Source: https://docs.junction.com/lab/walk-in/psc-appointment-scheduling

Error handling and optional features for scheduling appointments at Patient Service Centers (PSCs).

## API Endpoints

All endpoints require authentication and are prefixed with `/v3/order`:

* [`POST /v3/order/psc/appointment/availability`](/api-reference/lab-testing/psc-scheduling/appointment-psc-availability) - Get available time slots
* [`POST /v3/order/{order_id}/psc/appointment/book`](/api-reference/lab-testing/psc-scheduling/appointment-psc-booking) - Book an appointment
* [`PATCH /v3/order/{order_id}/psc/appointment/reschedule`](/api-reference/lab-testing/psc-scheduling/appointment-psc-rescheduling) - Reschedule an appointment
* [`PATCH /v3/order/{order_id}/psc/appointment/cancel`](/api-reference/lab-testing/psc-scheduling/appointment-psc-cancelling) - Cancel an appointment
* [`GET /v3/order/{order_id}/psc/appointment`](/api-reference/lab-testing/psc-scheduling/get-psc-appointment) - Get appointment details
* [`GET /v3/order/psc/appointment/cancellation-reasons`](/api-reference/lab-testing/psc-scheduling/appointment-psc-cancellation-reasons) - Get cancellation reasons

***

## Error Handling

### Environment Differences

⚠️ **Important**: Currently, error behavior differs between sandbox and production environments:

| Environment    | Quest Integration | Slot Conflict Behavior                        |
| -------------- | ----------------- | --------------------------------------------- |
| **Sandbox**    | Mock client       | Returns 400 (mock doesn't simulate conflicts) |
| **Production** | Real Quest API    | Returns 404 (actual Quest response)           |

**Recommendation**: For now, handle both 400 and 404 responses as potential slot-unavailability scenarios.

### Frequently Asked Questions

#### Q: Do I need to parse error messages to distinguish between different 400 errors?

**Yes.** Since multiple error types return 400 status codes, you must examine the error message content to determine the appropriate handling:

* **Time in the past**: `"This appointment slot is no longer available. Please select a new time."` → Select new slot
* **Invalid booking key**: `"Invalid booking key format"` → Fix request
* **Missing parameters**: `"radius must be provided when using zip_code"` → Fix request
* **Order state issues**: `"Order is not in a state that allows booking..."` → Contact support

HTTP status codes alone are insufficient because our API uses 400 for multiple distinct error categories that require different user experiences.

### Error Categories

#### Slot Unavailability Errors

*The user should select a different time slot*

| Status     | Error Message                                                               | When It Occurs                           | User Action                            |
| ---------- | --------------------------------------------------------------------------- | ---------------------------------------- | -------------------------------------- |
| 400        | `"This appointment slot is no longer available. Please select a new time."` | Requested time is in the past            | Select a future time slot              |
| 400 or 404 | Quest-specific error messages                                               | Slot already taken or no longer exists   | Select a different time slot           |
| 404        | `"No slots found for zip code"`                                             | No locations/slots in the area           | Try a different location or date range |
| 404        | `"No slots found. Not all PSCs support scheduling..."`                      | All locations lack scheduling capability | Use different locations                |

#### Input Validation Errors

*Fix the request parameters*

| Status | Error Message                                                 | When It Occurs                        | User Action                                     |
| ------ | ------------------------------------------------------------- | ------------------------------------- | ----------------------------------------------- |
| 400    | `"Invalid booking key format"`                                | Malformed or corrupted booking key    | Refresh availability and get a new booking key  |
| 400    | `"Location with site code {code} not found"`                  | Invalid PSC location code             | Use a valid site code from the availability API |
| 400    | `"Location with site code {code} is no longer active"`        | PSC location disabled                 | Select a different location                     |
| 400    | `"start_date must be greater or equal than the current date"` | Past date in the availability request | Use current or future date                      |
| 400    | `"site_codes or zip_code must be provided"`                   | Missing search parameters             | Provide either site codes or zip code           |
| 400    | `"radius must be provided when using zip_code"`               | Missing radius with zip code search   | Include radius parameter                        |
| 400    | `"site_codes must be less than or equal to 3"`                | Too many site codes                   | Limit to 3 site codes maximum                   |
| 404    | `"This order doesn't exist."`                                 | Invalid order ID                      | Verify order ID is correct                      |
| 404    | `"This order is not a walk-in phlebotomy order."`             | Wrong order type                      | Use the correct order type                      |

#### Order State Errors

*Order workflow or business logic issues*

| Status | Error Message                                           | When It Occurs                       | User Action                             |
| ------ | ------------------------------------------------------- | ------------------------------------ | --------------------------------------- |
| 400    | `"Order is not a walkin phlebotomy order"`              | Wrong order type for PSC booking     | Contact support                         |
| 400    | `"Order does not have a sample ID..."`                  | Order missing requisition            | Wait for requisition or contact support |
| 400    | `"Order is not in a state that allows booking..."`      | Order workflow prevents booking      | Contact support                         |
| 400    | `"Order is not in a state that allows cancelling..."`   | Order workflow prevents cancellation | Contact support                         |
| 400    | `"Order is not in a state that allows rescheduling..."` | Order workflow prevents rescheduling | Contact support                         |
| 400    | `"This order does not have a requisition..."`           | Missing requisition for appointment  | Wait or contact support                 |
| 400    | `"This lab is not supported."`                          | Order not for Quest lab              | Use a supported lab                     |

#### Appointment Management Errors

*Appointment-specific business rules*

| Status | Error Message                                         | When It Occurs                              | User Action                       |
| ------ | ----------------------------------------------------- | ------------------------------------------- | --------------------------------- |
| 400    | `"This order doesn't have an appointment yet."`       | Trying to modify a non-existent appointment | Book an appointment first         |
| 400    | `"This appointment cannot be rescheduled."`           | Appointment marked non-reschedulable        | Cancel and book a new appointment |
| 400    | `"This appointment has been cancelled or completed."` | Operating on finalized appointment          | No further action is possible     |
| 400    | `"This appointment has already been cancelled."`      | Cancelling already cancelled appointment    | No action needed                  |
| 404    | `"Appointment not found."`                            | Appointment doesn't exist                   | Verify appointment exists         |

#### Authorization Errors

*Access control issues*

| Status | Error Message        | When It Occurs                      | User Action     |
| ------ | -------------------- | ----------------------------------- | --------------- |
| 401    | Authentication error | Invalid or expired token            | Re-authenticate |
| 403    | `"Action forbidden"` | User lacks permission for the order | Contact support |

#### Service Availability Errors

*System configuration or availability*

| Status | Error Message           | When It Occurs          | User Action                  |
| ------ | ----------------------- | ----------------------- | ---------------------------- |
| 503    | `"Feature not enabled"` | PSC scheduling disabled | Contact support or try later |

### Testing Error Scenarios

#### Sandbox Testing

* Most validation errors can be tested in sandbox
* Slot conflicts cannot be reliably tested (mock client always succeeds)
* Use invalid parameters to test input validation

#### Production Testing

* Test with caution using real appointments
* Slot conflicts will return actual 404 errors from Quest
* Time-based errors can be tested with past dates

### Quest API Limitations

* **Timeout**: 15 seconds read timeout, 5 seconds connection timeout
* **Slot availability**: Real-time, can change between availability check and booking
* **Mock client**: Doesn't simulate all real-world scenarios
* **Rate limiting**: Standard Quest API rate limits apply


# Opt-In Features
Source: https://docs.junction.com/lab/walk-in/psc-appointment-scheduling-opt-ins

Error handling and optional features for scheduling appointments at Patient Service Centers (PSCs).

## Availability Cache

Junction maintains a fast Availability Cache containing the next 21 days of availability for all locations. Each location is refreshed every 5 minutes on average.

When you set the `allow_stale` parameter on the [PSC Appointment Availability](/api-reference/lab-testing/psc-scheduling/appointment-psc-availability) endpoint, your request is fulfilled directly from the Availability Cache, provided that:

1. All requested locations have a cache hit; and
2. The cached information is reasonably fresh (less than 15 minutes old).

When a request cannot be fulfilled by the Availability Cache, it is routed directly to the PSC Appointment Provider.

#### When to use

Use `allow_stale` when you need fast response times and can tolerate slightly stale availability data—for example, when displaying initial availability options to a user before they select a specific slot.

## Idempotency Key

Consider specifying an Idempotency Key (`X-Idempotency-Key` header) when submitting a booking request to the
[Book PSC Appointment](/api-reference/lab-testing/psc-scheduling/appointment-psc-booking) endpoint. Requests with the same
idempotency key within a 14-day retention window will return the original request's response.

Note that the response is frozen at the time it is first emitted. For example, if a pending appointment later transitions to confirmed via Async Confirmation, requests with that idempotency key will still return the appointment in its original pending status.

#### When to use

When a booking request fails on your side (API consumer side), it could be a transient connection issue or a Junction request queueing issue.
Your request might have still reached the Junction API server and might have been processed.

Using an Idempotency Key allows your system to safely retry booking requests, without the risk of double booking appointments.

## Async Confirmation

When calling the [Book PSC Appointment](/api-reference/lab-testing/psc-scheduling/appointment-psc-booking) endpoint, Junction by default returns a 500 Internal Server Error if the PSC Appointment Provider fails to acknowledge the booking request.

With Async Confirmation enabled, Junction waits synchronously for the provider to acknowledge the booking request, up to `sync_confirmation_timeout_millisecond` (default: 2.5s, range: 1–10s).

* If an acknowledgment is received before the timeout, Junction creates a *reserved* or *confirmed* appointment immediately and returns it in the response.

* If no acknowledgment is received before the timeout, Junction creates a placeholder **pending** appointment and returns it in the response.

#### Background retries

If a pending appointment is created, Junction continues to retry the booking request with the PSC Appointment Provider in the background, up to `async_confirmation_timeout_millisecond` (default: 15min, range: 1min–48hr).

* If an acknowledgment is received before the timeout, the appointment transitions to *reserved* or *confirmed* status.

* If the provider still fails to acknowledge after the timeout, the pending appointment transitions to *cancelled* status.

#### Webhooks

You will receive the `labtest.appointment.updated` event for the initial appointment creation, as well as for all state transitions described above.

#### When to use

Use Async Confirmation when you want to provide a consistently responsive booking experience.

When the PSC Appointment Provider is under stress, Async Confirmation allows you to show a pending appointment to the end patient, and
manages all the logistics of retrying the booking requests with the provider.


# Webhooks
Source: https://docs.junction.com/lab/walk-in/webhooks

Reference for webhook events triggered during walk-in order lifecycle changes, with example payload structures.

The following webhook events are of interest when placing a Walk-in order. These are
described in detail in the following sections.

## Order webhook events

Based on the status present in [`Order Lifecycle`](/lab/walk-in/order-lifecycle),
Junction will trigger two kinds of webhook events, [`labtest.order.created`](/event-catalog/labtest.order.created) and
[`labtest.order.updated`](/event-catalog/labtest.order.updated).

The `labtest.order.created` event is triggered when an order is created in the system,
having the `ordered` status, and all subsequent status changes will trigger a `labtest.order.updated` event in the system.

<Note>The `partial_results` status does not trigger a Webhook unless specifically requested from Junction.</Note>

The webhook payload body will have the following information if the Order is in the `completed` status:

```json Walk-in Order Updated theme={null}
{
  "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
  "team_id": "6353bcab-3526-4838-8c92-063fa760fb6b",
  "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "patient_details": { "dob": "2020-01-01", "gender": "male" },
  "patient_address": {
    "receiver_name": "John Doe",
    "first_line": "123 Main St.",
    "second_line": "Apt. 208",
    "city": "San Francisco",
    "state": "CA",
    "zip": "91189",
    "country": "United States",
    "phone_number": "+1123456789"
  },
  "details": {
    "type": "walk_in_test",
    "data": {
      "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
      "created_at": "2020-01-01T00:00:00Z",
      "updated_at": "2020-01-01T00:00:00Z"
    }
  },
  "sample_id": "123456789",
  "notes": "This is a note",
  "created_at": "2020-01-01T00:00:00Z",
  "updated_at": "2020-01-01T00:00:00Z",
  "status": "completed",
  "events": [
    {
      "id": 1,
      "created_at": "2022-01-01T00:00:00Z",
      "status": "received.walk_in_test.ordered"
    },
    {
      "id": 2,
      "created_at": "2022-01-02T00:00:00Z",
      "status": "received.walk_in_test.requisition_created"
    },
    {
      "id": 3,
      "created_at": "2022-01-03T00:00:00Z",
      "status": "collecting_sample.walk_in_test.appointment_pending"
    },
    {
      "id": 4,
      "created_at": "2022-01-03T00:00:00Z",
      "status": "collecting_sample.walk_in_test.appointment_scheduled"
    },
    {
      "id": 5,
      "created_at": "2022-01-04T00:00:00Z",
      "status": "sample_with_lab.walk_in_test.partial_results"
    },
    {
      "id": 6,
      "created_at": "2022-01-04T00:00:00Z",
      "status": "completed.walk_in_test.completed"
    }
  ],
  "origin": "initial",
  "order_transaction": {
    "id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
    "status": "completed",
    "orders": [
      {
        "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2022-01-04T00:00:00Z",
        "low_level_status": "completed",
        "low_level_status_created_at": "2022-01-04T00:00:00Z",
        "origin": "initial"
      }
    ]
  }
}
```

## Appointment webhook events

Based on the status present in [`Order and Appointment Lifecycle - Appointment Lifecycle`](/lab/walk-in/order-lifecycle#appointment-lifecycle),
Junction will trigger the [`labtest.appointment.updated`](/event-catalog/labtest.appointment.updated) webhook event.

<Info>
  If the ability to [schedule appointments before a requisition has been created](/lab/walk-in/order-lifecycle#scheduling-appointments-before-a-requisition-has-been-created) is enabled for your team, and you intend to send patient communications for appointment updates, use the `labtest.appointment.updated` event. See more on communications for this feature [here](/lab/walk-in/communications#scheduling-appointments-before-a-requisition-has-been-created).
</Info>

<Info>
  If [Arizona Scheduling](/lab/walk-in/order-lifecycle#arizona-scheduling) is enabled for your team, subscribe to
  `labtest.appointment.updated` for Sonora Quest appointment status changes. The Order and Appointment lifecycles are
  managed separately for this flow, and the Order status updates for `appointment_pending` and `appointment_scheduled`
  are emitted only after Sonora Quest confirms the appointment. An endpoint subscribed only to `labtest.order.*` will
  not receive Appointment status changes such as scheduled or cancelled.
</Info>

The webhook payload body may have the following information if the appointment is in the `scheduled` status, after a **reschedule** has happened:

```json Walk-in Appointment Updated theme={null}
{
  "event_type": "labtest.appointment.updated",
  "data": {
    "id": "06c2c65b-74a0-4f25-a4a9-44f796296355",
    "user_id": "acf79a82-0c2c-4ca0-998b-378931793905",
    "order_id": "1ed9c8d7-e1b4-4d61-8123-0f99de5ae99a",
    "order_transaction_id": "6424dd45-ee1a-49c6-ad0c-5769b8e03fc1",
    "address": {
      "first_line": "123 Main St.",
      "second_line": "Apt. 208",
      "city": "San Francisco",
      "state": "CA",
      "zip_code": "91189",
      "country": "United States"
    },
    "location": {
      "lng": -122.4194155,
      "lat": 37.7749295
    },
    "start_at": "2022-01-01T00:00:00",
    "end_at": "2022-01-01T00:00:00",
    "iana_timezone": "America/New_York",
    "type": "patient_service_center",
    "provider": "quest",
    "status": "confirmed",
    "event_status": "scheduled",
    "provider_id": "123",
    "can_reschedule": true,
    "event_data": {
      "origin": "patient",
      "is_reschedule": true
    },
    "events": [
      {
        "created_at": "2022-01-01T00:00:00Z",
        "data": null,
        "status": "scheduled"
      },
      {
        "created_at": "2022-01-01T00:00:00Z",
        "data": {
          "origin": "patient",
          "is_reschedule": true
        },
        "status": "scheduled"
      }
    ]
  }
}
```

<br />

<Info>The `event_data` field contains relevant information regarding the current appointment status, and may be specific for each `provider`.</Info>


# Ask on Order Entry (AOE)
Source: https://docs.junction.com/lab/workflow/aoe

Handle Ask on Order Entry (AOE) questions required for certain biomarkers, including the four question types and API integration.

Some biomarkers require answers to clinical questions. This is referred to as AOE. These can be as simple as the volume of a particular marker or the patient's ethnicity.

<Note>
  Questions that are related to fasting (e.g., code `FSTING`) do not need to be answered. Junction uses the default value you provided for fasting at the [lab test](/api-reference/lab-testing/post-test) creation time. However, if you do answer them, then the answered value overrides the preset test value.
</Note>

There are 4 types of AOE:

1. `choice`
2. `multiple_choice`
3. `numeric`
4. `text`

`choice` and `multiple_choice` have a set list of answers to pick from, while `numeric` and `text` don't, and are free-form responses.

AOE questions can also be required or optional.

## Where to find the questions

As these AOE are directly related to biomarkers, you can find them in the [`GET /v3/lab_tests/markers`](/api-reference/lab-testing/biomarkers) endpoint or in the [`GET /v3/lab_tests/{lab_test_id}/markers`](/api-reference/lab-testing/lab-test-markers) endpoint, in the `aoe` object.

As an example, let's look at the `Lead, Blood (Adult)` marker. We have omitted some extra questions for the sake of this doc.

```json theme={null}
{
  "id": 173,
  "name": "Lead, Blood (Adult)",
  "provider_id": "007625",
  ...
  "questions": [
    {
      "id": 1234567890251,
      "code": "BLPURP",
      "type": "choice",
      "value": "BLOOD LEAD PURPOSE",
      "constraint": null,
      "answers": [
        {
          "id": 1234567890252,
          "code": "LCANS4",
          "value": "I - INITIAL"
        },
        {
          "id": 1234567890253,
          "code": "LCANS5",
          "value": "R - REPEAT"
        },
        {
          "id": 1234567890254,
          "code": "LCANS6",
          "value": "F - FOLLOW-UP"
        }
      ],
      "required": true,
      "sequence": 1
    }
  ]
}
```

Each question has its own `id`, which will be required to answer it. The types we refer to above are an enum that is represented by `type`.

The `answers` list contains a list of possible answers in the case that `type` is one of `choice` or `multiple_choice`, otherwise it is empty.

`required` determines whether the questions MUST be answered in order for the order to be valid; otherwise, an error will be shown: `Missing required questions - "question"`.

## How to answer

<Tip>
  For more information, refer to our [Knowledge Base article](https://support.junction.com/articles/1804950149-understanding-ask-on-order-entry-aoe-questions).
</Tip>

Answering is done at order time, via the [`POST /v3/order`](/api-reference/lab-testing/create-order) endpoint, in the field `aoe_answers`.

This field has the following format:

```json theme={null}
{
    "aoe_answers": [
        {
            "marker_id": <int>,
            "question_id": <int>,
            "answer": <str>,
        }
    ]
}
```

You will populate this list with the answers to the questions defined in the `marker` object, as explained [above](/lab/workflow/aoe#where-to-find-the-questions).

The `marker_id` refers to the Junction [marker id](/api-reference/lab-testing/biomarkers), and the `question_id` to the id of the question being answered.

For example, marker `Lead, Blood (Adult)`, has the following data:

```json theme={null}
{
  "id": 173,
  "name": "Lead, Blood (Adult)",
  "provider_id": "007625",
  ...
  "questions": [
    {
      "id": 1234567890251,
      "code": "BLPURP",
      "type": "choice",
      "value": "BLOOD LEAD PURPOSE",
      "constraint": null,
      "answers": [
        {
          "id": 1234567890252,
          "code": "LCANS4",
          "value": "I - INITIAL"
        },
        {
          "id": 1234567890253,
          "code": "LCANS5",
          "value": "R - REPEAT"
        },
        {
          "id": 1234567890254,
          "code": "LCANS6",
          "value": "F - FOLLOW-UP"
        }
      ],
      "required": true,
      "sequence": 1
    }
  ]
}
```

In order to answer the required question, you will need the `marker_id` which is the `id` field, the `question_id` in `questions[0].id` and one of the `code` values in the `questions[0].answers` field.

### Answer types

The `answer` field contains the actual answer to the question and its value will depend on the type and question itself.

1. `choice` and `multiple_choice`

In this case, the `answer` should contain the `code` field in the list of `answers` provided, as shown [here](/lab/workflow/aoe#where-to-find-the-questions).

E.g., the question is as follows for marker of id `173`:

```json theme={null}
{
  "id": 173,
  "questions": [
    {
      "id": 1234567890251,
      "code": "BLPURP",
      "type": "choice",
      "value": "BLOOD LEAD PURPOSE",
      "constraint": null,
      "answers": [
        {
          "id": 1234567890252,
          "code": "LCANS4",
          "value": "I - INITIAL"
        },
        {
          "id": 1234567890253,
          "code": "LCANS5",
          "value": "R - REPEAT"
        },
        {
          "id": 1234567890254,
          "code": "LCANS6",
          "value": "F - FOLLOW-UP"
        }
      ],
      "required": true,
      "sequence": 1
    }
  ]
}
```

The answer is:

```json theme={null}
{
    "aoe_answers": [
        {
            "marker_id": 173,
            "question_id": 1234567890251,
            "answer": "LCANS4",
        }
    ]
}
```

2. `text`

This case can encompass many responses, and depends on the question itself.

<Note>
  In some instances, the `text` case contains a `constraint` field. This is a string tooltip indicator of the constraints applied by the lab on this particular question.
</Note>

E.g., the question is as follows for marker of id `173`:

```json theme={null}
{
  "id": 173,
  "questions": [
    {
      "id": 1234567890304,
      "code": "EDDATE",
      "type": "text",
      "constraint": null,
      "value": "EDD/EDC DATE",
      "answers": [],
      "required": true,
      "sequence": 2
    }, 
  ]
}
```

The answer is:

```json theme={null}
{
    "aoe_answers": [
        {
            "marker_id": 173,
            "question_id": 1234567890304,
            "answer": "19900101",
        }
    ]
}
```

3. `numeric`

In this case, the `answer` should be a numeric string representation of the actual value.

E.g., the question is as follows for marker of id `173`:

```json theme={null}
{
  "id": 173,
  "questions": [
    {
      "id": 1234567890240,
      "code": "COLVOL",
      "type": "numeric",
      "constraint": null,
      "value": "URINE VOLUME (MILLILITERS)",
      "answers": [],
      "required": true,
      "sequence": 1
    }
  ]
}
```

The answer is:

```json theme={null}
{
    "aoe_answers": [
        {
            "marker_id": 173,
            "question_id": 1234567890240,
            "answer": "1000",
        }
    ]
}
```


# Cancelling an Order
Source: https://docs.junction.com/lab/workflow/cancelling-an-order

Cancel lab test orders based on their current status and collection method, with guidance on timing to avoid unnecessary costs.

Order cancellation depends on the current status of your order and its collection method. Orders follow a finite state machine (FSM) pattern with specific rules governing when cancellation is permitted.

<Warning>
  **Important timing considerations to avoid financial costs:**

  * **At-home phlebotomy**: Appointments cancelled more than 24 hours in advance will be fully refunded. Cancellations made less than 24 hours before the scheduled appointment are non-refundable.
  * **Testkits**: Cancel before shipment to avoid fees. Testkits typically ship within the same business day if orders are placed before 1pm EST.
  * **Walk-in tests**: Can generally be cancelled until sample collection begins.
  * **On-site collection**: Cancel before the collection event.
</Warning>

## When Orders Can Be Cancelled

### ✅ Generally Cancellable States

All collection methods allow cancellation during these early phases:

* `ordered` - Initial order placement
* `requisition_created` - After requisition form is generated
* `requisition_bypassed` - When requisition is bypassed
* `awaiting_registration` - For registrable testkits

### ❌ Never Cancellable (Terminal States)

Orders **cannot** be cancelled once they reach these states:

* **Already cancelled**: `cancelled`, `do_not_process`
* **Completed**: `completed`, `partial_results`
* **Failed**: `lost`, `sample_error`, `failure_to_deliver_to_customer`, `failure_to_deliver_to_lab`

## Collection method-specific rules

<Tabs>
  <Tab title="Walk-In Tests">
    **✅ Can cancel:**

    * `received.walk_in_test.ordered`
    * `received.walk_in_test.requisition_created`
    * `received.walk_in_test.requisition_bypassed`
    * `collecting_sample.walk_in_test.appointment_scheduled`
    * `collecting_sample.walk_in_test.appointment_cancelled`
    * `collecting_sample.walk_in_test.appointment_pending`

    **❌ Cannot cancel once collection begins:**

    * `cancelled.walk_in_test.cancelled` - Already cancelled
    * `completed.walk_in_test.completed` - Order completed
    * `sample_with_lab.walk_in_test.partial_results` - Has partial results
    * `collecting_sample.walk_in_test.redraw_available` - Order marked as final but has missing results
    * `failed.walk_in_test.sample_error` - Sample error occurred

    <Info>Cancelling a walk-in test order does **not** automatically cancel PSC appointments to prevent a poor patient experience, as it could lead to a patient arriving and their appointment being cancelled. Use the [PSC appointment cancellation endpoint](/api-reference/lab-testing/psc-scheduling/appointment-psc-cancelling) if you need to cancel the appointment separately. Orders can be reinstated by our support team if a patient shows up for an appointment related to a cancelled order.</Info>
  </Tab>

  <Tab title="At-Home Phlebotomy">
    **✅ Can cancel:**

    * `received.at_home_phlebotomy.ordered`
    * `received.at_home_phlebotomy.requisition_created`
    * `received.at_home_phlebotomy.requisition_bypassed`
    * `collecting_sample.at_home_phlebotomy.appointment_pending`
    * `collecting_sample.at_home_phlebotomy.appointment_scheduled`
    * `collecting_sample.at_home_phlebotomy.appointment_cancelled`
    * `collecting_sample.at_home_phlebotomy.draw_completed`

    **❌ Cannot cancel once collection begins:**

    * `cancelled.at_home_phlebotomy.cancelled` - Already cancelled
    * `completed.at_home_phlebotomy.completed` - Order completed
    * `sample_with_lab.at_home_phlebotomy.partial_results` - Has partial results
    * `failed.at_home_phlebotomy.sample_error` - Sample error occurred

    <Warning>You can cancel the order directly - the system will automatically cancel any scheduled at-home phlebotomy appointments. Appointments cancelled with less than 24 hours' notice are non-refundable.</Warning>
  </Tab>

  <Tab title="Testkits">
    **✅ Can cancel:**

    * `received.testkit.ordered`
    * `received.testkit.awaiting_registration`
    * `received.testkit.requisition_created`
    * `received.testkit.requisition_bypassed`
    * `received.testkit.testkit_registered`

    **❌ Cannot cancel once shipped:**

    * `collecting_sample.testkit.out_for_delivery`
    * `collecting_sample.testkit.transit_customer`
    * `collecting_sample.testkit.with_customer`
    * `collecting_sample.testkit.transit_lab`
    * `sample_with_lab.testkit.delivered_to_lab`

    <Warning>Testkits become non-cancellable once they enter the shipping phase to avoid logistics costs. Testkits typically ship within the same business day if orders are placed before 1pm EST.</Warning>
  </Tab>

  <Tab title="On-Site Collection">
    **✅ Can cancel:**

    * `received.on_site_collection.ordered`
    * `received.on_site_collection.requisition_created`
    * `received.on_site_collection.requisition_bypassed`
    * `collecting_sample.on_site_collection.draw_completed`

    **❌ Cannot cancel once collection begins:**

    * `cancelled.on_site_collection.cancelled` - Already cancelled
    * `completed.on_site_collection.completed` - Order completed
    * `sample_with_lab.on_site_collection.partial_results` - Has partial results
    * `failed.on_site_collection.sample_error` - Sample error occurred
  </Tab>
</Tabs>

## How to Cancel

### Cancel Appointment (Optional - For Reference Only)

<Info>
  **Note**: You can skip this step and go directly to [cancelling the order](#cancel-the-order). The system will automatically cancel at-home phlebotomy appointments when you cancel the order, but **will not** automatically cancel walk-in PSC appointments to avoid a poor patient experience where they arrive and their appointment is cancelled.
</Info>

If you need to cancel only the appointment (without cancelling the entire order), use the appropriate endpoint for your collection method:

* **At-home phlebotomy**: [Appointment Cancellation endpoint](/api-reference/lab-testing/at-home-phlebotomy/appointment-cancelling)
* **Walk-in tests (PSC)**: [PSC Appointment Cancellation endpoint](/api-reference/lab-testing/psc-scheduling/appointment-psc-cancelling)

#### At-Home Phlebotomy Appointment Cancellation

<CodeGroup>
  ```bash cURL theme={null}
  curl --request PATCH \
       --url '{{BASE_URL}}/v3/order/<order_id>/phlebotomy/appointment/cancel' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: {YOUR_KEY}' \
       --data '{"cancellation_reason_id": "7dfd7da5-ed6e-40bb-a7e4-c8003f0c10a9"}'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.cancel_phlebotomy_appointment(
      "<order_id>",
      cancellation_reason_id="<cancellation_reason_id>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.cancelPhlebotomyAppointment({
      orderId: "<order_id>",
      cancellationReasonId: "<cancellation_reason_id>",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.ApiApiV1EndpointsVitalApiLabTestingOrdersHelpersAppointmentCancelRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().cancelPhlebotomyAppointment(
      "<order_id>",
      ApiApiV1EndpointsVitalApiLabTestingOrdersHelpersAppointmentCancelRequest.builder()
          .cancellationReasonId("<cancellation_reason_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CancelPhlebotomyAppointment(context.TODO(), &junction.ApiApiV1EndpointsVitalApiLabTestingOrdersHelpersAppointmentCancelRequest{
      OrderId:              "<order_id>",
      CancellationReasonId: "<cancellation_reason_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

#### Walk-In Test (PSC) Appointment Cancellation

For walk-in tests scheduled at Patient Service Centers, use the PSC appointment cancellation endpoint:

<CodeGroup>
  ```bash cURL theme={null}
  curl --request DELETE \
       --url '{{BASE_URL}}/v3/order/<order_id>/appointment/psc/cancel' \
       --header 'accept: application/json' \
       --header 'x-vital-api-key: {YOUR_KEY}'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.cancel_psc_appointment(
      "<order_id>",
      cancellation_reason_id="<cancellation_reason_id>",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.cancelPscAppointment({
      orderId: "<order_id>",
      cancellationReasonId: "<cancellation_reason_id>",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.VitalCoreClientsLabTestGetlabsSchemaAppointmentCancelRequest;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().cancelPscAppointment(
      "<order_id>",
      VitalCoreClientsLabTestGetlabsSchemaAppointmentCancelRequest.builder()
          .cancellationReasonId("<cancellation_reason_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CancelPscAppointment(context.TODO(), &junction.VitalCoreClientsLabTestGetlabsSchemaAppointmentCancelRequest{
      OrderId:              "<order_id>",
      CancellationReasonId: "<cancellation_reason_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

### Cancel the Order

Use the [order cancellation endpoint](/api-reference/lab-testing/cancel-order) to cancel any lab test order.

<Info>
  **Automatic Appointment Handling**:

  * **At-home phlebotomy**: Appointments are automatically cancelled when you cancel the order
  * **Walk-in PSC**: Appointments are **not** automatically cancelled to prevent poor patient experience if they show up. Orders can be reinstated if needed.
</Info>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request DELETE \
       --url {{BASE_URL}}/v3/lab_test/<order_id>/cancel \
       --header 'Accept: application/json' \
       --header 'x-vital-api-key: <API_KEY>' \
       --header 'Content-Type: application/json'
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.cancel_order("<order_id>")
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.cancelOrder({ orderId: "<order_id>" });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().cancelOrder("<order_id>");
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.CancelOrder(context.TODO(), &junction.CancelOrderLabTestsRequest{
      OrderId: "<order_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

## Error Handling

When attempting to cancel an order that cannot be cancelled, you'll receive an error response:

```json Error Response theme={null}
{
  "error": "Bad Request",
  "message": "Transition from current_status to cancelled is not allowed",
  "status_code": 400
}
```

**Common reasons for cancellation failure:**

* Order is already in a terminal state (completed, failed, or cancelled)
* Testkit has already shipped
* Sample has been collected or is with the lab
* Results are already available (partial or complete)

## Checking Cancellation Eligibility

Before attempting cancellation, check the order's current status using the [get order endpoint](/api-reference/lab-testing/get-order):

```bash Check Order Status theme={null}
curl --request GET \
     --url {{BASE_URL}}/v3/order/<order_id> \
     --header 'x-vital-api-key: <API_KEY>'
```

Look for these indicators in the response:

* `status` field shows the current high-level status
* `events` array shows the detailed state progression
* Orders in early states (`ordered`, `requisition_created`) are typically cancellable
* Orders with `cancelled`, `completed`, or `failed` status cannot be cancelled

## Response Examples

### Successful Cancellation

```json Cancellation Success Response theme={null}
{
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "patient_details": {"dob": "2020-01-01", "gender": "male"},
        "patient_address": {
            "receiver_name": "John Doe",
            "first_line": "123 Main St.",
            "second_line": "Apt. 208",
            "city": "San Francisco",
            "state": "CA",
            "zip": "91189",
            "country": "United States",
            "phone_number": "+1123456789",
        },
        "details": {
            "type": "testkit",
            "data": {
                "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
                "shipment": {
                    "id": "d55210cc-3d9f-4115-8262-5013f700c7be",
                    "outbound_tracking_number": "<outbound_tracking_number>",
                    "outbound_tracking_url": "<outbound_tracking_url>",
                    "inbound_tracking_number": "<inbound_tracking_number>",
                    "inbound_tracking_url": "<inbound_tracking_url>",
                    "outbound_courier": "usps",
                    "inbound_courier": "usps",
                    "notes": "<notes>",
                    "created_at": "2020-01-01T00:00:00.000Z",
                    "updated_at": "2020-01-01T00:00:00.000Z",
                },
                "created_at": "2020-01-01T00:00:00Z",
                "updated_at": "2020-01-01T00:00:00Z",
            },
        },
        "diagnostic_lab_test": {
            "name": "Lipids Panel",
            "description": "Cholesterol test",
            "method": "testkit",
        },
        "sample_id": "123456789",
        "notes": "This is a note",
        "created_at": "2020-01-01T00:00:00Z",
        "updated_at": "2020-01-01T00:00:00Z",
        "status": "cancelled",
        "events": [
            {
                "id": 1,
                "created_at": "2022-01-01T00:00:00Z",
                "status": "received.testkit.ordered",
            },
            {
                "id": 2,
                "created_at": "2022-01-01T00:00:00Z",
                "status": "cancelled.testkit.cancelled",
            }
        ],
}
```

### Successful Appointment Cancellation

```json Appointment Cancellation Response theme={null}
{
    "id": "413d7205-f8a9-42ed-aa4a-edb99e481ca0",
    "user_id": "202b2c2f-fb4c-44dc-a4f8-621186fde227",
    "address": {
        "first_line": "West Lincoln Street",
        "second_line": "",
        "city": "Phoenix",
        "state": "AZ",
        "zip_code": "85004",
        "unit": "14"
    },
    "location": {
        "lng": -112.0772235,
        "lat": 33.4421912
    },
    "start_at": "2023-05-17T20:00:00+00:00",
    "end_at": "2023-05-17T22:00:00+00:00",
    "iana_timezone": "America/Phoenix",
    "type": "phlebotomy",
    "provider": "getlabs",
    "status": "cancelled",
    "provider_id": "e89eb489-7382-4966-bb14-7ab4763eba6c",
    "can_reschedule": true
}
```

## Related Resources

* [Order Lifecycle Overview](/lab/workflow/lab-test-lifecycle)
* [API Reference: Cancel Order](/api-reference/lab-testing/cancel-order)
* [API Reference: Cancel At-Home Appointment](/api-reference/lab-testing/at-home-phlebotomy/appointment-cancelling)
* [API Reference: Cancel PSC Appointment](/api-reference/lab-testing/psc-scheduling/appointment-psc-cancelling)
* [Webhooks for Order Events](/lab/testkits/webhooks)

***

*For questions about specific cancellation scenarios, please contact support at [support@junction.com](mailto:support@junction.com)*


# Clinical Notes
Source: https://docs.junction.com/lab/workflow/clinical-comments

Add clinical notes to lab orders at the order level or as a Lab Account default for requisition forms.

Central labs (Labcorp, Quest, Sonora Quest and BioReference) allow you to pass in clinical notes. These are reflected in the Requisition form that is generated.

Junction also allows you to set these notes, on an order-by-order basis, or as a [Lab Account](/lab/overview/lab-accounts) default.

<Warning>
  The Lab Account default is an internal configuration, contact your CSM to set this up.
</Warning>

If your Lab Account has a default configured, then it will always be propagated to every order placed with the given Lab Account.

### Order Level Notes

You can also pass in order-specific notes that will be displayed in the requisition form.

This can be achieved via the `clinical_notes` field in the [Create Order endpoint](/api-reference/lab-testing/create-order).

### What happens when both the Order and the Lab Account have a clinical note?

When this happens, Junction *concatenates* both of the notes, so they are always displayed.

### Usage

Notes are new line separated (`\n`) strings. If you wish to provide more than one note, you can append a new line to your provided string.


# Creating a Lab Test
Source: https://docs.junction.com/lab/workflow/create-test

Create custom lab tests by selecting biomarker IDs and collection methods, then submit them for Junction approval before ordering.

Junction provides a way to create your own lab tests. A lab test is a collection or pre-set of orderable markers. These are made up of:

1. One or more marker IDs or Provider IDs.
2. The collection method: `testkit`, `walk_in_test`, `at_home_phlebotomy`, or `on_site_collection`.

Once a Lab Test is created and approved, you can use it when making an order.

<Warning>
  When you create a Lab Test, it is not immediately available. We need to validate and approve it on our side, before you can use it when making orders.
</Warning>

### Example

To begin, make a request to our [`GET /v3/lab_tests/markers`](/api-reference/lab-testing/biomarkers) endpoint, in order to choose the markers you want to test for.

```json Get all markers theme={null}
{
  "markers": [
    {
      "id": 1,
      "name": "17-OH Progesterone LCMS",
      "slug": "17-oh-progesterone-lcms",
      "description": "17-OH Progesterone LCMS",
      "lab_id": 1,
      "provider_id": "070085",
      "type": null,
      "unit": null,
      "price": "N/A"
    },
    {
      "id": 2,
      "name": "ABO Grouping",
      "slug": "abo-grouping",
      "description": "ABO Grouping",
      "lab_id": 1,
      "provider_id": "006056",
      "type": null,
      "unit": null,
      "price": "N/A"
    }
  ],
  "total": 2,
  "page": 1,
  "size": 2
}
```

With this information, you can create a lab test using [`POST /v3/lab_tests`](/api-reference/lab-testing/post-test).

As an example, let's say you wanted a test from `Labcorp`, with markers with provider IDs `006056` and `070085`:

```python Create a new test theme={null}
from junction import Junction, LabTestCollectionMethod
from junction.environment import JunctionEnvironment

client = Junction(
    api_key="YOUR_API_KEY",
    environment=JunctionEnvironment.SANDBOX,
)

data = client.lab_tests.create(
    name="Example test",
    description="Example test",
    method=LabTestCollectionMethod.AT_HOME_PHLEBOTOMY,
    provider_ids=["006056", "070085"],  # or use the marker ids e.g marker_ids=[1, 2]
)
```

<Note>
  It is possible to create a test with either the marker id or provider id. Provider ids are the recommended way, since these are shared across environments. Not all labs provide these, so in their absence, use the marker id.
</Note>

Once your lab test creation is successful you will receive the following response:

```json theme={null}
{
  "lab_test": {
    "name": "Example test",
    "description": "Example test",
    "sample_type": "serum",
    "method": "at_home_phlebotomy",
    "price": 10,
    "is_active": false,
    "status": "pending_approval",
    "lab": {
      "slug": "labcorp",
      "name": "LabCorp",
      "first_line_address": "123 Main St",
      "city": "San Francisco",
      "zipcode": "91789"
    },
    "markers": [
      {
        "name": "17-OH Progesterone LCMS",
        "slug": "17-oh-progesterone-lcms",
        "description": "17-OH Progesterone LCMS"
      },
      {
        "name": "ABO Grouping",
        "slug": "abo-grouping",
        "description": "ABO Grouping"
      }
    ]
  }
}
```

Note that `status` is set to `pending_approval`. You can verify when it changes to `active` by calling the [lab tests endpoint](/api-reference/lab-testing/tests-paginated). The `is_active` field is deprecated; use `status` instead.


# Importing an Order
Source: https://docs.junction.com/lab/workflow/importing-order

Import externally-placed lab orders into Junction for centralized tracking and result delivery using the correct sample or requisition ID.

<Card icon="person-digging">
  This feature is in **closed beta**.

  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

If you want to track orders with Junction that weren't originally placed with Junction, you can import an order.

<Warning>
  It's crucial that imported orders are created with the correct sample ID. This is the **requisition number** or **control number** for Labcorp orders, or the **lab reference ID** for Quest orders. If in doubt, contact your Junction Customer Success Manager to confirm what you should supply for the sample ID.

  The sample ID must also uniquely identify the order. If you supply an ID that clashes with an existing order, that will result in a validation error.
</Warning>

An imported order can be cancelled, receive results, and function similarly to orders originally placed with Junction, but with one important exception: a requisition will not be created. This means you cannot use the [endpoint to retrieve the requisition](/api-reference/lab-testing/requisition-pdf) for an imported order since Junction does not have access to it.

After an order is successfully imported, it will be in the `requisition_bypassed` state (for example, an imported testkit order would have a `received.testkit.requisition_bypassed` status). This status supports transitioning to the same states that `requisition_created` does.

<Info>
  Since import skips the regular order creation flow, no communications are sent to patients about the order being created, but communications are sent for subsequent events that might occur (order completion, for example).
</Info>

### Example

<CodeGroup>
  ```python Python theme={null}
  from junction import Billing, Gender, Junction, LabTestCollectionMethod, OrderSetRequest, PatientAddress, PatientDetailsWithValidation, PhysicianCreateRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.lab_tests.import_order(
      user_id="<user_id>",
      billing_type=Billing.CLIENT_BILL,
      order_set=OrderSetRequest(lab_test_ids=["<lab_test_id>"]),
      collection_method=LabTestCollectionMethod.WALK_IN_TEST,
      physician=PhysicianCreateRequest(first_name="Jane", last_name="Doe", npi=""),
      patient_details=PatientDetailsWithValidation(
          first_name="John",
          last_name="Doe",
          dob="2020-01-01",
          gender=Gender.MALE,
          phone_number="+1123456789",
          email="email@email.com",
      ),
      patient_address=PatientAddress(
          receiver_name="John Doe",
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
          phone_number="+1123456789",
      ),
      sample_id="1234567890",
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.labTests.importOrder({
      userId: "<user_id>",
      billingType: "client_bill",
      orderSet: { labTestIds: ["<lab_test_id>"] },
      collectionMethod: "walk_in_test",
      physician: { firstName: "Jane", lastName: "Doe", npi: "" },
      patientDetails: {
          firstName: "John",
          lastName: "Doe",
          dob: "2020-01-01",
          gender: "male",
          phoneNumber: "+1123456789",
          email: "email@email.com",
      },
      patientAddress: {
          receiverName: "John Doe",
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
          phoneNumber: "+1123456789",
      },
      sampleId: "1234567890",
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.labtests.requests.ImportOrderBody;
  import com.junction.api.types.Billing;
  import com.junction.api.types.Gender;
  import com.junction.api.types.LabTestCollectionMethod;
  import com.junction.api.types.OrderSetRequest;
  import com.junction.api.types.PatientAddress;
  import com.junction.api.types.PatientDetailsWithValidation;
  import com.junction.api.types.PhysicianCreateRequest;
  import java.util.List;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.labTests().importOrder(
      ImportOrderBody.builder()
          .userId("<user_id>")
          .billingType(Billing.CLIENT_BILL)
          .orderSet(OrderSetRequest.builder()
              .labTestIds(List.of("<lab_test_id>"))
              .build())
          .collectionMethod(LabTestCollectionMethod.WALK_IN_TEST)
          .patientDetails(PatientDetailsWithValidation.builder()
              .firstName("John")
              .lastName("Doe")
              .dob("2020-01-01")
              .gender(Gender.MALE)
              .phoneNumber("+1123456789")
              .email("email@email.com")
              .build())
          .patientAddress(PatientAddress.builder()
              .receiverName("John Doe")
              .firstLine("123 Main St.")
              .city("San Francisco")
              .state("CA")
              .zip("91189")
              .country("US")
              .build())
          .sampleId("1234567890")
          .physician(PhysicianCreateRequest.builder()
              .firstName("Jane")
              .lastName("Doe")
              .npi("")
              .build())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.LabTests.ImportOrder(context.TODO(), &junction.ImportOrderBody{
      UserId:           "<user_id>",
      BillingType:      junction.BillingClientBill,
      OrderSet:         &junction.OrderSetRequest{LabTestIds: []string{"<lab_test_id>"}},
      CollectionMethod: junction.LabTestCollectionMethodWalkInTest,
      Physician: &junction.PhysicianCreateRequest{
          FirstName: "Jane",
          LastName:  "Doe",
          Npi:       "",
      },
      PatientDetails: &junction.PatientDetailsWithValidation{
          FirstName:   "John",
          LastName:    "Doe",
          Dob:         "2020-01-01",
          Gender:      junction.GenderMale,
          PhoneNumber: "+1123456789",
          Email:       "email@email.com",
      },
      PatientAddress: &junction.PatientAddress{
          ReceiverName: "John Doe",
          FirstLine:    "123 Main St.",
          City:         "San Francisco",
          State:        "CA",
          Zip:          "91189",
          Country:      "US",
      },
      SampleId: "1234567890",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

This example assumes you provide your own physician. The physician must be provided if your team configuration is set up as delegated. The billing type provided must also be appropriate to your team configuration. These aspects will be validated when making the request.

Since the request represents an existing order, we don't perform strict address validation on the patient address, to make it easier to import orders exactly as they were placed in the original interface.

The response object schema is the same as [creating an order](/api-reference/lab-testing/create-order).


# Lab Test Lifecycle
Source: https://docs.junction.com/lab/workflow/lab-test-lifecycle

Understand the namespaced status system for lab test orders with high-level and modality-specific low-level status transitions.

## Introduction

We encapsulate our lab test modalities in the `order` object. This is the main object you will interact with, as it contains all the information related to that lab test. An order has a high-level status which represents where the order is throughout its lifecycle, e.g., received/delivered/completed/cancelled, etc.

The high-level status is the same across test modalities. On top of that, each modality has its own sub-statuses to represent lifecycle stages specific to that modality. For example, test-kits involve shipping of the box, so we have a whole set of statuses to track the shipment.

<Warning>
  **Please note**: To ensure future compatibility, we ask that you avoid exhaustive matching on enum values such as an order's status. We may introduce new statuses (and other enum values) over time, and code that assumes all current values are exhaustive could break or fail to compile with SDK upgrades.

  To stay compatible and benefit from future enhancements, treat unknown values gracefully—for example, by using default cases or limiting checks to only the values your integration depends on.
</Warning>

## Statuses

Statuses are namespaced with the following format:

`[HIGH-LEVEL STATUS].[TEST MODALITY].[LOW-LEVEL STATUS]`

As an example, the status `collecting_sample.at_home_phlebotomy.appointment_scheduled` means the patient has scheduled an at-home phlebotomy appointment.

### High-level statuses

The high-level statuses of an order are represented by `order.status`:

* `received`: we received the order, stored it into our system, and started processing it.
* `collecting_sample`: these track collecting the sample from the patient. For test-kits, these track the shipment of the kit. For at-home phlebotomy and walk-in tests, these track appointment scheduling and management.
* `sample_with_lab`: the lab received the sample and is currently analyzing it.
* `completed`: the order is complete and the results are ready.
* `cancelled`: the order has been cancelled, by you or the patient.
* `failed`: we failed to process the order.

Modality-specific sub-statuses are encapsulated in the `order.events` list. These include a list of all the events for that test:

```json Test kit order status theme={null}
{
        ...,
        "status": "cancelled",
        "events": [
            {
                "id": 1,
                "created_at": "2022-01-01T00:00:00Z",
                "status": "received.testkit.ordered",
            },
            {
                "id": 2,
                "created_at": "2022-01-01T00:00:00Z",
                "status": "received.testkit.requisition_created",
            },
            {
                "id": 3,
                "created_at": "2022-01-01T00:00:00Z",
                "status": "collecting_sample.testkit.transit_customer"
            },
        ],
}
```

Below is a full list of all the available statuses by test modality. The names should be self-descriptive. Please do not hesitate to contact us if you need any additional clarification!

### Test-kit statuses

* `received.testkit.ordered`
* `received.testkit.awaiting_registration`
* `received.testkit.testkit_registered`
* `received.testkit.requisition_created`
* `received.testkit.requisition_bypassed`
* `collecting_sample.testkit.transit_customer`
* `collecting_sample.testkit.out_for_delivery`
* `collecting_sample.testkit.with_customer`
* `collecting_sample.testkit.transit_lab`
* `collecting_sample.testkit.problem_in_transit_customer`
* `collecting_sample.testkit.problem_in_transit_lab`
* `sample_with_lab.testkit.delivered_to_lab`
* `sample_with_lab.testkit.lab_processing_blocked`
* `completed.testkit.completed`
* `failed.testkit.failure_to_deliver_to_customer`
* `failed.testkit.failure_to_deliver_to_lab`
* `failed.testkit.sample_error`
* `failed.testkit.lost`
* `cancelled.testkit.cancelled`
* `cancelled.testkit.do_not_process`

### Walk-in visit statuses

* `received.walk_in_test.ordered`
* `received.walk_in_test.requisition_created`
* `received.walk_in_test.requisition_bypassed`
* `collecting_sample.walk_in_test.appointment_pending`
* `collecting_sample.walk_in_test.appointment_scheduled`
* `collecting_sample.walk_in_test.appointment_cancelled`
* `collecting_sample.walk_in_test.redraw_available`
* `sample_with_lab.walk_in_test.partial_results`
* `completed.walk_in_test.completed`
* `failed.walk_in_test.sample_error`
* `cancelled.walk_in_test.cancelled`

### At-home phlebotomy statuses

* `received.at_home_phlebotomy.ordered`
* `received.at_home_phlebotomy.requisition_created`
* `received.at_home_phlebotomy.requisition_bypassed`
* `collecting_sample.at_home_phlebotomy.appointment_pending`
* `collecting_sample.at_home_phlebotomy.appointment_scheduled`
* `collecting_sample.at_home_phlebotomy.draw_completed`
* `collecting_sample.at_home_phlebotomy.appointment_cancelled`
* `sample_with_lab.at_home_phlebotomy.partial_results`
* `completed.at_home_phlebotomy.completed`
* `failed.at_home_phlebotomy.sample_error`
* `cancelled.at_home_phlebotomy.cancelled`

### On-site collection statuses (currently in beta testing)

* `received.on_site_collection.ordered`
* `received.on_site_collection.requisition_created`
* `received.on_site_collection.requisition_bypassed`
* `collecting_sample.on_site_collection.draw_completed`
* `sample_with_lab.on_site_collection.partial_results`
* `completed.on_site_collection.completed`
* `failed.on_site_collection.sample_error`
* `cancelled.on_site_collection.cancelled`


# Ordering a Registrable Testkit
Source: https://docs.junction.com/lab/workflow/order-registrable-testkit

Use registrable testkits to ship kits without a bound patient, deferring patient registration until the testkit is activated.

Under the [standard Testkit ordering flow](/api-reference/lab-testing/create-order), you place a Testkit order for a specific patient. The resulting Testkit is bound to that patient, and the Testkit cannot be passed on to someone else.

With Registrable Testkits, Junction offers a different option — Testkits not bound to any specific patient can be ordered to a specific household address. The patient registration process is deferred until an actual patient intends to use the Testkit.

For guidance on self-ordering, proxy ordering, and bulk inventory workflows, see [Proxy and Inventory Workflows for Registrable Testkits](/lab/workflow/registrable-testkit-proxy-inventory-workflows).

<Warning>
  Note that this only applies to Testkits, not Walk-in Tests or At-home Phlebotomy.
</Warning>

This involves two steps:

1. Ordering the Testkit.
2. Registering a patient.

After step 1, the order is sent to the requested address and generates all the same webhooks as a regular order would. However, it is stuck in the `received.testkit.awaiting_registration` state, meaning that no requisition form is generated for this order, and no results can be obtained until step 2 is done.

After step 2, the order flow resumes as normal, the patient sends the `testkit` to the lab, the order is progressed to the `received.testkit.testkit_registered` state and, again, all the same webhooks as in the regular order flow are dispatched.

### Example

To make an order, make a request to our [`POST /v3/order/testkit`](/api-reference/lab-testing/create-unregistered-order), to fulfill step 1.

<CodeGroup>
  ```python Python theme={null}
  from junction import Junction, ShippingAddressWithValidation
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.testkit.create_order(
      user_id="<user_id>",
      lab_test_id="<lab_test_id>",
      shipping_details=ShippingAddressWithValidation(
          receiver_name="John Doe",
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
          phone_number="+11234567890",
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.testkit.createOrder({
      userId: "<user_id>",
      labTestId: "<lab_test_id>",
      shippingDetails: {
          receiverName: "John Doe",
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
          phoneNumber: "+11234567890",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.testkit.requests.CreateRegistrableTestkitOrderRequest;
  import com.junction.api.types.ShippingAddressWithValidation;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.testkit().createOrder(
      CreateRegistrableTestkitOrderRequest.builder()
          .userId("<user_id>")
          .labTestId("<lab_test_id>")
          .shippingDetails(ShippingAddressWithValidation.builder()
              .receiverName("John Doe")
              .firstLine("123 Main St.")
              .city("San Francisco")
              .state("CA")
              .zip("91189")
              .country("US")
              .phoneNumber("+11234567890")
              .build())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Testkit.CreateOrder(context.TODO(), &junction.CreateRegistrableTestkitOrderRequest{
      UserId:    "<user_id>",
      LabTestId: "<lab_test_id>",
      ShippingDetails: &junction.ShippingAddressWithValidation{
          ReceiverName: "John Doe",
          FirstLine:    "123 Main St.",
          City:         "San Francisco",
          State:        "CA",
          Zip:          "91189",
          Country:      "US",
          PhoneNumber:  "+11234567890",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

The `testkit` is now ordered, and will be sent to the specified address. Once delivered, it can be kept until its expiration.

Once the `testkit` is with its intended patient, it should be registered (step 2).

In order to register, Junction requires a `sample_id`, which is found within the `testkit` itself. This is a unique identifier.
Junction also requires the patient details and address.
Besides this, if using Junction's physician network, then supplying the consents field is required. If providing your own physician, then the physician information is required.

For this example, we will assume the use of your own physician.

To register a `testkit` order, make a request to our [`POST /v3/order/testkit/register`](/api-reference/lab-testing/register-order), to fulfill step 2.

<CodeGroup>
  ```python Python theme={null}
  from junction import Gender, Junction, PatientAddressWithValidation, PatientDetailsWithValidation, PhysicianCreateRequestBase
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.testkit.register(
      user_id="<user_id>",
      sample_id="123123123",
      patient_details=PatientDetailsWithValidation(
          first_name="John",
          last_name="Doe",
          dob="2020-01-01",
          gender=Gender.MALE,
          phone_number="+1123456789",
          email="email@email.com",
      ),
      patient_address=PatientAddressWithValidation(
          first_line="123 Main St.",
          second_line="Apt. 208",
          city="San Francisco",
          state="CA",
          zip="91189",
          country="US",
      ),
      physician=PhysicianCreateRequestBase(
          first_name="Doctor",
          last_name="Doc",
          npi="123123123",
      ),
  )
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.testkit.register({
      userId: "<user_id>",
      sampleId: "123123123",
      patientDetails: {
          firstName: "John",
          lastName: "Doe",
          dob: "2020-01-01",
          gender: "male",
          phoneNumber: "+1123456789",
          email: "email@email.com",
      },
      patientAddress: {
          firstLine: "123 Main St.",
          secondLine: "Apt. 208",
          city: "San Francisco",
          state: "CA",
          zip: "91189",
          country: "US",
      },
      physician: {
          firstName: "Doctor",
          lastName: "Doc",
          npi: "123123123",
      },
  });
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.testkit.requests.RegisterTestkitRequest;
  import com.junction.api.types.Gender;
  import com.junction.api.types.PatientAddressWithValidation;
  import com.junction.api.types.PatientDetailsWithValidation;
  import com.junction.api.types.PhysicianCreateRequestBase;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.testkit().register(
      RegisterTestkitRequest.builder()
          .sampleId("123123123")
          .patientDetails(PatientDetailsWithValidation.builder()
              .firstName("John")
              .lastName("Doe")
              .dob("2020-01-01")
              .gender(Gender.MALE)
              .phoneNumber("+1123456789")
              .email("email@email.com")
              .build())
          .patientAddress(PatientAddressWithValidation.builder()
              .firstLine("123 Main St.")
              .city("San Francisco")
              .state("CA")
              .zip("91189")
              .country("US")
              .build())
          .userId("<user_id>")
          .physician(PhysicianCreateRequestBase.builder()
              .firstName("Doctor")
              .lastName("Doc")
              .npi("123123123")
              .build())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  userId := "<user_id>"
  response, err := c.Testkit.Register(context.TODO(), &junction.RegisterTestkitRequest{
      UserId:   &userId,
      SampleId: "123123123",
      PatientDetails: &junction.PatientDetailsWithValidation{
          FirstName:   "John",
          LastName:    "Doe",
          Dob:         "2020-01-01",
          Gender:      junction.GenderMale,
          PhoneNumber: "+1123456789",
          Email:       "email@email.com",
      },
      PatientAddress: &junction.PatientAddressWithValidation{
          FirstLine: "123 Main St.",
          City:      "San Francisco",
          State:     "CA",
          Zip:       "91189",
          Country:   "US",
      },
      Physician: &junction.PhysicianCreateRequestBase{
          FirstName: "Doctor",
          LastName:  "Doc",
          Npi:       "123123123",
      },
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

Your `testkit` order is now registered, and the order flow should resume as normal. For more information regarding the lifecycle of a test, refer to the [lab test lifecycle](/lab/workflow/lab-test-lifecycle) page.


# Order Requirements
Source: https://docs.junction.com/lab/workflow/order-requirements

Review all requirements for placing a lab test order including patient consents, physician options, and patient name validation rules.

### Consents

When you order a lab test through our API, you may have to collect consents from the patient and forward them to us. The following consents may include:

* `hipaa-authorization`.
* `terms-of-use`.
* `telehealth-informed-consent`.

### Physician

As mentioned [in the introduction](/lab/overview/introduction#features), you don't need to have your own Physician to place orders with Junction. When you [order a test](/lab/overview/quickstart#3-placing-an-order), you can pass an optional `physician` argument.
If provided, the order will use your physician. If not, your order will go through with Junction's physician network. For more information, please check [our lab testing page](https://tryvital.io/labs) and [book an introductory call with us](https://cal.com/team/vital/discovery-call).

### Patient Name Validation

Patient names must conform to a specific regex pattern due to lab restrictions. This validation applies to all name fields including `first_name` and `last_name`.

**Regex Pattern:**

```
^([a-zA-Z0-9]{1})([a-zA-Z0-9-.,']*(\s[a-zA-Z0-9-.,']+)*[a-zA-Z0-9-.,']?)$
```

**Validation Rules:**

* Must start with an alphanumeric character (not space, hyphen, or punctuation)
* Only allows letters (a-z, A-Z), numbers (0-9), hyphens (-), periods (.), commas (,), apostrophes (')
* Spaces are only allowed between words
* Cannot start or end with a space
* Cannot have consecutive spaces

**Valid Name Examples:**

* `John`
* `Mary-Jane`
* `O'Connor`
* `Dr. Smith`
* `Jean-Pierre`

**Invalid Name Examples:**

* ` John` (starts with space)
* `John ` (ends with space)
* `-John` (starts with hyphen)
* `John@Smith` (contains @)
* `José` (contains accented character)

### Minors

Ordering for minors (under 18 years old) requires a specific configuration, which you can request from your Junction Customer Success Manager.

Once enabled, you are able to order for a minor patient by providing information regarding the medical proxy.

This information can be supplied at [ordering time](/api-reference/lab-testing/create-order#body-patient-details-medical-proxy), or at any time by updating the [user demographics](/api-reference/user/upsert-info#body-medical-proxy).


# Order Transactions
Source: https://docs.junction.com/lab/workflow/order-transactions

Understand the importance of order transactions and how they are used.

## Order Transactions and Initial Orders

Junction uses the concept of **order transactions** to group related orders together and provide unified results.
An order transaction represents a single testing journey, which will include, at a minimum, one `initial` order.

An order transaction is created whenever a new `initial` order is created and a link is established between the two entities.

* `initial` orders are orders that are not derived from another order. This is denoted in the order's `origin` field.

* Order transactions have a `status` field, which can be `active`, `completed`, or `cancelled`.
  * `active` - one or more orders belonging to the order transaction are in progress
  * `completed` - all expected lab work for this transaction is completed and no further updates are expected
  * `cancelled` - all orders belonging to the order transaction have been cancelled

* An order can only ever belong to one order transaction

We recommend that you store both the `order_id` and `order_transaction_id` when placing an order in Junction.
Both of these IDs will be needed when implementing some of Junction's current and future features like [redraws](/lab/workflow/redraws).

## API Endpoints

The following endpoints are available to retrieve order transaction information and combined results for all orders within a transaction:

* `GET /v3/order_transaction/{order_transaction_id}` - Get an order transaction's details and list of orders
* `GET /v3/order_transaction/{order_transaction_id}/result` - Get combined results for an order transaction in JSON format
* `GET /v3/order_transaction/{order_transaction_id}/result/pdf` - Get combined results for an order transaction in PDF format

Responses for the following endpoints also include order transaction details:

* `GET /v3/orders` - Get a list of filtered orders (including the ability to filter by order transaction ID)
* `GET /v3/order/{order_id}` - Get an individual order
* `GET /v3/order/{order_id}/result` - Get an individual order's results


# Ordering
Source: https://docs.junction.com/lab/workflow/ordering

Learn the ordering concepts including markers, lab tests, and the various order_set field combinations for placing lab test orders.

# Concepts

Junction has a series of concepts to grasp regarding ordering:

* [Lab Tests](/lab/workflow/create-test)
* [Lifecycle of an Order](/lab/workflow/lab-test-lifecycle)
* [Order Transactions](/lab/workflow/order-transactions)
* [AOE](/lab/workflow/aoe)
* [Registrable Kits](/lab/workflow/order-registrable-testkit)
* [Scheduled Orders](/lab/workflow/scheduled-orders)

In this document, we will focus on orderable panels/biomarkers.

### Markers

[*Markers*](/api-reference/lab-testing/biomarkers) are individual, orderable tests, at the lab level. So, for example, at *Labcorp* you can order a `Lipid Panel` test and a `Vitamin D` test, a `panel` and a `biomarker` respectively. At Junction, these are both referred to as **markers**.

### Lab Tests

[*Lab Tests*](/api-reference/lab-testing/post-test) are a collection of markers, or a preset combination of markers, that you can order. Using the example above, you can create a `Labcorp Lipid Panel and Vitamin D` lab test. You can then place orders at Junction using this preset. This is useful for situations where you repeatedly want to order the same markers.

# Ordering

When placing an [Order](/api-reference/lab-testing/create-order), you will see an `order_set` field. This is what defines what markers will be ordered.

There are multiple combinations allowed with this field, so let's explore all of them.

### Ordering a *Lab Test*

When ordering from **one** previously created *Lab Test*, the `order_set` field should be populated as follows:

<CodeGroup>
  ```python Python theme={null}
  from junction import Junction, OrderSetRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.create_order(
      order_set=OrderSetRequest(
          lab_test_ids=["<lab_test_id>"],
      ),
      # ...
  )
  ```
</CodeGroup>

### Ordering from multiple *Lab Tests*

You may want to combine two or more existing Lab Tests without creating a new one. This is possible by doing:

<CodeGroup>
  ```python Python theme={null}
  from junction import Junction, OrderSetRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.create_order(
      order_set=OrderSetRequest(
          lab_test_ids=["<lab_test_id_1>", "<lab_test_id_2>"],
      ),
      # ...
  )
  ```
</CodeGroup>

### Ordering without a *Lab Test*

This is what Junction calls `à la carte` ordering. You may want to order from the marker compendium without creating a preset.

<Note>
  `à la carte` ordering is generally available to all teams. Not all markers are enabled for `à la carte` ordering — see [À La Carte Markers](#à-la-carte-markers) below.
</Note>

It is possible to order `à la carte` using Junction `marker_ids` or the lab's `provider_ids`.

<CodeGroup>
  ```python Python theme={null}
  from junction import AddOnOrder, Junction, LabTestCollectionMethod, OrderSetRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.create_order(
      order_set=OrderSetRequest(
          add_on=AddOnOrder(
              provider_ids=["322022"],
              # marker_ids=[1],
          ),
      ),
      collection_method=LabTestCollectionMethod.WALK_IN_TEST,
      # ...
  )
  ```
</CodeGroup>

### Order *Lab Tests* with extra *Markers*

You may also add extra **markers** to an order. For example, you want to order the `Labcorp Lipid Panel and Vitamin D` but for this particular patient, you also want to order a `CBC Panel`.

<CodeGroup>
  ```python Python theme={null}
  from junction import AddOnOrder, Junction, LabTestCollectionMethod, OrderSetRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.create_order(
      order_set=OrderSetRequest(
          lab_test_ids=["<lab_test_id>"],
          add_on=AddOnOrder(
              provider_ids=["322022"],
              # marker_ids=[1],
          ),
      ),
      collection_method=LabTestCollectionMethod.WALK_IN_TEST,
      # ...
  )
  ```
</CodeGroup>

<Note>
  When supplying the `add_on` field, it is always required to provide the `collection_method` field.
</Note>

## Collection Method

As further explored in the documentation, Junction also has the concept of *Collection Method*. Junction currently supports four methods: [`At Home Phlebotomy`](/lab/at-home-phlebotomy/overview), [`Walk In`](/lab/walk-in/overview), [`Testkits`](/lab/testkits/overview), and [`On-Site Collection`](/lab/on-site-collection/overview).
When ordering, you must select one of these, either at lab test creation, or at ordering time.

<CodeGroup>
  ```python Python theme={null}
  from junction import Junction, LabTestCollectionMethod, OrderSetRequest
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  client.lab_tests.create_order(
      order_set=OrderSetRequest(
          lab_test_ids=["<lab_test_id>"],
      ),
      collection_method=LabTestCollectionMethod.WALK_IN_TEST,
      # ...
  )
  ```
</CodeGroup>

## À La Carte Markers

As mentioned above, not all `markers` are `à la carte` orderable. You can find which ones are orderable via the [GET /v3/lab\_tests/markers](/api-reference/lab-testing/biomarkers).

<CodeGroup>
  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  markers = client.lab_tests.get_markers(
      name="322022",
      a_la_carte_enabled=True,
  )

  if not all(m.a_la_carte_enabled for m in markers.markers):
      raise Exception("Markers not a_la_carte_enabled")
  ```
</CodeGroup>

## Error Cases

With the existence of various allowed combinations with the `order_set` field, there are many validations that are done server side. Here are some of the errors you can expect to encounter:

### 400 Bad Request

1. `collection_method must be set if add_on is set`: When the `add_on` field is supplied, you must provide the `collection_method`.
2. `marker_ids or provider_ids must be set in add_on`: One of `marker_ids` or `provider_ids` must be set, if the `add_on` field is provided.
3. `cannot set both marker_ids and provider_ids in add_on`: Similarly, only one of these two fields can be provided.
4. `cannot order lab_tests from multiple labs`: You can only order multiple lab tests from the same lab.
5. `cannot order lab tests with multiple collection methods`: You must supply a `collection_method` if ordering multiple lab tests with multiple collection methods.
6. `Lab <lab name> does not allow gender <gender value>`: The associated lab restricts which patient genders it accepts. This restriction applies to the entire lab, not to individual lab tests.


# Partial Result Notifications
Source: https://docs.junction.com/lab/workflow/partials

Configure partial result handling to receive webhook notifications when some but not all biomarker results are ready for an order.

Orders may have partial results, meaning that the lab has made available part of the result, while the full result is not complete. In general, these are short-lived and as such, Junction does not expose them via webhooks.
Some orders, however, have long-lived partial results, specifically when one ordered marker takes significantly longer to result than the others.

In these situations, clients may want to be notified of the existence of partial results. As such, Junction provides a team-level configuration that enables the delivery of partial result webhooks. Similarly to other events, these are triggered via a `labtest.order.updated` event in the system.

Orders can also experience multiple partials in their lifecycle. In these cases, Junction will send a webhook for each partial update. So if your order experiences two partial results before a final, complete result, you should expect to receive two `labtest.order.updated` webhooks with `partial` status.

For example, in the second partial result, you would receive a webhook with the following body:

<Note>Note that the `events` block contains two `sample_with_lab.walk_in_test.partial_results` events.</Note>

```json Walk-in Order Updated theme={null}
{
  "id": "84d96c03-6b1c-4226-ad8f-ef44a6bc08af",
  "team_id": "6353bcab-3526-4838-8c92-063fa760fb6b",
  "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "patient_details": { "dob": "2020-01-01", "gender": "male" },
  "patient_address": {
    "receiver_name": "John Doe",
    "first_line": "123 Main St.",
    "second_line": "Apt. 208",
    "city": "San Francisco",
    "state": "CA",
    "zip": "91189",
    "country": "United States",
    "phone_number": "+1123456789"
  },
  "details": {
    "type": "walk_in_test",
    "data": {
      "id": "a655f0e4-6405-4a1d-80b7-66f06c2108a7",
      "created_at": "2020-01-01T00:00:00Z",
      "updated_at": "2020-01-01T00:00:00Z"
    }
  },
  "sample_id": "123456789",
  "notes": "This is a note",
  "created_at": "2020-01-01T00:00:00Z",
  "updated_at": "2020-01-01T00:00:00Z",
  "status": "sample_with_lab",
  "events": [
    {
      "id": 1,
      "created_at": "2022-01-01T00:00:00Z",
      "status": "received.walk_in_test.ordered"
    },
    {
      "id": 2,
      "created_at": "2022-01-02T00:00:00Z",
      "status": "received.walk_in_test.requisition_created"
    },
    {
      "id": 3,
      "created_at": "2022-01-03T00:00:00Z",
      "status": "sample_with_lab.walk_in_test.partial_results"
    },
    {
      "id": 4,
      "created_at": "2022-01-04T00:00:00Z",
      "status": "sample_with_lab.walk_in_test.partial_results"
    }
  ]
}
```


# Redraws
Source: https://docs.junction.com/lab/workflow/redraws

Handle redraw scenarios where patients return for a new blood draw due to missing results.

<Card icon="person-digging">
  Interested in this feature? Get in touch with your Customer Success Manager.
</Card>

<Warning>
  Please note that this feature is only available for walk-in tests with Labcorp, Quest, and BioReference.
</Warning>

A patient may be eligible for a redraw if their order has missing results due to a lab error.
To get a redraw, the patient will need to go back to the PSC with a new requisition form, listing only the missing biomarkers that need to be retested.

To facilitate this flow, Junction uses the concept of [order transactions](/lab/workflow/order-transactions), which in the case of a redraw flow, will include one initial order and at most, one redraw order.

## Order Transactions and Redraw Orders

If an `initial` order is eligible for a redraw, rather than transitioning the [low-level status](/lab/workflow/lab-test-lifecycle) of the order to `completed`, the order status is set to `redraw_available`.

* At this point, a new order is created, with `origin` set to `redraw`, and it is linked to the same order transaction as the `initial` order
* The order transaction's `status` field will be as follows:
  * `active` - one or more orders belonging to the order transaction are in progress
  * `completed` - all expected lab work for this transaction is completed and no further updates are expected
  * `cancelled` - the initial order has been cancelled (and there's no redraw order), or both the initial and redraw orders have been cancelled

### Webhooks

* A `labtest.order.updated` [webhook](/lab/walk-in/webhooks) will be sent for the `initial` order when it transitions to the `redraw_available` status
* Additionally, `labtest.order.created` and `labtest.order.updated` [webhooks](/lab/walk-in/webhooks) will be sent for the `redraw` order, when it is created and transitions to the `requisition_created` status upon the requisition being processed
* The `labtest.order.created` and `labtest.order.updated` webhook payloads will include origin and order transaction details (including a list of orders that belong to the transaction, ordered by ascending date of order creation) - see the example payload on the [webhooks](/lab/walk-in/webhooks) page for more details
* Ordering of webhook delivery is not guaranteed and should not be relied upon

### Monitoring updates and completion

* The results for the `initial` order will be available via the [results API](/lab/results/result-formats) when the `redraw_available` event is received. **No further updates for this order should be expected.**
* The `redraw` order will move through the usual [order lifecycle](/lab/walk-in/order-lifecycle) and will emit its own webhooks so that updates can be tracked.
* A patient can only go through one redraw per order transaction. This means that a `redraw` order will never transition to a `redraw_available` state.
* When results for the `redraw` order have been received and processed, the `redraw` order status will transition to `completed`, regardless of whether there are still missing results. The order transaction status will also be marked as `completed`.

## Key things to note

* The order transaction should be used as the primary way to reason about all related orders. A workflow for monitoring redraws could be as follows:
  * Save the order transaction ID associated with an order at the point of its creation
  * When processing order webhooks, if an initial order is in a `redraw_available` state, check the order transaction details in the payload to retrieve the order ID for the new redraw order
  * Monitor webhooks for the redraw order to get the latest updates
  * Once the redraw order is completed, use the order transaction endpoint to retrieve combined results

## Cancelling a Redraw Order

* Once the `initial` order transitions to `redraw_available` state, it can no longer be cancelled.
* To cancel the `redraw` order, use the [cancel order endpoint](/api-reference/lab-testing/cancel-order) with the `redraw` order ID (not the `initial` order ID). The redraw order follows the standard [walk-in test cancellation rules](/lab/workflow/cancelling-an-order).

## Integrating with Order Transactions

We recommend that you store both the `order_id` and `order_transaction_id` when placing an order in Junction. This way, if you receive a webhook for an order that you have not seen in your system, you will be able to link it to the initial order via the `order_transaction_id`.
From there, you can always use the `order_transaction_id` directly to obtain results, ensuring you always get the complete set of results for a particular order transaction.


# Proxy Ordering Registrable Testkits
Source: https://docs.junction.com/lab/workflow/registrable-testkit-proxy-inventory-workflows

Recommended flows for proxy ordering and inventory workflows with registrable testkits.

Junction's registrable testkit flow supports ordering testkits before the final patient registration step is completed. These are also known as "unregistered" testkits because the patient registration step happens after the kit is ordered.

The initial [`POST /v3/order/testkit`](/api-reference/lab-testing/create-unregistered-order) request still requires a `user_id`. That user can be the intended patient if they are already known, or it can be a stock/inventory user that your system uses while the final patient is still unknown.

When the kit is registered with [`POST /v3/order/testkit/register`](/api-reference/lab-testing/register-order), you can provide a different `user_id`. If that user exists on the same team as the order, Junction rebinds the order to that user during registration. If `user_id` is omitted, the order stays bound to the user from the original order request.

This workflow guide outlines recommended implementation patterns for two flows where the patient may not be the person or system initiating the order:

* A user orders a registrable testkit on behalf of another person.
* A clinic orders registrable testkits in bulk for on-hand inventory.

These are general recommendations. Your implementation may need to adjust these patterns based on your specific user model, inventory workflow, registration workflow, and result display/retrieval requirements.

<Note>
  The `user_id` on the create request is the current order owner. The `user_id` on the registration request is the final patient and result owner when it is supplied. Registration-time rebinding only works for an existing user on the same team as the order.
</Note>

## Relevant API requests

These flows use the following Junction API requests:

* Use [`POST /v2/user`](/api-reference/user/create-user) to create a Junction user when the intended patient or stock/inventory user does not already exist in Junction.
* Use [`GET /v2/user/{user_id}`](/api-reference/user/get-user) when you already have the Junction `user_id` and need to retrieve that user.
* Use [`GET /v2/user/resolve/{client_user_id}`](/api-reference/user/resolve-user) when you have your own `client_user_id` and need to look up the corresponding Junction user.
* Use [`POST /v3/order/testkit`](/api-reference/lab-testing/create-unregistered-order) to create a registrable testkit order.
* Use [`POST /v3/order/testkit/register`](/api-reference/lab-testing/register-order) to register the kit when the final patient information is available.

<Note>
  In the flows below, "create or resolve a user" means: first look up the existing Junction user using `GET /v2/user/{user_id}` if you already store the Junction `user_id`, or `GET /v2/user/resolve/{client_user_id}` if you store your own user identifier. If no Junction user exists, create one with `POST /v2/user`.
</Note>

## Scenario 1: User A orders a testkit for User B

### Example

User A places an unregistered testkit order for User B. User B is the intended patient and will register the kit using their own demographic information.

### Recommended flow

Junction does not model this as a separate proxy-ordering concept. From Junction's perspective, this is still a normal [registrable testkit order](/lab/workflow/order-registrable-testkit). Your application decides whether to bind the order to the final patient at order time or at registration time.

If User B is known before the kit is ordered, create the Junction order directly for User B:

1. User A starts the order in your application.
2. Your application determines that the intended patient and result owner is User B.
3. Create or resolve User B in Junction.
4. Place the registrable testkit order with [`POST /v3/order/testkit`](/api-reference/lab-testing/create-unregistered-order) using User B's Junction `user_id`.
5. Register the kit with [`POST /v3/order/testkit/register`](/api-reference/lab-testing/register-order). You can omit `user_id` because the order is already bound to User B.
6. Order and results are associated with User B.

### If the final patient is not known yet

If User A orders the kit before the final patient is known, use the same pattern as the inventory workflow:

1. Create or resolve a stock, household, account, or orderer Junction user for the initial order.
2. Place the registrable testkit order with that user's `user_id`.
3. When User B is known, create or resolve User B in Junction.
4. Register the kit for that Junction order with User B's Junction `user_id` and User B's patient information.
5. Junction rebinds the order to User B during registration.

## Scenario 2: Bulk registrable kits for inventory

### Example

Your clinic wants to order 100 registrable testkits and keep the physical kits in inventory or on site before the final patients are known. For example, you may need kits available at a location so they can be handed to patients later.

### Recommended flow

For inventory workflows, create one or more dedicated Junction users to represent stock inventory before patient assignment. These can be scoped however your application needs, such as one stock user per clinic location, program, warehouse, or inventory pool.

For example, if you need 100 kits for one clinic location:

1. Create or resolve a Junction stock user for that location, such as `clinic_a_inventory`.
2. Place 100 registrable testkit orders with [`POST /v3/order/testkit`](/api-reference/lab-testing/create-unregistered-order), using that stock user's Junction `user_id`.
3. Store the Junction order ID for each inventory order.
4. When a final patient is assigned to a kit or claims a kit, create or resolve that patient in Junction.
5. Register the kit with [`POST /v3/order/testkit/register`](/api-reference/lab-testing/register-order), using the Junction order ID, the patient's Junction `user_id`, and the patient's registration details.
6. Junction rebinds the order from the stock user to the patient user during registration. Results are associated with the patient user.

## Example inventory mapping

Your inventory tracking may look like this. The only Junction identifier you need to keep for registration is the order ID. Any extra fields are application-specific.

| Field                    | Description                                             |
| ------------------------ | ------------------------------------------------------- |
| Junction order ID        | The Junction order created for this kit                 |
| Patient ID               | Your patient ID, once the kit is assigned or claimed    |
| Patient Junction user ID | The Junction user supplied at registration time         |
| Status                   | Example inventory status maintained by your application |

| Junction Order ID | Patient ID   | Patient Junction User ID | Status     |
| ----------------- | ------------ | ------------------------ | ---------- |
| order\_001        | null         | null                     | available  |
| order\_002        | patient\_123 | junction\_user\_123      | registered |
| order\_003        | patient\_456 | junction\_user\_456      | registered |

## Webhook and event attribution

Before registration, order and shipping events are associated with the user from the original [`POST /v3/order/testkit`](/api-reference/lab-testing/create-unregistered-order) request. In an inventory workflow, that is usually your stock user.

After registration-time rebinding, the registration event and later order lifecycle events are associated with the patient user supplied in [`POST /v3/order/testkit/register`](/api-reference/lab-testing/register-order).

Because of this, your application should not treat pre-registration webhook `user_id` or `client_user_id` values as the final patient identity for inventory-held kits. Use your inventory mapping and the post-registration order state as the source of truth for final patient ownership.


# Scheduled Orders
Source: https://docs.junction.com/lab/workflow/scheduled-orders

Schedule lab test orders for future fulfillment dates using the activate_by parameter for follow-up testing workflows.

Junction provides a way to schedule your orders for a future date, both via the API and via the Junction Dashboard. A scheduled order is
one that will only be fulfilled in the future, so it won't be created in the Partner Laboratories until the defined date. This is useful for
defining follow-up orders after placing an initial Lab Order for a patient.

## Ordering through the API

To place a scheduled order through the API, you should use the [Create Order endpoint](/api-reference/lab-testing/create-order),
passing the `activate_by` date parameter. The `activate_by` parameter defines when that order is scheduled for,
and it will move from the `ordered` to the `requisition_created` status when the date arrives.

If you want to query all placed orders, you may use [Get Orders endpoint](/api-reference/lab-testing/get-orders), passing an `order_activation_types` query parameter, which accepts the following values `["current", "scheduled"]`.

* If the parameter is not provided, every order is returned.
* If `current` is provided, orders with a `null` `activate_by` date or with `activate_by` set before the current date are returned.
* If `scheduled` is provided, orders with an `activate_by` date greater than the current date are returned.

Placing a scheduled order for January 6, 2025 would look like this:

```json Placing a Scheduled Order request theme={null}
{
  "user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "patient_details": {
    "dob": "2022-07-06T22:20:26.796Z",
    "gender": "male | female",
    "email": "test@test.com"
  },
  "patient_address": {
    "receiver_name": "John Doe",
    "street": "Hazel Road",
    "street_number": "102",
    "city": "San Francisco",
    "state": "CA",
    "zip": "91789",
    "country": "U.S.",
    "phone_number": "+14158180852"
  },
  "lab_test_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "physician": {
    "first_name": "John",
    "last_name": "Doe",
    "email": "john@doe.com",
    "npi": "123456789",
    "licensed_states": ["CA", "NY"],
    "created_at": "2022-07-06T22:20:26.796Z",
    "updated_at": "2022-07-06T22:20:26.796Z"
  },
  "activate_by": "2025-01-06"
}
```

## Ordering through the Junction Dashboard

To place a scheduled order through the dashboard, just follow the normal procedure until you reach the Lab Testing selection screen.

<Frame>
  <img />
</Frame>

Notice that there is a checkbox for defining this order as scheduled for the future. After the checkbox is selected, you can pass in the desired scheduled date.

After that, you can filter out the `Scheduled` and `Current` orders in the dashboard using the filter in the orders listing.

<Frame>
  <img />
</Frame>


# Unmatched lab results
Source: https://docs.junction.com/lab/workflow/unmatched-results

Review lab results that could not be automatically matched to an order, including webhooks, listing, accepting, rejecting, and escalating.

# Unmatched Lab Results Integration Guide

The Unmatched Results API lets you review a lab result that could not be safely attached to an order automatically. You can accept a proposed match, reject the result, or ask the Junction operations team to review it.

This guide covers:

* receiving review notifications by webhook;
* listing and retrieving unmatched results;
* accepting, rejecting, or escalating a result; and
* understanding the available match cases.

## Recommended integration flow

1. Subscribe your webhook endpoint to `labtest.match_review.created`.
2. When the webhook arrives, store its `data.id` as the unmatched-result ID.
3. Fetch the full item with `GET /v3/unmatched_result/{id}`.
4. Present the result, patient context, and candidate orders to a reviewer.
5. Display only the actions listed in `allowed_actions`.
6. Call the accept or resolve endpoint with the reviewer's decision.
7. Use the returned resource as the source of truth for the final state and store its `order_transaction.id` to track related orders.

You can also poll the list endpoint to reconcile missed or delayed webhook deliveries.

## Test your integration in Sandbox

The unmatched-result testing API creates realistic review items in Junction Sandbox. It creates the required Sandbox orders, or uses orders that you provide, generates a central-lab result, and sends it through the normal matching and review flow. You do not need to create HL7, upload a result, or configure a lab account.

After a test run succeeds, use its `raw_result_id` with the normal unmatched-results API to test your polling, webhook, review, accept, reject, and escalation flows.

<Note>
  The testing API is available only in Sandbox and returns `404 Not Found` in production. It also requires the unmatched-result testing feature to be enabled for your Sandbox team. Contact your Junction representative to enable it.
</Note>

Test fixtures:

* support Labcorp, Quest, Sonora Quest, and BioReference panels;
* produce the same decision codes described in [Available decision cases](#available-decision-cases);
* emit the normal `labtest.match_review.created` and `labtest.match_review.updated` webhooks; and
* persist in Sandbox. There is no cleanup or cancellation endpoint.

Enabling the testing API in Sandbox does not enable automatic unmatched-result routing in production. Production uses a separate feature configuration.

### Run a test

1. Call [the case catalog](/api-reference/lab-testing/unmatched-results/list-unmatched-result-test-cases) and choose a case.
2. Choose `managed` orders for Junction-created fixtures or `provided` orders to use existing Sandbox orders.
3. [Create a test run](/api-reference/lab-testing/unmatched-results/create-unmatched-result-test) with a stable `X-Idempotency-Key`.
4. [Poll the run](/api-reference/lab-testing/unmatched-results/get-unmatched-result-test) until its status is `succeeded` or `failed`.
5. On success, retrieve the generated item with `GET /v3/unmatched_result/{raw_result_id}` and process it through the normal accept or resolve endpoints.
6. Reconcile the API state with the normal match-review webhooks.

The case catalog is the source of truth for each case's required order roles, target statuses, and lab constraints. Do not hard-code these requirements.

This request creates a completed-order match with Junction-managed orders:

```bash theme={null}
curl --fail-with-body --request POST \
  --url 'https://api.sandbox.us.junction.com/v3/unmatched_result_test' \
  --header "x-vital-api-key: $JUNCTION_API_KEY" \
  --header 'X-Idempotency-Key: match-completed-001' \
  --header 'content-type: application/json' \
  --data '{
    "case": "match_completed",
    "order_source": "managed",
    "result_status": "final"
  }'
```

The API returns `202 Accepted` with a run ID:

```json theme={null}
{
  "run_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
  "status": "queued"
}
```

Poll the run using that ID:

```bash theme={null}
curl --fail-with-body \
  --url 'https://api.sandbox.us.junction.com/v3/unmatched_result_test/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' \
  --header "x-vital-api-key: $JUNCTION_API_KEY"
```

When the run succeeds, the response includes the generated `raw_result_id` and any orders that Junction created:

```json theme={null}
{
  "run_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
  "status": "succeeded",
  "stage": "succeeded",
  "case": "match_completed",
  "order_source": "managed",
  "result_status": "final",
  "orders": {
    "provenance": {
      "order_id": "11111111-1111-1111-1111-111111111111",
      "user_id": "22222222-2222-2222-2222-222222222222",
      "order_transaction_id": "33333333-3333-3333-3333-333333333333"
    }
  },
  "raw_result_id": "44444444-4444-4444-4444-444444444444",
  "error": null,
  "created_at": "2026-07-20T10:00:00Z",
  "updated_at": "2026-07-20T10:00:08Z",
  "started_at": "2026-07-20T10:00:01Z",
  "completed_at": "2026-07-20T10:00:08Z"
}
```

Managed mode is available for every case except `wrong_collection`. That case requires a provided at-home phlebotomy order. In `provided` mode, the `orders` object must contain exactly the role names returned by the case catalog, and the orders must belong to the authenticated Sandbox team.

If Junction finds more than one eligible central-lab panel, include `lab_test_id` in the create request. For `wrong_lab`, use `wrong_lab_test_id` in managed mode to select the panel for the order at the other central lab.

Keep the idempotency key stable when retrying after a client timeout. Reusing the same key and request returns the existing run; reusing it with a different request returns `409 Conflict`.

## Webhook

### Event type

```text theme={null}
labtest.match_review.created
```

The event is sent when an unmatched result first becomes available for customer review. An item that is only awaiting internal operations review does not emit this event.

The webhook is a notification, not the complete review record. Fetch `data.id` from the API before displaying or resolving the item.

Example payload:

```json theme={null}
{
  "event_type": "labtest.match_review.created",
  "team_id": "44444444-4444-4444-4444-444444444444",
  "user_id": "11111111-1111-1111-1111-111111111111",
  "client_user_id": "customer-patient-123",
  "data": {
    "id": "6c3a56b7-c3ef-4edb-889a-6c4c874bcf00",
    "status": "pending_customer_review",
    "decision_code": "match_sample_id_mismatch_demo",
    "sub_reason_codes": []
  }
}
```

Configure this event on your existing webhook destination; it does not require a separate callback URL. Verify each delivery using the signing secret for that destination, return a successful response promptly, and process repeated deliveries idempotently.

## API endpoints

### List unmatched results

```http theme={null}
GET /v3/unmatched_result
```

Example:

```bash theme={null}
curl --request GET \
  --url 'https://api.tryvital.io/v3/unmatched_result?status=pending_customer_review&lab_slug=labcorp&limit=50' \
  --header "x-vital-api-key: $VITAL_API_KEY"
```

Query parameters:

| Parameter          | Type    | Description                                                             |
| ------------------ | ------- | ----------------------------------------------------------------------- |
| `limit`            | integer | Page size. Defaults to `50`.                                            |
| `next_cursor`      | string  | Opaque cursor returned by the previous page.                            |
| `status`           | string  | `pending_customer_review`, `pending_ops_review`, or `resolved`.         |
| `decision_code`    | string  | One of the decision codes listed below.                                 |
| `lab_slug`         | string  | Lab identifier, such as `labcorp` or `quest`.                           |
| `created_at_start` | date    | Result receipt date on or after this UTC date, formatted `YYYY-MM-DD`.  |
| `created_at_end`   | date    | Result receipt date on or before this UTC date, formatted `YYYY-MM-DD`. |

Without a `status` filter, the endpoint returns actionable customer items and items that you escalated with `unsure`. Resolved items are excluded by default.

Response:

```json theme={null}
{
  "data": [
    {
      "id": "6c3a56b7-c3ef-4edb-889a-6c4c874bcf00",
      "status": "pending_customer_review",
      "decision_code": "match_sample_id_mismatch_demo",
      "sub_reason_codes": [],
      "reason": "The sample matched, but the demographics do not line up with the existing order.",
      "patient": {
        "first_name": "Ada",
        "last_name": "Lovelace",
        "dob": "1815-12-10T00:00:00Z",
        "gender": "female"
      },
      "lab": {
        "id": 1,
        "name": "Labcorp",
        "slug": "labcorp"
      },
      "markers": [
        {
          "provider_id": "005009",
          "name": "Complete Blood Count"
        }
      ],
      "interpretation": "abnormal",
      "result_status": "partial",
      "note": null,
      "allowed_actions": ["accept", "reject", "unsure"],
      "candidate_groups": [
        {
          "user_id": "11111111-1111-1111-1111-111111111111",
          "candidates": [
            {
              "candidate_type": "provenance_order",
              "order_id": "22222222-2222-2222-2222-222222222222",
              "order_transaction_id": "33333333-3333-3333-3333-333333333333",
              "last_status": "completed",
              "confidence_level": "high",
              "confidence_score": 0.9,
              "reasons": ["sample_id_match", "demographic_mismatch"],
              "marker_overlap": null,
              "status_context": null
            }
          ]
        }
      ],
      "created_at": "2026-07-15T14:50:40Z",
      "updated_at": "2026-07-15T14:50:40Z",
      "reviewed_at": null
    }
  ],
  "next_cursor": null
}
```

Pass `next_cursor` unchanged into the next request. Do not parse or construct cursor values in your application.

### Get one unmatched result

```http theme={null}
GET /v3/unmatched_result/{raw_result_id}
```

```bash theme={null}
curl --request GET \
  --url 'https://api.tryvital.io/v3/unmatched_result/6c3a56b7-c3ef-4edb-889a-6c4c874bcf00' \
  --header "x-vital-api-key: $VITAL_API_KEY"
```

The response uses the same unmatched-result shape as an item in the list response.

Important fields:

| Field                                                  | Meaning                                                                             |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `id`                                                   | Raw result ID used by the API and webhook.                                          |
| `status`                                               | Current review state.                                                               |
| `decision_code`                                        | The primary reason the result entered this flow.                                    |
| `reason`                                               | Human-readable summary for the reviewer.                                            |
| `result_status`                                        | Whether the incoming lab result is `partial` or `final`.                            |
| `allowed_actions`                                      | Actions currently accepted by the API. Always use this field to enable UI controls. |
| `candidate_groups`                                     | Candidate orders grouped by Junction `user_id`.                                     |
| `candidate_groups[].candidates[].order_transaction_id` | Groups a candidate with its related original, replacement, or recreated orders.     |

Candidate confidence and reason codes are review aids; they do not replace human confirmation. A `provenance_order` is the order associated with the incoming sample or result history. An `order_option` is another order that may be a valid target.

### Accept a match

```http theme={null}
POST /v3/unmatched_result/{raw_result_id}/accept
```

Use `accept` only when it appears in `allowed_actions`.

Request fields:

| Field      | Required | Description                                                                              |
| ---------- | -------- | ---------------------------------------------------------------------------------------- |
| `order_id` | No       | Existing Junction order that should receive the result.                                  |
| `user_id`  | No       | Junction user who should own a replacement order when an existing order is not selected. |
| `note`     | No       | Review context for audit and support.                                                    |

If both `user_id` and `order_id` are supplied, the order must belong to that user. Send an empty object only when accepting the system's unambiguous default target. For ambiguous demographic or no-match cases, explicitly select an `order_id` or `user_id`.

Example:

```bash theme={null}
curl --request POST \
  --url 'https://api.tryvital.io/v3/unmatched_result/6c3a56b7-c3ef-4edb-889a-6c4c874bcf00/accept' \
  --header "x-vital-api-key: $VITAL_API_KEY" \
  --header 'content-type: application/json' \
  --data '{
    "order_id": "22222222-2222-2222-2222-222222222222",
    "note": "Confirmed against the requisition."
  }'
```

The response is the standard lab order object. Depending on the decision case, acceptance may use the selected order or create/recreate an appropriate order. Always persist the order ID returned by this endpoint instead of assuming it is the same as a candidate order ID.

### Track related orders with an order transaction

An order transaction groups orders that belong to the same order lifecycle. For example, an original order and an order created to replace it can have different order IDs while sharing one order transaction ID.

The candidate records expose `order_transaction_id` when one is available. The order returned by `accept` includes the authoritative `order_transaction`. An abridged response looks like:

```json theme={null}
{
  "id": "55555555-5555-5555-5555-555555555555",
  "order_transaction": {
    "id": "33333333-3333-3333-3333-333333333333",
    "status": "active",
    "orders": [
      {
        "id": "22222222-2222-2222-2222-222222222222",
        "low_level_status": "cancelled",
        "origin": "initial",
        "parent_id": null
      },
      {
        "id": "55555555-5555-5555-5555-555555555555",
        "low_level_status": "completed",
        "origin": "recreation",
        "parent_id": "22222222-2222-2222-2222-222222222222"
      }
    ]
  }
}
```

Use `order_transaction.id` as the grouping key in your system and keep each individual order ID as a member of that group. Use `parent_id` to identify the direct predecessor and `low_level_status` to determine what happened to each order. Do not overwrite the original order ID with the replacement order ID or assume that a candidate order remains the active order after acceptance.

You can retrieve all orders in the group with:

```http theme={null}
GET /v3/orders?order_transaction_id={order_transaction_id}
```

Always take the transaction from the order returned by `accept`. If acceptance creates an order for a different patient, its transaction may differ from the candidate transaction shown before review.

### Reject or escalate a result

```http theme={null}
POST /v3/unmatched_result/{raw_result_id}/resolve
```

Request body:

```json theme={null}
{
  "action": "reject",
  "note": "This result does not belong to our patient."
}
```

Available actions:

| Action   | Outcome                                                                     |
| -------- | --------------------------------------------------------------------------- |
| `reject` | Marks the review item as resolved without attaching the result to an order. |
| `unsure` | Sends the item to the Junction operations team for review.                  |

Example escalation:

```bash theme={null}
curl --request POST \
  --url 'https://api.tryvital.io/v3/unmatched_result/6c3a56b7-c3ef-4edb-889a-6c4c874bcf00/resolve' \
  --header "x-vital-api-key: $VITAL_API_KEY" \
  --header 'content-type: application/json' \
  --data '{
    "action": "unsure",
    "note": "The demographics match two patients. Please review."
  }'
```

The response is the updated unmatched result. After `unsure`, its status is `pending_ops_review` and `allowed_actions` is empty. After `reject`, its status is `resolved` and `allowed_actions` is empty.

## Review statuses

| Status                    | Meaning                                                                |
| ------------------------- | ---------------------------------------------------------------------- |
| `pending_customer_review` | The result is waiting for your decision.                               |
| `pending_ops_review`      | You selected `unsure` and Junction operations is reviewing the result. |
| `resolved`                | A reviewer rejected the result or otherwise completed the review.      |

## Available decision cases

The `decision_code` describes why automatic matching stopped. Use the API's `reason`, candidates, and `allowed_actions` together when presenting a case.

| Decision code                   | Meaning                                                                        | What to verify                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `match_completed`               | The sample matched an order that is already completed.                         | Confirm whether the new result belongs to the same patient and test.                    |
| `match_cancelled`               | The sample matched an order that is cancelled.                                 | Confirm whether the result is valid and should be released through a replacement order. |
| `wrong_collection`              | The linked order is not in a valid collection or result-ready state.           | Confirm that the collection occurred and that the candidate order is correct.           |
| `wrong_lab`                     | The patient demographics match, but the open order is for another central lab. | Confirm the receiving lab, patient, and test before accepting.                          |
| `multiple_demo_match`           | The demographics match more than one patient.                                  | Select the correct patient and order; use `unsure` if identity cannot be confirmed.     |
| `match_sample_id_mismatch_demo` | The sample identifier matches an order, but the result demographics do not.    | Resolve the demographic conflict before attaching the result.                           |
| `match_demo`                    | The demographics match a patient, but not one unambiguous order.               | Select the correct candidate order or patient.                                          |
| `no_match`                      | No patient or order could be matched confidently.                              | Select a known patient/order, reject the result, or escalate it.                        |

### Candidate reason codes

Each candidate may include one or more machine-readable reasons:

| Category      | Reason codes                                                                     |
| ------------- | -------------------------------------------------------------------------------- |
| Identity      | `sample_id_match`, `demographic_match`, `demographic_mismatch`, `same_user`      |
| Lab           | `possible_wrong_lab`, `same_lab`                                                 |
| Order state   | `matched_completed_order`, `matched_cancelled_order`, `invalid_collection_state` |
| Test contents | `marker_overlap_exact`, `marker_overlap_partial`, `marker_overlap_none`          |

## Error handling

| HTTP status | Meaning                                                                                                          |
| ----------- | ---------------------------------------------------------------------------------------------------------------- |
| `400`       | The unmatched-results review queue is not enabled for the authenticated team.                                    |
| `404`       | The item, user, or order was not found for the authenticated team, or the item is no longer customer-reviewable. |
| `422`       | The request is invalid, inconsistent with the selected user/order, or cannot be completed for the case.          |

Because another reviewer may act on an item first, re-fetch the item after a failed action before prompting the user to retry. Never attempt to work around a `404` by using an ID from another team.

## Integration checklist

* Enable the unmatched-results review queue for the correct environment and team.
* Subscribe the existing webhook destination to `labtest.match_review.created`.
* Verify webhook signatures and handle repeated deliveries safely.
* Fetch the full item from the API after receiving a webhook.
* Use `allowed_actions` to control available review actions.
* Require an explicit patient/order selection for ambiguous cases.
* Store the order and `order_transaction.id` returned by `accept`.
* Group related orders by transaction ID while retaining each individual order ID.
* Reconcile outstanding items periodically with the list endpoint.


# Data Prioritization
Source: https://docs.junction.com/sense/data-prioritization

Learn how Junction Sense automatically prioritizes data from multiple providers and source types, keeping only the highest-priority source per group.

## Overview

Data prioritization automatically kicks in whenever you use a [Group By clause](/sense/query-dsl/group-by-clause).

Within each group outlined by your [Group By clause](/sense/query-dsl/group-by-clause), the query executor would **implicitly**:

* Sub-group the data further by their Provider and their [Source Type](/wearables/providers/data-attributions);
* Keep only data from the sub-group that has the highest Source Type priority, or the highest Provider priority when multiple sub-groups exist
  for the same Source Type.

<Note>
  Data prioritization works only in a [Group By](/sense/query-dsl/group-by-clause) context.
</Note>

<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>

<Accordion title="Example">
  You are querying Sleep Score grouped by every 1 day:

  ```json theme={null}
  {
      "select": [
          { "func": "newest", "arg": { "value_macro": "sleep_score" } }
      ],
      "group_by": [
          { "date_trunc": { "value": 1, "unit": "day" }, "arg": { "index": "sleep" } }
      ]
  }
  ```

  Assuming this timeline:

  * Day 1: The user connected Oura.
  * Day 5: The user connected Apple HealthKit.
  * Both Oura and HealthKit have been consistently sending sleep data every day since connected.

  #### If your team priority states that Apple HealthKit > Oura

  The query result would be:

  * Day 1-4: Sleep Score computed from Oura sleep data
  * Day 5-7: Sleep Score computed from Apple HealthKit sleep data

  #### If your team priority states that Oura > Apple HealthKit

  The query result would be:

  * Day 1-7: Sleep Score computed from Oura sleep data

  <Info>
    Apple HealthKit data are ignored in this case, because Oura is available throughout all
    days and has higher provider priority.
  </Info>
</Accordion>

## Previewing the prioritization effect

You can experiment and preview the effect of prioritization using the [Query API](/sense/using-query-api).

By specifying a list of provider slugs in `config.provider_priority_overrides`, you instruct the query executor to treat these providers
as the highest priority — above the team provider priority — specifically in this query invocation.

## Managing Provider Priorities

You can manage the provider priorities for each summary type through the **Data Priority** section of the [Junction Dashboard](https://app.junction.com/).

<Note>A larger number indicates a higher priority.</Note>

## Source Type Priorities

We use pre-assigned, non-configurable Source Type priorities that follow the general expectation of data reliability.

*(sorted from highest priority to lowest priority)*

* `lab`
* `automatic`
* `watch`
* `ring`
* `chest_strap`
* `scale`
* `cuff`
* `fingerprick`
* `manual_scan`
* `phone`
* `app`
* `multiple_sources`
* `unknown`


# Examples
Source: https://docs.junction.com/sense/examples

Explore practical code examples of Junction Sense Continuous Query use cases including daily sleep analysis, in both Python and JSON DSL.

A couple of examples to get you started with [Continuous Query](/continuous-query-overview):

## Daily Sleep Analysis

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

  va.select(
      va.group_key("*"), 
      va.Sleep.col("efficiency").mean(),
      va.Sleep.score().mean(),
      va.Sleep.chronotype().newest()
  ).where(
      "type = 'long_sleep'"
  ).group_by(
      va.date_trunc(va.Sleep.index(), 1, "day")
  ).finalize()
  ```

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

## Weekly Insights Into Users' Activity

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

  va.select(
      va.group_key("*"), 
      va.Activity.col("heart_rate_resting").mean(),
      va.Activity.col("calories_total").max(),
      va.Activity.col("steps").min(),
      va.Activity.col("duration_active_second").mean()
  ).group_by(
      va.date_trunc(va.Activity.index(), 1, "week")
  ).finalize()
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      {
        "group_key": "*"
      },
      {
        "arg": {
          "activity": "heart_rate_resting"
        },
        "func": "mean"
      },
      {
        "arg": {
          "activity": "calories_total"
        },
        "func": "max"
      },
      {
        "arg": {
          "activity": "steps"
        },
        "func": "min"
      },
      {
        "arg": {
          "activity": "duration_active_second"
        },
        "func": "mean"
      }
    ],
    "group_by": [
      {
        "date_trunc": {
          "value": 1,
          "unit": "week"
        },
        "arg": {
          "index": "activity"
        }
      }
    ]
  }
  ```
</CodeGroup>

## First Glucose Measurement Of The Day Grouped By Source Type and Provider

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

  va.select(
      va.group_key("*"),
      va.Timeseries.col("glucose").field("value").oldest()
  ).group_by(
      va.date_trunc(va.Timeseries.index(), 1, "day"),
      va.Source.col("source_provider"),
      va.Source.col("source_type")
  ).finalize()
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
        {
          "group_key": "*"
        },
        {
          "arg": {
            "field": "value",
            "timeseries": "glucose"
          },
          "func": "oldest"
        }
      ],
      "group_by": [
        {
          "arg": {
            "index": "timeseries"
          },
          "date_trunc": {"unit": "day", "value": 1}
        },
        {"source": "source_provider"},
        {"source": "source_type"}
      ]
  }
  ```
</CodeGroup>

## Daily Summaries of Metabolic Biomarkers Grouped By Source Type and Provider

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

  va.select(
      va.group_key("*"),
      va.Timeseries.col("glucose").field("value").mean(),
      va.Timeseries.col("heartrate").field("value").mean(),
      va.Timeseries.col("steps").field("value").sum(),
      va.Timeseries.col("hrv").field("value").mean(),
      va.Timeseries.col("calories_active").field("value").sum(),
      va.Timeseries.col("body_temperature").field("value").mean(),
      va.Timeseries.col("body_temperature").field("value").min(),
      va.Timeseries.col("body_temperature").field("value").max(),
  ).group_by(
      va.date_trunc(va.Timeseries.index(), 1, "day"),
      va.Source.col("source_provider"),
      va.Source.col("source_type"),
  ).finalize()
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
        {
          "group_key": "*"
        },
        {
          "arg": {
            "timeseries": "glucose",
            "field": "value"
          },
          "func": "mean"
        },
        {
          "arg": {
            "timeseries": "heartrate",
            "field": "value"
          },
          "func": "mean"
        },
        {
          "arg": {
            "timeseries": "steps",
            "field": "value"
          },
          "func": "sum"
        },
        {
          "arg": {
            "timeseries": "hrv",
            "field": "value"
          },
          "func": "mean"
        },
        {
          "arg": {
            "timeseries": "calories_active",
            "field": "value"
          },
          "func": "sum"
        },
        {
          "arg": {
            "timeseries": "body_temperature",
            "field": "value"
          },
          "func": "mean"
        },
        {
          "arg": {
            "timeseries": "body_temperature",
            "field": "value"
          },
          "func": "min"
        },
        {
          "arg": {
            "timeseries": "body_temperature",
            "field": "value"
          },
          "func": "max"
        }
      ],
      "group_by": [
        {
          "arg": {
            "index": "timeseries"
          },
          "date_trunc": {"unit": "day", "value": 1}
        },
        {"source": "source_provider"},
        {"source": "source_type"}
      ]
  }
  ```
</CodeGroup>

## Weekly Exercise Summary

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

  va.select(
      va.group_key("*"),
      va.Workout.col("calories").max(),
      va.Workout.col("calories").min(),
      va.Workout.col("heart_rate_zone_1").mean(),
      va.Workout.col("heart_rate_zone_2").mean(),
      va.Workout.col("heart_rate_zone_3").mean(),
      va.Workout.col("heart_rate_zone_4").mean(),
      va.Workout.col("heart_rate_zone_5").mean(),
      va.Workout.col("heart_rate_zone_6").mean(),
      va.Workout.col("distance_meter").max(),
      va.Workout.col("duration_active_second").mean()
  ).group_by(
      va.date_trunc(va.Workout.index(), 1, "week")
  ).finalize()
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      {
        "group_key": "*"
      },
      {
        "arg": {
          "workout": "calories"
        },
        "func": "max"
      },
      {
        "arg": {
          "workout": "calories"
        },
        "func": "min"
      },
      {
        "arg": {
          "workout": "heart_rate_zone_1"
        },
        "func": "mean"
      },
      {
        "arg": {
          "workout": "heart_rate_zone_2"
        },
        "func": "mean"
      },
      {
        "arg": {
          "workout": "heart_rate_zone_3"
        },
        "func": "mean"
      },
      {
        "arg": {
          "workout": "heart_rate_zone_4"
        },
        "func": "mean"
      },
      {
        "arg": {
          "workout": "heart_rate_zone_5"
        },
        "func": "mean"
      },
      {
        "arg": {
          "workout": "heart_rate_zone_6"
        },
        "func": "mean"
      },
      {
        "arg": {
          "workout": "distance_meter"
        },
        "func": "max"
      },
      {
        "arg": {
          "workout": "duration_active_second"
        },
        "func": "mean"
      }
    ],
    "group_by": [
      {
        "date_trunc": {
          "value": 1,
          "unit": "week"
        },
        "arg": {
          "index": "workout"
        }
      }
    ]
  }
  ```
</CodeGroup>

## Menstrual Cycle Summary

Period end, cycle end, mean basal body temperature, and number of meaningful flow days — 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(),
      va.MenstrualCycle.col("menstrual_flow")
          .unnest_and_select(lambda col: col.count())
          .where("flow != 'none'")
          .mean(),
  ).group_by(
      va.date_trunc(va.MenstrualCycle.index(), 1, "day")
  ).finalize()
  ```

  ```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" } }
        }
      },
      {
        "func": "mean",
        "arg": {
          "select": { "func": "count", "arg": null },
          "from": { "unnest": { "menstrual_cycle": "menstrual_flow" } },
          "where": "flow != 'none'"
        }
      }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "day" },
        "arg": { "index": "menstrual_cycle" }
      }
    ]
  }
  ```
</CodeGroup>


# Managing Queries
Source: https://docs.junction.com/sense/managing-queries

Create, edit, and manage your Continuous Query configurations using the Junction Dashboard.

<iframe title="Using Junction's Continuous Query" />


# Sense overview
Source: https://docs.junction.com/sense/overview

Junction Sense consolidates wearable data from multiple providers into tabular outputs with data prioritization, floating-time aggregation, and query APIs.

Junction Sense consolidates the multitude of user data collected across [wearable data providers](/wearables/providers/introduction) and [source types](/wearables/providers/data-attributions#source-type) into simple tabular outputs.

<img alt="A high-level illustration of the inputs and outputs of the Aggregation API" />

<Card title="Consolidation" icon="merge">
  Work with simple tabular outputs, and offload the dirty work to Junction Sense — the data gathering, prioritization, and sanitization
  processes across multiple providers and source types.
</Card>

<Card title="Data prioritization" icon="bars-staggered">
  Provider priorities can be configured at team level. Query-level priority overrides are also supported to enable
  experimentation.
</Card>

<Card title="Aggregate in floating time" icon="timeline-arrow">
  Work with time zone agnostic outputs that are indexed in the user's perception of time (i.e., *floating time*).
</Card>

## Query API and Continuous Query

You can use Junction Sense through two mediums:

### Query API

* Rapid Insights: Ideal for quick analysis, allowing developers to experiment with individual user data immediately.
* Flexible Outputs: Produces easy-to-use DataFrames that work seamlessly with popular data analysis tools.
* Cost and Time Efficiency: Get aggregated insights without having to dump all user data from the API.
* Build Dynamic Dashboards: Queries are adjustable and executed in real time, suitable for dynamic dashboards that need to support flexible adjustments of queries based on user inputs.

### Continuous Query

* Automatic Evaluation: Best suited for scenarios requiring ongoing data monitoring and updates, automatically re-evaluating queries when new data is available.
* Streamlined Workflows: Minimizes manual effort and efficiently manages large datasets, making it ideal for continuous reporting and collaboration within teams.
* Pushed Changes: New and changed query results are pushed to you as webhooks via Svix or ETL Pipelines.

### Decision Guide

<Steps>
  <Step title="Experiment with Query API">
    Start with Query API if you need quick insights, one-time analysis or query experimentation using an individual user's data.
  </Step>

  <Step title="Productize with Continuous Query">
    Switch to Continuous Query once you have queries which you wish to turn into a continuously updated, named dataset and to receive change events for.
  </Step>
</Steps>

Choose based on your current needs — rapid analysis and iteration with Query API or continual data monitoring with Continuous Query. Both features use the same Query DSL, complement each other, and can adapt as your product evolves.


# Getting Started
Source: https://docs.junction.com/sense/python-sdk/getting-started

Get started with the Junction Sense Python SDK for authentication, query construction, and integration with Polars and Pandas DataFrames.

<Note>
  The [`junction-api-sdk` PyPI package](/home/libraries) (Junction API Python SDK) provides typed bindings
  of the Junction API in Python that track our OpenAPI Schema.

  Junction Sense Python SDK is a standalone product offering, separate from the `junction-api-sdk` PyPI package.
</Note>

**Junction Sense Python SDK** bridges Junction's data aggregation and intelligence capabilities with the
rich Python ecosystem for scientific computing and data analysis.

## Authentication

<AccordionGroup>
  <Accordion title="Deployed Services" icon="server">
    Use the same [Junction API Keys](/api-details/junction-api#authentication) your system uses to interact with the Junction API.

    To set the Junction API Key for the Junction Sense Python SDK, you can:

    * Pass the Junction API Key explicitly to the Junction Sense SDK entity's constructor (e.g., the Query Executor); or
    * Set the Junction API Key as the `VITAL_API_KEY` environment variable.
  </Accordion>

  <Accordion title="Data Scientists and Developers" icon="user">
    Sign-in locally on your machine with your [Junction Dashboard](https://app.junction.com/) account. You can
    do so through the [Junction Sense SDK Command Line Tool](#sdk-command-line-tool) as detailed below.
  </Accordion>
</AccordionGroup>

## Installation

### Aggregation API

<Steps>
  <Step title="Add a dependency">
    Add the `vitalx-aggregation` package to your project.

    ```bash theme={null}
    poetry add vitalx-aggregation
    ```
  </Step>
</Steps>

### SDK Command Line Tool

<Steps>
  <Step title="Add a dependency">
    Add the `vitalx-cli` package as a dev dependency to your project.

    ```bash theme={null}
    poetry add -G dev vitalx-cli
    ```
  </Step>

  <Step title="Start the device authentication process">
    Create the `~/.vitalx` directory, and grant it execute permission. The SDK requires execute
    permission to maintain a file lock.

    ```bash theme={null}
    mkdir -p ~/.vitalx && chmod +x ~/.vitalx
    ```

    Call the `auth login` CLI subcommand on the `vitalx-cli` package to start the authentication
    process.

    ```bash theme={null}
    poetry run vitalx-cli auth login
    ```
  </Step>

  <Step title="Sign in with your Junction Dashboard account">
    You will be prompted in your default web browser to sign in to the [Junction Dashboard](https://app.junction.com/).
    You will then be asked to confirm the device authorization.

    <Frame>
      <img alt="Device Confirmation prompt" />
    </Frame>
  </Step>

  <Step title="Voilà!">
    You have now signed in. You can now use any Junction Sense SDK entity without a Junction API Key.

    <Note>
      Your permanent session is stored at `~/.vitalx/`.
    </Note>
  </Step>
</Steps>

## Example

[tryvital/vitalx-notebook](https://github.com/tryVital/vitalx-notebook) is a pre-configured Jupyter Notebook project
with dependencies on Aggregation API and SDK Command Line Tool.

The notebook also includes an example to compute *Chronotypes* using the Aggregation API.


# ALIGN clause
Source: https://docs.junction.com/sense/query-dsl/align-clause

Use the ALIGN clause in Junction Sense queries to fill empty time buckets with carry-forward, carry-backward, or carry-nearest operators on sparse data.

## Overview

Use the ALIGN clause to fill empty time buckets in your aggregated output with values carried over from neighboring buckets. Without it, a GROUP BY over sparse data - for example a once-a-week weigh-in grouped daily - produces only the rows that have real observations, with no rows for the days in between. With ALIGN you get one row per bucket in the time range, with empty buckets filled according to your chosen carry operator.

All ALIGN behavior is **post-aggregation**: Junction Sense first runs your GROUP BY and aggregates, then materializes the missing buckets and fills them.

The ALIGN clause supports three carry operators:

* `"carry_forward"` - fill empty buckets with the most recent prior non-empty value (last-observation-carried-forward).
* `"carry_backward"` - fill empty buckets with the next subsequent non-empty value.
* `"carry_nearest"` - fill empty buckets with the value from the nearest non-empty bucket (past or future, whichever is closer; ties prefer the past value).

Each operator takes a duration argument (`max_age` for forward/backward, `span` for nearest) that caps how far the carry will reach. Buckets beyond the cap remain as explicit nulls in the output.

## Operators

### `"carry_forward"` - last-observation-carried-forward

For each empty bucket, carries the most recent prior non-empty bucket's aggregate forward in time. `max_age` caps how far back the carry searches; if no prior non-empty bucket exists within `max_age`, the bucket stays as an explicit null.

Use `carry_forward` for causal or streaming use cases where only past data should influence the current value - for example a daily weight series built from sparse weigh-ins, or HRV tracking that should hold the last known reading across days the device wasn't worn.

<AccordionGroup>
  <Accordion title="Input arguments" icon="arrow-right-to-bracket">
    | Argument  | Remarks                                                                                                                         |
    | --------- | ------------------------------------------------------------------------------------------------------------------------------- |
    | `max_age` | Optional [Period](/sense/query-dsl/group-by-clause#group-by-a-truncated-datetime) - caps look-back. Omit to carry indefinitely. |
  </Accordion>
</AccordionGroup>

### `"carry_backward"` - carry-backward

Symmetric counterpart to `carry_forward`. For each empty bucket, carries the nearest subsequent non-empty bucket's aggregate backward in time. `max_age` caps how far forward the carry searches.

Useful for retrospective analysis - for example applying a measurement taken on day N back to days N-1, N-2, etc., up to the cap.

<AccordionGroup>
  <Accordion title="Input arguments" icon="arrow-right-to-bracket">
    | Argument  | Remarks                                                                                                                            |
    | --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
    | `max_age` | Optional [Period](/sense/query-dsl/group-by-clause#group-by-a-truncated-datetime) - caps look-forward. Omit to carry indefinitely. |
  </Accordion>
</AccordionGroup>

### `"carry_nearest"` - carry from nearest bucket

For each empty bucket, borrows the value from the nearest non-empty bucket in either direction - past or future, whichever is closer. `span` caps the search in both directions. On a tie (equidistant past and future non-empty buckets), the past value is preferred.

Use `carry_nearest` when direction doesn't matter and minimizing carry distance is the priority - for example filling a daily weight composite from the closest measurement within a few days, in either direction.

<AccordionGroup>
  <Accordion title="Input arguments" icon="arrow-right-to-bracket">
    | Argument | Remarks                                                                                                                                         |
    | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
    | `span`   | Optional [Period](/sense/query-dsl/group-by-clause#group-by-a-truncated-datetime) - caps search in both directions. Omit to carry indefinitely. |
  </Accordion>
</AccordionGroup>

### Operator comparison

| Operator           | Direction            | Tie behavior         |
| ------------------ | -------------------- | -------------------- |
| `"carry_forward"`  | Backward only        | - (single direction) |
| `"carry_backward"` | Forward only         | - (single direction) |
| `"carry_nearest"`  | Both - picks closest | Prefer past on tie   |

## Behavior

### Bucket materialization

Every ALIGN operator materializes empty datetime buckets in the output. Even if the carry cap is reached and a bucket can't be filled, the row still appears in the result with a null value. Downstream consumers see a contiguous time series rather than a sparse one.

### Spine extension beyond observed data

The spine extends beyond the observed data range by the carry window so sparse data - like one weigh-in per week - produces a properly-carried series:

* `carry_forward(max_age=X)` extends the spine forward by `X` past the latest observation.
* `carry_backward(max_age=X)` extends the spine backward by `X` before the earliest observation.
* `carry_nearest(span=X)` extends on both sides by `X`.

Forward extension is clamped at the current time - the spine never includes buckets dated past "now". If a recent observation plus `X` would land in the future, the spine ends at the bucket containing the current moment. Backward extension is not capped.

When `max_age` / `span` is omitted (uncapped carry), no extension is applied - the spine falls back to the observed range. For dense-data queries that's fine; for the very-sparse case specify a cap to opt in to boundary materialization.

### Column-level carry

Each output column is carried independently. A bucket that has a null aggregate for column A but a real value for column B will have A filled from the nearest eligible source while B retains its real value. This is the intended behavior - it avoids introducing nulls when a value is available within the carry window, regardless of other columns.

### Per-partition carry

When `group_by` contains keys beyond the [Date Truncate expression](/sense/query-dsl/group-by-clause#group-by-a-truncated-datetime) (for example `Source.col("source_provider")`), each unique combination of those keys is treated as an independent time series. Values do not carry across partition boundaries - Oura's last reading will never fill an Apple Health bucket, even if the Apple Health partition has an empty bucket within `max_age`.

Every group\_by column must be projected in `select` (via `group_key("*")` or each `group_key(i)`); otherwise the query is rejected.

## Validation

When you create a Continuous Query or submit a synchronous Query with ALIGN, the following must hold:

1. `group_by` must contain at least one [Date Truncate expression](/sense/query-dsl/group-by-clause#group-by-a-truncated-datetime). `carry_forward`, `carry_backward`, and `carry_nearest` all operate over the truncated datetime axis.
2. `max_age` / `span` must be greater than or equal to the `group_by` Date Truncate period. A carry window smaller than the bucket size could never reach an adjacent bucket and is rejected rather than silently no-op'd.
3. Every `group_by` column must be projected in `select` - use `group_key("*")` for the common case.
4. `group_by` cannot mix a Date Truncate expression with a [Date Part expression](/sense/query-dsl/group-by-clause#group-by-a-date-or-time-component) (such as day-of-month). Date Part values are derived from the same date column as the bucket and treating them as independent partition keys would produce impossible combinations like (February, day-of-month=30).

## Honest null is the default

Omitting `.align()` preserves the existing behavior - sparse GROUP BY output, no extra rows for missing buckets. Reach for ALIGN only when you actively want carry behavior.

## Examples

### Carry weight forward up to 14 days

Fill in the days between weigh-ins so a daily trend chart has a usable value every day, capped at 14 days of carry to avoid stale data.

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

  va.select(
      va.group_key("*"),
      va.Body.col("weight_kilogram").mean(),
  ).group_by(
      va.date_trunc(va.Body.index(), 1, "day"),
  ).align("carry_forward", max_age=va.period(14, "day"))
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "group_key": "*" },
      { "func": "mean", "arg": { "body": "weight_kilogram" } }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "day" },
        "arg": { "index": "body" }
      }
    ],
    "align": {
      "carry": {
        "mode": "carry_forward",
        "max_age": { "value": 14, "unit": "day" }
      }
    }
  }
  ```
</CodeGroup>

### Carry HRV forward on days the wearable was not worn

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

  va.select(
      va.group_key("*"),
      va.Sleep.col("hrv_mean_rmssd").mean(),
  ).group_by(
      va.date_trunc(va.Sleep.index(), 1, "day"),
  ).align("carry_forward", max_age=va.period(7, "day"))
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "group_key": "*" },
      { "func": "mean", "arg": { "sleep": "hrv_mean_rmssd" } }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "day" },
        "arg": { "index": "sleep" }
      }
    ],
    "align": {
      "carry": {
        "mode": "carry_forward",
        "max_age": { "value": 7, "unit": "day" }
      }
    }
  }
  ```
</CodeGroup>

### Fill weight from the nearest measurement within 3 days

Use `carry_nearest` when direction doesn't matter and minimizing carry distance is the priority.

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

  va.select(
      va.group_key("*"),
      va.Body.col("weight_kilogram").mean(),
  ).group_by(
      va.date_trunc(va.Body.index(), 1, "day"),
  ).align("carry_nearest", span=va.period(3, "day"))
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "group_key": "*" },
      { "func": "mean", "arg": { "body": "weight_kilogram" } }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "day" },
        "arg": { "index": "body" }
      }
    ],
    "align": {
      "carry": {
        "mode": "carry_nearest",
        "span": { "value": 3, "unit": "day" }
      }
    }
  }
  ```
</CodeGroup>

### Per-provider weight trend with independent carry

Group by `source_provider` so each provider gets its own series. The carry stays within each partition - Oura's value never bleeds into Apple Health's series.

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

  va.select(
      va.group_key("*"),
      va.Body.col("weight_kilogram").mean(),
  ).group_by(
      va.date_trunc(va.Body.index(), 1, "day"),
      va.Source.col("source_provider"),
  ).align("carry_forward", max_age=va.period(14, "day"))
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "group_key": "*" },
      { "func": "mean", "arg": { "body": "weight_kilogram" } }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "day" },
        "arg": { "index": "body" }
      },
      { "source": "source_provider" }
    ],
    "align": {
      "carry": {
        "mode": "carry_forward",
        "max_age": { "value": 14, "unit": "day" }
      }
    }
  }
  ```
</CodeGroup>

### Blood pressure: infrequently measured, carry up to 3 months

For lab-grade measurements that are sampled infrequently but stay relevant for weeks or months between readings.

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

  va.select(
      va.group_key("*"),
      va.Timeseries.col("blood_pressure").field("systolic").mean(),
  ).group_by(
      va.date_trunc(va.Timeseries.index(), 1, "day"),
  ).align("carry_forward", max_age=va.period(3, "month"))
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "group_key": "*" },
      {
        "func": "mean",
        "arg": { "timeseries": "blood_pressure", "field": "systolic" }
      }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "day" },
        "arg": { "index": "timeseries" }
      }
    ],
    "align": {
      "carry": {
        "mode": "carry_forward",
        "max_age": { "value": 3, "unit": "month" }
      }
    }
  }
  ```
</CodeGroup>


# Column expressions
Source: https://docs.junction.com/sense/query-dsl/column-expressions

Reference for table column expressions in Junction Sense queries to select fields from activity, body, sleep, and timeseries resources.

## Table Column expression

Your Query Instruction can pull data from [Junction summary and timeseries resources](/wearables/providers/resources)
through Table Column expressions.

Each Table Column expression can select one column (a field) of the table (the Junction resource).
You can use multiple Table Column expressions to select multiple columns in the same query.

<Info>
  Cross-table query instructions are not supported — you cannot mix Table Column expressions from
  different tables in the same query instruction.
</Info>

<Note>
  Column expressions for Electrocardiogram, Sleep Cycle, and lab testing biomarkers are planned to be introduced in the future.
</Note>

### Summaries

| Table                                     | Python DSL                   | JSON DSL                        |
| ----------------------------------------- | ---------------------------- | ------------------------------- |
| Activity daily summary<br />`activity`    | `Activity.col("$1")`         | `{ "activity": "$1" }`          |
| Body summary<br />`body`                  | `Body.col("$1")`             | `{ "body": "$1" }`              |
| Sleep summary<br />`sleep`                | `Sleep.col("$1")`            | `{ "sleep": "$1" }`             |
| Readiness scores<br />`derived_readiness` | `DerivedReadiness.col("$1")` | `{ "derived_readiness": "$1" }` |
| Workout summary<br />`workout`            | `Workout.col("$1")`          | `{ "workout": "$1" }`           |
| Meal summary<br />`meal`                  | `Meal.col("$1")`             | `{ "meal": "$1" }`              |
| Menstrual cycle<br />`menstrual_cycle`    | `MenstrualCycle.col("$1")`   | `{ "menstrual_cycle": "$1" }`   |
| Profile<br />`profile`                    | `Profile.col("$1")`          | `{ "profile": "$1" }`           |

### Column Mappings

Map summary resource properties to their Continuous Query column equivalents.

#### Sleep

| Sleep field                | Query Column               |
| -------------------------- | -------------------------- |
| `calendar_date`            | `index`                    |
| `bedtime_start`            | `session_start`            |
| `bedtime_stop`             | `session_end`              |
| `type`                     | `type`                     |
| `timezone_offset`          | `time_zone`                |
| `duration`                 | `duration_second`          |
| `total`                    | `stage_asleep_second`      |
| `awake`                    | `stage_awake_second`       |
| `light`                    | `stage_light_second`       |
| `rem`                      | `stage_rem_second`         |
| `deep`                     | `stage_deep_second`        |
| -                          | `stage_unknown_second`     |
| `score`                    | `score`                    |
| `recovery_readiness_score` | `recovery_readiness_score` |
| `hr_lowest`                | `heart_rate_minimum`       |
| `hr_average`               | `heart_rate_mean`          |
| -                          | `heart_rate_maximum`       |
| `hr_resting`               | `heart_rate_resting`       |
| `efficiency`               | `efficiency`               |
| `latency`                  | `latency_second`           |
| `temperature_delta`        | `skin_temperature_delta`   |
| `skin_temperature`         | `skin_temperature`         |
| `hr_dip`                   | `heart_rate_dip`           |
| `average_hrv`              | `hrv_mean_rmssd`           |
| -                          | `hrv_mean_sdnn`            |
| `respiratory_rate`         | `respiratory_rate`         |
| `state`                    | `state`                    |
| `source.slug`              | `source_provider`          |
| `source.type`              | `source_type`              |
| `source.device_id`         | `source_device_id`         |
| `source.app_id`            | `source_app_id`            |

#### Activity

| Activity field               | Query Column                 |
| ---------------------------- | ---------------------------- |
| `calendar_date`              | `index`                      |
| `calories_total`             | `calories_total`             |
| `calories_active`            | `calories_active`            |
| `steps`                      | `steps`                      |
| `daily_movement`             | `duration_active_second`     |
| `distance`                   | `distance_meter`             |
| `floors_climbed`             | `floors_climbed`             |
| `time_zone`                  | `time_zone`                  |
| `timezone_offset`            | `time_zone_offset`           |
| `heart_rate.avg_bpm`         | `heart_rate_mean`            |
| `heart_rate.min_bpm`         | `heart_rate_minimum`         |
| `heart_rate.max_bpm`         | `heart_rate_maximum`         |
| `heart_rate.resting_bpm`     | `heart_rate_resting`         |
| `heart_rate.avg_walking_bpm` | `heart_rate_mean_walking`    |
| `wheelchair_use`             | `wheelchair_use`             |
| `wheelchair_push`            | `wheelchair_push`            |
| -                            | `intensity_sedentary_second` |
| `low`                        | `intensity_low_second`       |
| `medium`                     | `intensity_medium_second`    |
| `high`                       | `intensity_high_second`      |
| `source.slug`                | `source_provider`            |
| `source.type`                | `source_type`                |
| `source.device_id`           | `source_device_id`           |
| `source.app_id`              | `source_app_id`              |

#### Workout

| Workout field            | Query Column              |
| ------------------------ | ------------------------- |
| `title`                  | `title`                   |
| `timezone_offset`        | `time_zone`               |
| `average_hr`             | `heart_rate_mean`         |
| `max_hr`                 | `heart_rate_maximum`      |
| -                        | `heart_rate_minimum`      |
| `distance`               | `distance_meter`          |
| `calendar_date`          | -                         |
| `time_start`             | `session_start`           |
| `time_end`               | `session_end`             |
| `calories`               | `calories`                |
| `sport.name`             | `sport_name`              |
| `sport.slug`             | `sport_slug`              |
| `hr_zones`               | `heart_rate_zone_1..6`    |
| `moving_time`            | `duration_active_second`  |
| `total_elevation_gain`   | `elevation_gain_meter`    |
| `elev_high`              | `elevation_maximum_meter` |
| `elev_low`               | `elevation_minimum_meter` |
| `average_speed`          | `speed_mean`              |
| `max_speed`              | `speed_maximum`           |
| `average_watts`          | `power_mean`              |
| `device_watts`           | `power_source`            |
| `max_watts`              | `power_maximum`           |
| `weighted_average_watts` | `power_weighted_mean`     |
| `steps`                  | `steps`                   |
| `map.polyline`           | `map_polyline`            |
| `map.summary_polyline`   | `map_summary_polyline`    |
| `provider_id`            | `external_id`             |
| `source.slug`            | `source_provider`         |
| `source.type`            | `source_type`             |
| `source.device_id`       | `source_device_id`        |
| `source.app_id`          | `source_app_id`           |

#### Body

| Body field                       | Query Column                     |
| -------------------------------- | -------------------------------- |
| `calendar_date`                  | -                                |
| `date` (deprecated)              | -                                |
| -                                | `measured_at`                    |
| `weight`                         | `weight_kilogram`                |
| `fat`                            | `fat_mass_percentage`            |
| `water_percentage`               | `water_percentage`               |
| `muscle_mass_percentage`         | `muscle_mass_percentage`         |
| `visceral_fat_index`             | `visceral_fat_index`             |
| `bone_mass_percentage`           | `bone_mass_percentage`           |
| `body_mass_index`                | `body_mass_index`                |
| `lean_body_mass_kilogram`        | `lean_body_mass_kilogram`        |
| `waist_circumference_centimeter` | `waist_circumference_centimeter` |
| -                                | `time_zone`                      |
| `source.slug`                    | `source_provider`                |
| `source.type`                    | `source_type`                    |
| `source.device_id`               | `source_device_id`               |
| `source.app_id`                  | `source_app_id`                  |

#### Meal

| Meal field                         | Query Column                 |
| ---------------------------------- | ---------------------------- |
| `timestamp`                        | `index`                      |
| `name`                             | `name`                       |
| `energy.value`                     | `calories`                   |
| `macros.carbs`                     | `carbohydrate_gram`          |
| `macros.protein`                   | `protein_gram`               |
| `macros.alcohol`                   | `alcohol_gram`               |
| `macros.water`                     | `water_gram`                 |
| `macros.fibre`                     | `fibre_gram`                 |
| `macros.sugar`                     | `sugar_gram`                 |
| `macros.cholesterol`               | `cholesterol_gram`           |
| `macros.fats.saturated`            | `saturated_fat_gram`         |
| `macros.fats.monounsaturated`      | `monounsaturated_fat_gram`   |
| `macros.fats.polyunsaturated`      | `polyunsaturated_fat_gram`   |
| `macros.fats.omega3`               | `omega3_fat_gram`            |
| `macros.fats.omega6`               | `omega6_fat_gram`            |
| `macros.fats.total`                | `total_fat_gram`             |
| `micros.minerals.sodium`           | `sodium_milligram`           |
| `micros.minerals.potassium`        | `potassium_milligram`        |
| `micros.minerals.calcium`          | `calcium_milligram`          |
| `micros.minerals.phosphorus`       | `phosphorus_milligram`       |
| `micros.minerals.magnesium`        | `magnesium_milligram`        |
| `micros.minerals.iron`             | `iron_milligram`             |
| `micros.minerals.zinc`             | `zinc_milligram`             |
| `micros.minerals.fluoride`         | `fluoride_milligram`         |
| `micros.minerals.chloride`         | `chloride_milligram`         |
| `micros.vitamins.vitamin_a`        | `vitamin_a_milligram`        |
| `micros.vitamins.vitamin_b1`       | `vitamin_b1_milligram`       |
| `micros.vitamins.riboflavin`       | `riboflavin_milligram`       |
| `micros.vitamins.niacin`           | `niacin_milligram`           |
| `micros.vitamins.pantothenic_acid` | `pantothenic_acid_milligram` |
| `micros.vitamins.vitamin_b6`       | `vitamin_b6_milligram`       |
| `micros.vitamins.biotin`           | `biotin_microgram`           |
| `micros.vitamins.vitamin_b12`      | `vitamin_b12_microgram`      |
| `micros.vitamins.vitamin_c`        | `vitamin_c_milligram`        |
| `micros.vitamins.vitamin_d`        | `vitamin_d_microgram`        |
| `micros.vitamins.vitamin_e`        | `vitamin_e_milligram`        |
| `micros.vitamins.vitamin_k`        | `vitamin_k_microgram`        |
| `micros.vitamins.folic_acid`       | `folic_acid_microgram`       |
| `micros.trace_elements.chromium`   | `chromium_microgram`         |
| `micros.trace_elements.copper`     | `copper_milligram`           |
| `micros.trace_elements.iodine`     | `iodine_microgram`           |
| `micros.trace_elements.manganese`  | `manganese_milligram`        |
| `micros.trace_elements.molybdenum` | `molybdenum_microgram`       |
| `micros.trace_elements.selenium`   | `selenium_microgram`         |
| `source.slug`                      | `source_provider`            |
| `source.type`                      | `source_type`                |
| `source.app_id`                    | `source_app_id`              |
| `source.device_id`                 | `source_device_id`           |

#### MenstrualCycle

| MenstrualCycle field      | Query Column              | Element type                             |
| ------------------------- | ------------------------- | ---------------------------------------- |
| `period_start`            | `period_start`            | scalar (Date)                            |
| `period_end`              | `period_end`              | scalar (Date)                            |
| `cycle_end`               | `cycle_end`               | scalar (Date)                            |
| `is_predicted`            | `is_predicted`            | scalar (Boolean)                         |
| `menstrual_flow`          | `menstrual_flow`          | list of struct `{date, flow}`            |
| `cervical_mucus`          | `cervical_mucus`          | list of struct `{date, quality}`         |
| `intermenstrual_bleeding` | `intermenstrual_bleeding` | list of struct `{date}`                  |
| `contraceptive`           | `contraceptive`           | list of struct `{date, type}`            |
| `detected_deviations`     | `detected_deviations`     | list of struct `{date, deviation}`       |
| `ovulation_test`          | `ovulation_test`          | list of struct `{date, test_result}`     |
| `home_pregnancy_test`     | `home_pregnancy_test`     | list of struct `{date, test_result}`     |
| `home_progesterone_test`  | `home_progesterone_test`  | list of struct `{date, test_result}`     |
| `sexual_activity`         | `sexual_activity`         | list of struct `{date, protection_used}` |
| `basal_body_temperature`  | `basal_body_temperature`  | list of struct `{date, value}`           |
| `source.slug`             | `source_provider`         | scalar                                   |
| `source.type`             | `source_type`             | scalar                                   |
| `source.device_id`        | `source_device_id`        | scalar                                   |
| `source.app_id`           | `source_app_id`           | scalar                                   |

<Note>
  List columns cannot be selected directly as a table column — aggregate them with a scalar-output subquery. See [List-column aggregation](/sense/query-dsl/list-column-aggregation) for the full syntax.
</Note>

#### Profile

| Profile field      | Query Column        |
| ------------------ | ------------------- |
| `sex`              | `sex`               |
| `gender`           | `gender`            |
| `height`           | `height_centimeter` |
| `wheelchair_use`   | `wheelchair_use`    |
| `birth_date`       | `birth_date`        |
| `created_at`       | `created_at`        |
| `updated_at`       | `updated_at`        |
| `source.slug`      | `source_provider`   |
| `source.type`      | `source_type`       |
| `source.app_id`    | `source_app_id`     |
| `source.device_id` | `source_device_id`  |

### Timeseries

| Table                      | Python DSL                         | JSON DSL                                |
| -------------------------- | ---------------------------------- | --------------------------------------- |
| Timeseries Resource fields | `Timeseries.col("$1").field("$2")` | `{ "timeseries": "$1", "field": "$2" }` |

## Source Column expression

You can use a Source Column expression to refer to Data Source fields common to Junction resources.

Note that some Source Column expressions may not be valid in the implicit query context. For example, `source_sport` is not a valid Source Column
expression in a query that is selecting from the Sleep resource.

<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>

| Column        | Python DSL                        | JSON DSL                            | Available in                   |
| ------------- | --------------------------------- | ----------------------------------- | ------------------------------ |
| Provider      | `Source.col("source_provider")`   | `{ "source": "source_provider" }`   | All Tables                     |
| Source Type   | `Source.col("source_type")`       | `{ "source": "source_type" }`       | All Tables                     |
| Source App ID | `Source.col("source_app_id")`     | `{ "source": "source_app_id" }`     | All summary tables             |
| Workout ID    | `Source.col("source_workout_id")` | `{ "source": "source_workout_id" }` | Workout Stream timeseries only |
| Sport         | `Source.col("source_sport")`      | `{ "source": "source_sport" }`      | Workout Stream timeseries only |

## Index Column expression

Each Junction resource has a primary datetime index. Your Query Instruction can reference this datetime
index using the special Index Column expression:

| Table                                     | Index                     | Python DSL                 | JSON DSL                           |
| ----------------------------------------- | ------------------------- | -------------------------- | ---------------------------------- |
| Activity daily summary<br />`activity`    | Calendar date             | `Activity.index()`         | `{ "index": "activity" }`          |
| Body summary<br />`body`                  | Measurement datetime      | `Body.index()`             | `{ "index": "body" }`              |
| Sleep summary<br />`sleep`                | Session end datetime      | `Sleep.index()`            | `{ "index": "sleep" }`             |
| Readiness scores<br />`derived_readiness` | Calendar date             | `DerivedReadiness.index()` | `{ "index": "derived_readiness" }` |
| Workout summary<br />`workout`            | Session start datetime    | `Workout.index()`          | `{ "index": "workout" }`           |
| Menstrual cycle<br />`menstrual_cycle`    | Period start date         | `MenstrualCycle.index()`   | `{ "index": "menstrual_cycle" }`   |
| Profile<br />`profile`                    | Updated At (UTC) datetime | `Profile.index()`          | `{ "index": "profile" }`           |

## Group Key Column expression

When your Query Instruction uses [the `group_by` clause](/sense/query-dsl/group-by-clause), each expression
in the `group_by` clause creates a temporary Group Key Column (`group_key.$OFFSET`). These temporary columns are then used to
facilitate the data grouping and aggregation as specified by your Query Instruction.

You can refer to these columns using the special Group Key Column expression:

| Group Key Columns | Python DSL       | JSON DSL               |
| ----------------- | ---------------- | ---------------------- |
| All               | `group_key("*")` | `{ "group_key": "*" }` |
| The `$N`-th key   | `group_key($N)`  | `{ "group_key": $N }`  |


# GROUP BY clause
Source: https://docs.junction.com/sense/query-dsl/group-by-clause

Use the GROUP BY clause in Junction Sense queries for date truncation, date part extraction, table column, and source column grouping.

## Overview

You specify how Junction Sense should dissect the selected data through a Group By clause. The [Aggregate expressions](/sense/query-dsl/select-clause#aggregate-a-table-column)
you specify would then be applied to the resulting groups.

Your Group By clause can contain one or more of the following expressions:

* a [Date Truncate expression](#group-by-a-truncated-datetime)

  * A synchronous [Query](/sense/using-query-api) can have at most one [Date Truncate expression](#group-by-a-truncated-datetime).
  * A [Continuous Query](/sense/using-continuous-query) must have exactly one [Date Truncate expression](#group-by-a-truncated-datetime).

* a [Date Part expression](#group-by-a-date-or-time-component)

* a [Table Column expression](#group-by-a-table-column-expression)

* a [Source Column expression](#group-by-a-source-column-expression)

You can mix and match any expression types, and organize them in any order. [Group Key Column expression](/sense/query-dsl/select-clause#select-the-group-key-columns)
tracks your declaration order.

## Group by a truncated datetime

Group the input rows based on the specified column expression truncated to the specified granularity.
This is similar to the `DATE_TRUNC` function in SQL.

<AccordionGroup>
  <Accordion title="Input argument" icon="arrow-right-to-bracket">
    | Argument    | Remarks                                                                                                                            |
    | ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
    | date\_trunc | The date or time period to truncate the input datetime to.                                                                         |
    | arg         | The input expression — only an [Index Column expression](/sense/query-dsl/column-expressions#index-column-expression) is accepted. |
  </Accordion>

  <Accordion title="Output column name" icon="line-columns">
    `group_key.$OFFSET` if this is the N-th expression in the `group_by` clause.

    See also: [Select the group key columns](/sense/query-dsl/select-clause#select-the-group-key-columns)
  </Accordion>
</AccordionGroup>

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

  # Truncate to every 1 day.
  va.select(...).group_by(
      va.date_trunc(va.Sleep.index(), 1, "day"),
  )

  # Truncate to every 3 months.
  va.select(...).group_by(
      va.date_trunc(va.Sleep.index(), 3, "month"),
  )
  ```

  ```jsonc JSON DSL theme={null}
  # Truncate to every 1 day.
  {
      "group_by": [
          {
              "date_trunc": { "value": 1, "unit": "day" },
              "arg": { "index": "sleep" }
          }
      ]
  }

  # Truncate to every 3 months.
  {
      "group_by": [
          {
              "date_trunc": { "value": 3, "unit": "month" },
              "arg": { "index": "sleep" }
          }
      ]
  }
  ```
</CodeGroup>

## Group by a date or time component

Group the input rows based on a specific date or time component extracted from the specified column expression.
This is similar to the `DATE_PART` function in SQL.

<AccordionGroup>
  <Accordion title="Input argument" icon="arrow-right-to-bracket">
    | Argument   | Remarks                                                                                                                            |
    | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- |
    | date\_part | The date or time unit to extract.                                                                                                  |
    | arg        | The input expression — only an [Index Column expression](/sense/query-dsl/column-expressions#index-column-expression) is accepted. |
  </Accordion>

  <Accordion title="Output column name" icon="line-columns">
    `group_key.$OFFSET` if this is the N-th expression in the `group_by` clause.

    See also: [Select the group key columns](/sense/query-dsl/select-clause#select-the-group-key-columns)
  </Accordion>
</AccordionGroup>

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

  # Extract the weekday
  va.select(...).group_by(
      va.date_part(va.Sleep.index(), "weekday"),
  )

  # Extract the day-of-month
  va.select(...).group_by(
      va.date_part(va.Sleep.index(), "day")
  )
  ```

  ```jsonc JSON DSL theme={null}
  # Extract the weekday
  {
      "group_by": [
          {
              "date_part": "weekday",
              "arg": { "index": "sleep" }
          }
      ]
  }

  # Extract the day-of-month
  {
      "group_by": [
          {
              "date_part": "day",
              "arg": { "index": "sleep" }
          }
      ]
  }
  ```
</CodeGroup>

## Group by a Table Column expression

Group the input rows based on a [Table Column expression](/sense/query-dsl/column-expressions#table-column-expression).
This is similar to the generic `GROUP BY` clause in SQL.

<AccordionGroup>
  <Accordion title="Input argument" icon="arrow-right-to-bracket">
    Any [Table Column expressions](/sense/query-dsl/column-expressions#table-column-expression) — summary or timeseries.
  </Accordion>

  <Accordion title="Output column name" icon="line-columns">
    `group_key.$OFFSET` if this is the N-th expression in the `group_by` clause.

    See also: [Select the group key columns](/sense/query-dsl/select-clause#select-the-group-key-columns)
  </Accordion>
</AccordionGroup>

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

  # Group by calendar date and Stand Hour type (idle vs stand)

  va.select(
      va.group_key("*"),
      va.Timeseries.col("stand_hour").field("value").sum()
  ).group_by(
      va.date_trunc(va.Timeseries.index(), 1, "day"),
      va.Timeseries.col("stand_hour").field("type")
  )
  ```

  ```jsonc JSON DSL theme={null}

  # Group by calendar date and Stand Hour type (idle vs stand)
  {
      "group_by": [
          {
              "date_trunc": {
                  "value": 1,
                  "unit": "day"
              },
              "arg": { "index": "timeseries" }
          },
          {
              "timeseries": "stand_hour",
              "field": "type"
          }
      ]
  }
  ```
</CodeGroup>

## Group by a Source Column expression

Group the input rows based on a [Source Column expression](/sense/query-dsl/column-expressions#source-column-expression).
This is similar to a generic `GROUP BY` clause in SQL.

<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>

<AccordionGroup>
  <Accordion title="Input argument" icon="arrow-right-to-bracket">
    Any [Source Column expressions](/sense/query-dsl/column-expressions#source-column-expression) valid in the query context.
  </Accordion>

  <Accordion title="Output column name" icon="line-columns">
    `group_key.$OFFSET` if this is the N-th expression in the `group_by` clause.

    See also: [Select the group key columns](/sense/query-dsl/select-clause#select-the-group-key-columns)
  </Accordion>
</AccordionGroup>

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

  # Group by the source 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")
  )

  ```

  ```jsonc JSON DSL theme={null}
  # Group by the source provider and source type
  {
      "group_by": [
          {
              "date_trunc": {
                  "value": 1,
                  "unit": "week"
              },
              "arg": { "index": "sleep" }
          },
          { "source": "source_provider" },
          { "source": "source_type" }
      ]
  }
  ```
</CodeGroup>


# List-column aggregation
Source: https://docs.junction.com/sense/query-dsl/list-column-aggregation

Aggregate list columns in Junction Sense queries with a scalar-output subquery.

## Overview

Some Junction resources expose **list columns** — columns whose value on each row is itself a list. For example, the Menstrual Cycle resource stores `basal_body_temperature` as a list of `{date, value}` structs.

List columns cannot be selected directly as a Table Column. Instead, you aggregate their elements with a **scalar-output subquery** — a self-contained expression that unnests the list, optionally filters its elements, applies an aggregate function, and returns **one scalar per outer row**.

That scalar can be selected directly, or wrapped in a group-level aggregate (`.mean()`, `.sum()`, …) when used inside a GROUP BY.

## Anatomy

A scalar-output subquery has three parts:

```
select: aggregate function + optional element reference
from:   UNNEST(<list column>)
where:  optional predicate on element fields (or `element` for scalar lists)
```

The shape mirrors a SQL subquery: `SELECT agg(element) FROM UNNEST(list) WHERE <predicate>`.

## Aggregate a struct-field

Compute the mean basal body temperature per cycle, alongside the cycle's period end and cycle end dates. Scalar columns can be selected directly next to subquery aggregates — they share the per-cycle output row.

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

  va.select(
      va.MenstrualCycle.index(),
      va.MenstrualCycle.col("period_end"),
      va.MenstrualCycle.col("cycle_end"),
      va.MenstrualCycle.col("basal_body_temperature")
          .unnest_and_select(lambda col: col.field("value").mean())
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "index": "menstrual_cycle" },
          { "menstrual_cycle": "period_end" },
          { "menstrual_cycle": "cycle_end" },
          {
              "select": {
                  "func": "mean",
                  "arg": { "field_for": "menstrual_cycle", "basal_body_temperature": "value" }
              },
              "from": { "unnest": { "menstrual_cycle": "basal_body_temperature" } }
          }
      ]
  }
  ```
</CodeGroup>

## Aggregate a scalar-element list

When the list elements are scalars rather than structs (for example, a list of string tags or numeric samples), use `count` with `arg: null`, or target the element explicitly with `{ "element": true }` for any other aggregate.

Count the tags on each note row:

```jsonc JSON DSL theme={null}
{
    "select": [
        {
            "select": { "func": "count", "arg": null },
            "from": { "unnest": { "timeseries": "note", "field": "tags" } }
        }
    ]
}
```

Sum the values of a numeric scalar-element list:

```jsonc JSON DSL theme={null}
{
    "select": [
        {
            "select": { "func": "sum", "arg": { "element": true } },
            "from": { "unnest": { "timeseries": "some_numeric_series", "field": "values" } }
        }
    ]
}
```

## Choosing `arg`

| Goal                                     | `arg`                                                                                                  |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Count elements regardless of content     | `null` (only valid with `func: "count"`)                                                               |
| Aggregate a struct field of each element | a struct-field reference, e.g. `{ "field_for": "menstrual_cycle", "basal_body_temperature": "value" }` |
| Aggregate scalar elements directly       | `{ "element": true }`                                                                                  |

Struct-field references work only on struct-element lists; `{ "element": true }` works only on scalar-element lists.

## Filter elements with WHERE

The `where` predicate filters elements **before** the aggregate runs. The identifiers inside the predicate resolve against the unnested element:

* **Struct-element lists**: identifiers refer to struct fields of each element (e.g., `"flow != 'none'"` on `menstrual_flow` references the `flow` field of each struct). Unknown field names are rejected.
* **Scalar-element lists**: the reserved identifier `element` refers to the element value itself (e.g., `"element != 'unspecified'"`). Any identifier other than `element` is rejected.

Count only meaningful flow days per cycle:

<CodeGroup>
  ```python Python DSL theme={null}
  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>

The predicate grammar is the same as the top-level [WHERE clause](/sense/query-dsl/where-clause): comparison (`>`, `>=`, `<`, `<=`, `=`, `!=`), logical (`AND`, `OR`, `NOT`), parentheses, and string/numeric literals.

## Use inside GROUP BY

A scalar-output subquery in a grouped query must be wrapped in an outer aggregate.

Period end, cycle end, and number of meaningful flow days, one row per cycle:

<CodeGroup>
  ```python Python DSL theme={null}
  va.select(
      va.group_key("*"),
      va.MenstrualCycle.col("period_end").newest(),
      va.MenstrualCycle.col("cycle_end").newest(),
      va.MenstrualCycle.col("menstrual_flow")
          .unnest_and_select(lambda col: col.count())
          .where("flow != 'none'")
          .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": "count", "arg": null },
                  "from": { "unnest": { "menstrual_cycle": "menstrual_flow" } },
                  "where": "flow != 'none'"
              }
          }
      ],
      "group_by": [
          {
              "date_trunc": { "value": 1, "unit": "day" },
              "arg": { "index": "menstrual_cycle" }
          }
      ]
  }
  ```
</CodeGroup>

## Supported aggregates

Inside the subquery's `select`:

* `count` — count of elements (or matching elements if `where` is present).
* `sum`, `mean`, `min`, `max`, `median`, `stddev` — numeric aggregates over the element value or extracted struct field.

`oldest` and `newest` are **not** available inside a subquery — list elements have no inherent ordering.

## Output column naming

The subquery's inner aggregate function is added as a prefix, matching the standard aggregate-prefix convention.

* `arg: null` → `$FUNC.$COLUMN` (e.g., `count.menstrual_flow`).
* Struct field extract → `$FUNC.$COLUMN.$FIELD` (e.g., `mean.basal_body_temperature.value`).
* `{ "element": true }` → `$FUNC.$COLUMN.element` (e.g., `sum.tags.element`).

When the subquery is wrapped in an outer aggregate, the outer func prefix composes on top following the standard [SELECT clause](/sense/query-dsl/select-clause#aggregate-a-table-column) rules — e.g. `mean.mean.basal_body_temperature.value` reads as "mean of per-cycle mean of `basal_body_temperature.value`".


# Readiness Scores
Source: https://docs.junction.com/sense/query-dsl/readiness-scores

Query daily recovery, stress, and strain scores from connected wearable providers using Junction Sense readiness score expressions.

## Overview

Readiness Scores give you a daily signal for how prepared a user is to take on load, based on their recent sleep and overnight physiology. This helps you track recovery trends, identify rising stress or strain, and understand whether sleep patterns are supporting day-to-day performance.

The `derived_readiness` table returns one row per user and calendar day with:

* `sleep_score`
* `recovery_score` and `recovery_zone`
* `stress_score`
* `strain_score` and `strain_zone`
* `chronotype`

Junction Sense computes these metrics for you, so you can query readiness directly without building custom scoring pipelines.

## What you can do

* Pull one scored row per day for charting or downstream analytics.
* Filter for specific cohorts (for example, low recovery or high strain days).
* Group and aggregate scores like any other table.

<Note>
  `DerivedReadiness.index()` is a synonym of `DerivedReadiness.col("date")`.
</Note>

## How calculations work

Readiness Scores are computed from each user's daily **long sleep** session using:

* sleep duration
* resting heart rate during sleep
* HRV during sleep
* Junction sleep score

### Baseline and warm-up

* Recovery and stress are personalized against a rolling baseline from prior nights.
* Baseline window is 21 days, with a minimum of 7 prior observations.
* Before enough history exists, `recovery_score`, `stress_score`, and `strain_score` can be `null`.

### Recovery score (0-100)

Recovery combines three signals:

* HRV relative to personal baseline (higher is better)
* resting heart rate relative to baseline (lower is better)
* sleep duration relative to an 8-hour target

The combined signal is mapped to a `0-100` scale with a logistic curve.\
Higher `recovery_score` means better readiness.

### Stress score (0-100)

Stress is the inverse of recovery:

* `stress_score = 100 - recovery_score`

Higher `stress_score` means greater physiological stress burden.

### Strain score (0-21)

Strain combines:

* **recovery load** (how far recovery is below a neutral threshold), and
* **sleep load** (sleep debt relative to 8 hours).

These are combined and exponentially scaled to a `0-21` score.\
Higher `strain_score` means greater expected load/strain.

### Missing-data behavior

* Missing HRV or resting heart rate: `recovery_score` and `stress_score` are `null`.
* Missing sleep duration: `strain_score` is `null`.
* `sleep_score` is returned when available.

<Note>
  This section describes the current Readiness model. If you need deeper methodology details for your use case, contact the Junction team.
</Note>

## Available columns and meanings

| Column           | Meaning                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------ |
| `date`           | Daily index (`YYYY-MM-DD`).                                                                |
| `chronotype`     | Daily chronotype category from sleep timing patterns.                                      |
| `sleep_score`    | Sleep quality score (`0-100`), where higher is better sleep quality.                       |
| `recovery_score` | Readiness score (`0-100`), where higher means better recovery readiness.                   |
| `recovery_zone`  | Recovery band: `Red` (`0-33`, low), `Amber` (`34-66`, moderate), `Green` (`67-100`, high). |
| `stress_score`   | Stress score (`0-100`), where higher means higher physiological stress load.               |
| `strain_score`   | Daily strain (`0-21`), where higher means greater combined sleep/recovery load.            |
| `strain_zone`    | Strain band: `Low` (`0-6`), `Moderate` (`7-13`), `High` (`14-17`), `Very High` (`18-21`).  |

## Example: Select daily scores

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

  va.select(
      va.DerivedReadiness.col("date"),
      va.DerivedReadiness.col("sleep_score"),
      va.DerivedReadiness.col("recovery_score"),
      va.DerivedReadiness.col("stress_score"),
      va.DerivedReadiness.col("strain_score"),
      va.DerivedReadiness.col("recovery_zone"),
      va.DerivedReadiness.col("strain_zone"),
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "derived_readiness": "date" },
      { "derived_readiness": "sleep_score" },
      { "derived_readiness": "recovery_score" },
      { "derived_readiness": "stress_score" },
      { "derived_readiness": "strain_score" },
      { "derived_readiness": "recovery_zone" },
      { "derived_readiness": "strain_zone" }
    ]
  }
  ```
</CodeGroup>

## Example: Weekly aggregates

<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("stress_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": "mean", "arg": { "derived_readiness": "stress_score" } },
      { "func": "max", "arg": { "derived_readiness": "strain_score" } }
    ],
    "group_by": [
      {
        "date_trunc": { "value": 1, "unit": "week" },
        "arg": { "index": "derived_readiness" }
      }
    ]
  }
  ```
</CodeGroup>

## Example: Filter for high-strain days

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

  va.select(
      va.DerivedReadiness.col("date"),
      va.DerivedReadiness.col("strain_score"),
      va.DerivedReadiness.col("strain_zone"),
      va.DerivedReadiness.col("recovery_score"),
  ).where(
      "strain_score >= 14"
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
    "select": [
      { "derived_readiness": "date" },
      { "derived_readiness": "strain_score" },
      { "derived_readiness": "strain_zone" },
      { "derived_readiness": "recovery_score" }
    ],
    "where": "strain_score >= 14"
  }
  ```
</CodeGroup>


# SELECT clause
Source: https://docs.junction.com/sense/query-dsl/select-clause

Use the SELECT clause in Junction Sense queries to choose table columns, apply aggregate functions, and retrieve group key columns.

## Select a table column

Select a specific table column using a [Table Column expression](/sense/query-dsl/column-expressions).

<Accordion title="Output column name" icon="line-columns">
  The name of the column being selected.
</Accordion>

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

  va.select(va.Sleep.col("efficiency"))
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "sleep": "efficiency" }
      ]
  }
  ```
</CodeGroup>

## Perform an analysis and select the output value

Perform data analysis with in-built algorithms using value macro expressions:

* [Sleep Analysis](/sense/query-dsl/sleep-analysis) value macro expressions

<Accordion title="Output column name" icon="line-columns">
  The name of the value macro, e.g., `chronotype` for the Chronotype value macro.
</Accordion>

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

  va.select(va.Sleep.chronotype())
  ```

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

## Aggregate a table column

Aggregate a [Table Column expression](/sense/query-dsl/column-expressions) with respect to the `group_by` clause.

The specified aggregate function is applied to each and every group created by the `group_by` clause.
The result set is N aggregated values where N is the number of groups.

<AccordionGroup>
  <Accordion title="Statistical functions" icon="chart-column">
    | Aggregate function | Python DSL       | JSON DSL                             |
    | ------------------ | ---------------- | ------------------------------------ |
    | Minimum            | `$EXPR.min()`    | `{ "func": "min", "arg": $EXPR }`    |
    | Maximum            | `$EXPR.max()`    | `{ "func": "max", "arg": $EXPR }`    |
    | Mean               | `$EXPR.mean()`   | `{ "func": "mean", "arg": $EXPR }`   |
    | Median             | `$EXPR.median()` | `{ "func": "median", "arg": $EXPR }` |
    | Standard Deviation | `$EXPR.stddev()` | `{ "func": "stddev", "arg": $EXPR }` |
    | Count              | `$EXPR.count()`  | `{ "func": "count", "arg": $EXPR }`  |
    | Oldest Value       | `$EXPR.oldest()` | `{ "func": "oldest", "arg": $EXPR }` |
    | Newest Value       | `$EXPR.newest()` | `{ "func": "newest", "arg": $EXPR }` |
  </Accordion>

  <Accordion title="Math functions" icon="sigma">
    | Aggregate function | Python DSL    | JSON DSL                          |
    | ------------------ | ------------- | --------------------------------- |
    | Sum                | `$EXPR.sum()` | `{ "func": "sum", "arg": $EXPR }` |
  </Accordion>

  <Accordion title="Output column name" icon="line-columns">
    * the aggregation function name, e.g., `sum`; or
    * if the Split by Source mode is enabled — `$FUNCTION_NAME.$SOURCE_COLUMN_NAME`, e.g., `mean.efficiency`.
  </Accordion>
</AccordionGroup>

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

  va.select(
      va.Sleep.col("stage_asleep_second").newest()
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          {
              "func": "newest",
              "arg": { "sleep": "stage_asleep_second" }
          }
      ]
  }
  ```
</CodeGroup>

## Aggregate list-column elements

Resources like Menstrual Cycle expose list-of-struct columns (for example, `basal_body_temperature` is a list of `{date, value}` entries). Aggregate their elements with a scalar-output subquery — a self-contained `select` / `from` (UNNEST) / optional `where` block that produces one scalar per outer row.

<Accordion title="Output column name" icon="line-columns">
  * `$FUNC.$COLUMN` when the subquery aggregates rows directly (e.g., `count.menstrual_flow`); or
  * `$FUNC.$COLUMN.$FIELD` when a struct field is extracted (e.g., `mean.basal_body_temperature.value`); or
  * `$FUNC.$COLUMN.element` when the scalar-list element itself is aggregated (e.g., `sum.tags.element`).
</Accordion>

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

  va.select(
      va.MenstrualCycle.col("menstrual_flow")
          .unnest_and_select(lambda col: col.count())
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          {
              "select": { "func": "count", "arg": null },
              "from": { "unnest": { "menstrual_cycle": "menstrual_flow" } }
          }
      ]
  }
  ```
</CodeGroup>

See [List-column aggregation](/sense/query-dsl/list-column-aggregation) for the full reference, including the `where` predicate, struct-field extraction, and the reserved `element` identifier for scalar-element lists.

## Select the Index Column

Select the primary datetime index of the table using the [Index Column expression](/sense/query-dsl/column-expressions#index-column-expression).

<Accordion title="Output column name" icon="line-columns">
  `timestamp` (constant).
</Accordion>

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

  va.select(va.Sleep.index())
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "index": "sleep" }
      ]
  }
  ```
</CodeGroup>

## Select the Group Key Columns

Select the [Group Key Columns](/sense/query-dsl/column-expressions#group-key-column-expression) associated
with the `group_by` clause.

You can select one specific group key column by offset, or select all group key columns with a `*` wildcard.

<Accordion title="Output column name" icon="line-columns">
  `group_key.$OFFSET`, where `$OFFSET` corresponds to the N-th expression of the `group_by` clause.
</Accordion>

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

  # Select all group key columns
  va.select(va.group_key("*"))

  # Select the 2nd group key column
  va.select(va.group_key(2))
  ```

  ```jsonc JSON DSL theme={null}
  # Select all group key columns
  {
      "select": [
          { "group_key": "*" }
      ]
  }

  # Select the 2nd group key column
  {
      "select": [
          { "group_key": 2 }
      ]
  }
  ```
</CodeGroup>


# Sleep Macros
Source: https://docs.junction.com/sense/query-dsl/sleep-analysis

Use value macro expressions for sleep analysis in Junction Sense queries to access computed sleep stage metrics and breakdowns.

Macros are precomputed fields available in the Junction Sense DSL. Use them anywhere you would use a regular column expression.
Reference a macro with the `value_macro` expression:

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

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

  va.select(va.Sleep.chronotype())
  ```
</CodeGroup>

### Available Sleep Macros

| <span>Macro Name</span> | Description                                                                                             | <span>Output Column Name</span> |
| ----------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------- |
| **Chronotype**          | The chronotype of the sleep session, categorized as `lark`, `third`, or `owl`.                          | `chronotype`                    |
| **Sleep Score**         | A score from 0 to 100 computed using the Junction Sense Sleep Score model.                              | `sleep_score`                   |
| **Asleep At**           | Computed time of user falling asleep based on sleep session start time and latency.                     | `asleep_at`                     |
| **Awake At**            | Computed time the user woke up (may differ from session end time).                                      | `awake_at`                      |
| **Awakenings**          | Count of transitions from a sleeping phase to an awake phase, computed from sleep cycle hypnogram data. | `awakenings`                    |

You can combine multiple sleep macros in a single query and mix them with standard aggregations or other select expressions.

### Example query

```json theme={null}
{
    "select": [
        { "group_key": "*" },
        { "func": "mean", "arg": { "value_macro": "sleep_score" } },
        { "func": "mean", "arg": { "sleep": "efficiency" } },
        { "func": "newest", "arg": { "value_macro": "chronotype" } },
        { "func": "newest", "arg": { "value_macro": "asleep_at" } },
        { "func": "newest", "arg": { "value_macro": "awake_at" } },
        { "func": "mean", "arg": { "value_macro": "awakenings" } }
    ],
    "group_by": [
        {
            "date_trunc": { "value": 1, "unit": "week" },
            "arg": { "index": "sleep" }
        }
    ]
}
```

## Chronotype

Determine the chronotype of each sleep session.

Chronotype categories are based on the sleep midpoint time:

* **lark** if midpoint ≥ 17:00 or ≤ 03:30
* **third** if midpoint ≤ 06:00
* **owl** otherwise

### Get last chronotype of the week

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

  va.select(
      va.Sleep.chronotype().latest()
  ).group_by(
      va.date_trunc(va.Sleep.index(), 1, "week")
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "func": "latest", "arg": { "value_macro": "chronotype" } }
      ],
      "group_by": [
          {
              "date_trunc": {
                  "unit": "week",
                  "value": 1
              },
              "arg": {
                  "index": "sleep"
              }
          }
      ]
  }
  ```
</CodeGroup>

## Sleep Score

Calculate a Sleep Score (0 to 100) for each sleep session.

### Get mean sleep score of the week

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

  va.select(
      va.Sleep.score().mean()
  ).group_by(
      va.date_trunc(va.Sleep.index(), 1, "week")
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "func": "mean", "arg": { "value_macro": "sleep_score" } }
      ],
      "group_by": [
          {
              "date_trunc": {
                  "unit": "week",
                  "value": 1
              },
              "arg": {
                  "index": "sleep"
              }
          }
      ]
  }
  ```
</CodeGroup>

## Awake At / Asleep At

Pinpoint when a user first fell asleep and when they became fully awake during each sleep session.

The `asleep_at` macro returns the bedtime start adjusted by the recorded sleep latency, while `awake_at` returns the bedtime start plus the offset of the final non-awake sleep segment.

### Get mean asleep at and awake at of the week

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

  va.select(
      va.Sleep.asleep_at().mean(),
      va.Sleep.awake_at().mean()
  ).group_by(
      va.date_trunc(va.Sleep.index(), 1, "week")
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "func": "mean", "arg": { "value_macro": "asleep_at" } },
          { "func": "mean", "arg": { "value_macro": "awake_at" } }
      ],
      "group_by": [
          {
              "date_trunc": {
                  "unit": "week",
                  "value": 1
              },
              "arg": {
                  "index": "sleep"
              }
          }
      ]
  }
  ```
</CodeGroup>

## Awakenings

Count the number of times a user transitioned from a sleeping phase (deep, light, or REM) to an awake phase during a sleep session. This is computed from sleep cycle hypnogram data.

### Get average nightly awakenings per week

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

  va.select(
      va.Sleep.awakenings().mean()
  ).group_by(
      va.date_trunc(va.Sleep.index(), 1, "week")
  )
  ```

  ```jsonc JSON DSL theme={null}
  {
      "select": [
          { "func": "mean", "arg": { "value_macro": "awakenings" } }
      ],
      "group_by": [
          {
              "date_trunc": {
                  "unit": "week",
                  "value": 1
              },
              "arg": {
                  "index": "sleep"
              }
          }
      ]
  }
  ```
</CodeGroup>


# WHERE clause
Source: https://docs.junction.com/sense/query-dsl/where-clause

Use the WHERE clause in Junction Sense queries to filter summary or timeseries rows with SQL-style comparison and logical operators.

## Overview

Use the WHERE clause to filter summary or timeseries rows before Junction Sense evaluates aggregations or grouping. When a GROUP BY clause is present, the filter runs first so only matching rows contribute to each group.

The WHERE clause evaluates SQL-style predicates against the same [column expressions](/sense/query-dsl/column-expressions) that are available in the `select` and `group_by` clauses.

## Supported operators

WHERE predicates support these operators:

* Comparison: `>`, `>=`, `<`, `<=`, `=`, `!=`
* Logical: `AND`, `OR`, `NOT`
* Parentheses for explicit precedence
* String literals enclosed in single quotes (for example, `'long_sleep'`)

Comparison operands can reference table columns (for example, `stage_rem_second`) or Source columns (for example, `source_type`).

<Note>
  Column identifiers follow the Continuous Query column names. See [Column Mappings](/sense/query-dsl/column-expressions#column-mappings) for a full list.
</Note>

<Note>
  Inside a subquery over a scalar-element list (for example, `List[Utf8]` note `tags`), the reserved identifier `element` refers to the list element itself — e.g. `"element != 'unspecified'"`. Struct-element list predicates reference the struct field names directly. See [List-column aggregation](/sense/query-dsl/list-column-aggregation#filter-elements-with-where) for the full context.
</Note>

## Filter rows before grouping

<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>

## Combine multiple device sources

Filter for specific provider and source type combinations with parentheses to manage precedence.

```python theme={null}
va.select(
    va.Sleep.score().mean(),
).where(
  "(source_type = 'watch' AND source_provider = 'apple_health_kit') OR (source_type = 'ring' AND source_provider = 'oura')"
)
```

## Filter without grouping

You can apply a WHERE clause to ungrouped queries to trim the dataset before computing aggregates.

```python theme={null}
va.select(
    va.Sleep.score().mean()
).where("source_type = 'watch' AND source_provider = 'apple_health_kit'")
```

In the JSON DSL, supply the same predicate string via the top-level `where` property.

```json theme={null}
{
  "select": [
    { "func": "mean", "arg": { "value_macro": "sleep_score" } }
  ],
  "where": "source_type = 'watch' AND source_provider = 'apple_health_kit'"
}
```

## Evaluation order

1. Apply the WHERE predicate to the incoming rows.
2. Evaluate the GROUP BY clause (if provided) on the filtered rows.
3. Calculate the aggregates in the SELECT clause.

This order ensures filters never exclude rows after grouping has started, keeping results consistent with SQL semantics.


# Using Continuous Query
Source: https://docs.junction.com/sense/using-continuous-query

Create Continuous Queries in Junction Sense that automatically evaluate as new data arrives and push result changes via webhooks or ETL pipelines.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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

You can create a **Continuous Query** to have your Junction Sense [Query](/sense/overview) automatically evaluated by Junction as needed, and receive any query result changes through [your Webhook or ETL Pipeline destinations](/webhooks/introduction).

<AccordionGroup>
  <Accordion title="Automatic Evaluation" icon="wand-sparkles">
    Continuous Query runs automatically on all existing and new Users in your Team.
  </Accordion>

  <Accordion title="Continuous Change Monitoring" icon="camera-cctv">
    Continuous Query is scheduled intelligently in response to any new data points or updates discovered through device data connections
    made through [Junction Link](/wearables/connecting-providers/introduction) and [Junction Mobile SDKs](/wearables/sdks/health/overview).
  </Accordion>

  <Accordion title="Result Pushing" icon="webhook">
    Junction pushes any Continuous Query result table changes to your Webhook or ETL Pipeline destinations.
  </Accordion>

  <Accordion title="Result Pulling" icon="table">
    The latest result table can be queried through [Junction API](/api-reference/sense/continuous-query/get-result-table) at any time.
  </Accordion>
</AccordionGroup>

<img alt="A high-level illustration of the continuous query structure" />

With Continuous Query, you can focus on adding value to your product with the aggregated data insights:

* No need to build your own scalable data pipeline from scratch to ingest, store, and aggregate standardized device data.
* No need to poll the Query API to receive query results.

## Continuous Query requirements

While Continuous Query uses the same [Query DSL](/sense/query-dsl/) as the Query API, some restrictions are in place:

* The query must [group by a truncated datetime](/sense/query-dsl/group-by-clause#group-by-a-truncated-datetime).
* The query must [select all group key columns](/sense/query-dsl/select-clause#select-the-group-key-columns).

<Note>
  Please reach out if your use case is limited by these restrictions.
</Note>

## Subscribing to query result changes

When a Continuous Query is re-evaluated and the result set has changed, Junction sends a [Result Table Changed](/event-catalog/continuous_query.result_table.changed)
data event to your [Webhook or ETL Pipeline destinations](/webhooks/introduction).

This data event includes only the delta (new rows or changed rows with respect to the [group keys](/sense/query-dsl/select-clause#select-the-group-key-columns)).
To re-fetch the whole result set, use the [Get Result Table](/api-reference/sense/continuous-query/get-result-table) endpoint.

```json theme={null}
{
  "event_type": "continuous_query.result_table.changed",
  "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
  "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
  "team_id": "6b74423d-0504-4470-9afb-477252ccf67a",
  "data": [
    {
      "data": {
        "group_key.0": [
          "2025-02-17T00:00:00"
        ],
        "mean.efficiency": [
          98.22
        ],
        "mean.sleep_score": [
          76.75
        ],
        "min.sleep_score": [
          61
        ],
        "newest.chronotype": [
          "third"
        ]
      },
      "query_id": "b59425f8-eb5a-4c60-ae95-eda70179da04",
      "query_slug": "sleep-analysis-v1"
    }
  ]
}
```

## Managing your Continuous Queries

You can manage your Continuous Query using our [Org Management API](/api-reference/org-management):

* [Create Continuous Query Endpoint](/api-reference/sense/continuous-query/create)
* [List Continuous Queries Endpoint](/api-reference/sense/continuous-query/list)

You can also manage them through the *Continuous Query* section of the [Junction Dashboard](/sense/managing-queries):

<img alt="Screenshot of the query creator" />


# Using Query API
Source: https://docs.junction.com/sense/using-query-api

Access the Junction Sense Query API through the Python SDK with DataFrame integration, the JSON API endpoint, or typed API bindings.

<Card icon="person-digging">
  Junction Sense is in **closed beta**.

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

You can make use of the Query API through these mediums:

| Channel                                                             | Remarks                                                                                                                         |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| [Junction Sense Python SDK](/sense/python-sdk/getting-started)      | The `vitalx-aggregation` package provides an expressive Python Query DSL with [Polars DataFrame](https://pola.rs) integrations. |
| [Junction Sense Query API endpoint](/api-reference/sense/query-one) | JSON API calls.                                                                                                                 |
| [Typed bindings of Junction API](/home/libraries)                   | Call the Aggregation API in Go, Java, Node, and Python.                                                                         |


# Provider Types
Source: https://docs.junction.com/wearables/connecting-providers/auth_types

Browse all supported wearable providers grouped by authentication type: OAuth, email, and email plus password connections.

Providers have different authentication flows. Some require a username and password, others require an email address and password. Junction supports the following provider types:

* `OAuth`
* `Email`
* `Email + Password`

The majority of providers are `OAuth` providers. These providers require a user to be redirected to a third-party website to authenticate. Once authenticated, the user is redirected back to the link widget.
The `Email` and `Email + Password` providers require a user to enter their email address and password into the link widget. These are then sent to Junction and used to connect to the provider.

The list of providers and their auth types is as follows:

### OAuth

Current OAuth providers are:

| Provider     | Description                                 |
| ------------ | ------------------------------------------- |
| `Fitbit`     | Activity Trackers (all devices)             |
| `Garmin`     | Fitness watches (all devices)               |
| `Oura`       | Smart Sleep tracking ring                   |
| `Strava`     | Running & Cycling Social Network            |
| `Wahoo`      | Cycling Equipment                           |
| `Withings`   | Fitness scales, watches and health monitors |
| `Google Fit` | Activity Trackers (all devices)             |
| `Polar`      | Finnish sports tech pioneer                 |
| `Cronometer` | Nutrition data                              |
| `Omron`      | Blood Pressure data                         |
| `WHOOP`      | Smart Activity Watches                      |
| `Dexcom`     | Glucose monitors                            |

To connect an OAuth provider:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const tokenResponse = await client.link.token({ userId: "<user_id>" });

  const auth = await client.link.generateOauthLink({
      oauthProvider: "oura",
      vitalLinkToken: tokenResponse.linkToken,
  });
  ```

  ```python Python theme={null}
  from junction import Junction, OAuthProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  token_response = client.link.token(user_id="<user_id>")

  oauth = client.link.generate_oauth_link(
      OAuthProviders.OURA,
      vital_link_token=token_response.link_token,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.GenerateOauthLinkLinkRequest;
  import com.junction.api.resources.link.requests.LinkTokenExchange;
  import com.junction.api.types.OAuthProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var tokenResponse = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .build()
  );

  var oauth = client.link().generateOauthLink(
      OAuthProviders.OURA,
      GenerateOauthLinkLinkRequest.builder()
          .vitalLinkToken(tokenResponse.getLinkToken())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  tokenResponse, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }

  oauth, err := c.Link.GenerateOauthLink(context.TODO(), &junction.GenerateOauthLinkLinkRequest{
      OauthProvider:  junction.OAuthProvidersOura,
      VitalLinkToken: &tokenResponse.LinkToken,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", oauth)
  ```
</CodeGroup>

For `OAuth` Providers we return an `oauth_url` that can be used to redirect users to. In a web view, on redirection, you should check the user has connected to the provider successfully. In the case of mobile, the user should receive a message to return to the app.

The possible error codes that are returned are as follows:

* `401 INVALID_REQUEST` Link Token is Invalid
* `400 MISSING_LINK_TOKEN` Missing link token
* `400 INVALID_PROVIDER` Provider is not supported
* `400 INVALID_USER_ID` User id is incorrect
* `400 INVALID_CREDENTIALS` Credentials for provider are incorrect

### Email

Current email providers are:

| Provider    | Description                |
| ----------- | -------------------------- |
| `Freestyle` | Abbott CGM Glucose monitor |

### Email + Password

Current email and password providers are:

| Provider            | Description                        |
| ------------------- | ---------------------------------- |
| `Zwift`             | Virtual cycling and running        |
| `Peloton`           | Popular Indoor Exercise bike       |
| `Eight Sleep`       | Smart Mattress                     |
| `Beurer`            | Blood pressure and glucose devices |
| `Hammerhead`        | Cycling Computer                   |
| `Dexcom G6 & Older` | CGM Glucose Monitor                |
| `MyFitnessPal`      | Meal Tracking Application          |

To connect an email and password provider:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.connectPasswordProvider({
      provider: "whoop",
      username: "<username>",
      password: "<password>",
  });
  ```

  ```python Python theme={null}
  from junction import Junction, PasswordProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.connect_password_provider(
      PasswordProviders.WHOOP,
      username="<username>",
      password="<password>",
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.IndividualProviderData;
  import com.junction.api.types.PasswordProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().connectPasswordProvider(
      PasswordProviders.WHOOP,
      IndividualProviderData.builder()
          .username("<username>")
          .password("<password>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.ConnectPasswordProvider(context.TODO(), &junction.IndividualProviderData{
      Provider: junction.PasswordProvidersWhoop,
      Username: "<username>",
      Password: "<password>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>


# Hosting OAuth Callbacks on Your Own Domain
Source: https://docs.junction.com/wearables/connecting-providers/bring-your-own-oauth/callback-endpoints

Host OAuth callback endpoints on your own domain to satisfy provider redirect URI requirements and forward responses to Junction.

Some OAuth providers require that redirect URIs belong to a domain you own and can verify. This is common during app review or when providers enforce verified domain policies.

In these cases, you cannot register a Junction redirect URI such as `https://api.us.junction.com/…` directly, because you do not own that domain.

The solution is to host a lightweight callback endpoint on your own domain that forwards the OAuth response to Junction.

## When this is required

Use this pattern if a provider:

* Requires proof of domain ownership for redirect URIs
* Enforces trusted or verified domains
* Rejects third-party redirect URIs

## Customer-hosted callback endpoint

The full setup has four parts: configure a redirect URI override in Junction, register with the provider, implement the endpoint, and target the correct Junction endpoint for your environment.

<Note>
  Examples in this guide use **Acme Fit** (`acme_fit`) as a fictional provider for illustration purposes.
</Note>

<Steps>
  <Step title="Configure your redirect URI in Junction">
    In your Junction dashboard (or via the Management API), set the redirect URL override for the provider to your domain's callback URL. You can find this under [custom credentials](/wearables/connecting-providers/bring-your-own-oauth/overview#through-the-dashboard).

    ```
    https://yourdomain.com/oauth_callback/{provider}
    ```

    Example:

    ```
    https://yourdomain.com/oauth_callback/acme_fit
    ```

    This is the URI Junction passes to the provider during the authorization code exchange. It must exactly match the URI you register in Step 2.

    <Note>
      The path shown in the redirect URI override is just a suggestion. You can use any path that meets your provider's redirect URI requirements — what matters is that it sits on a domain you control and that your endpoint forwards the OAuth response to Junction.
    </Note>
  </Step>

  <Step title="Register your redirect URI with the provider">
    Register the same URI from Step 1 as an allowed redirect URI with the provider:

    ```
    https://yourdomain.com/oauth_callback/{provider}
    ```
  </Step>

  <Step title="Implement the callback endpoint">
    Your endpoint must:

    * Accept the incoming OAuth redirect (GET request)
    * Return a `307 Temporary Redirect`
    * Forward the request to the appropriate Junction endpoint
    * Preserve the full original query string unchanged

    Example flow:

    ```
    Incoming request:
    GET /oauth_callback/acme_fit?code=abc123&state=xyz

    Response:
    307 Temporary Redirect
    Location: https://api.us.junction.com/v2/link/connect/acme_fit?code=abc123&state=xyz
    ```

    Implementation requirements:

    * Use HTTP `307` (not `302`) — this preserves the GET method
    * Forward the full query string exactly as received
    * Do not modify, parse, or re-encode any parameters — the `state` parameter is a signed token that must arrive at Junction unmodified
  </Step>

  <Step title="Use the correct Junction endpoint">
    Choose the Junction target URL based on your environment:

    <AccordionGroup>
      <Accordion title="Junction endpoint URLs by environment">
        | Environment     | Junction endpoint                                                |
        | --------------- | ---------------------------------------------------------------- |
        | Sandbox (US)    | `https://api.sandbox.us.junction.com/v2/link/connect/{provider}` |
        | Sandbox (EU)    | `https://api.sandbox.eu.junction.com/v2/link/connect/{provider}` |
        | Production (US) | `https://api.us.junction.com/v2/link/connect/{provider}`         |
        | Production (EU) | `https://api.eu.junction.com/v2/link/connect/{provider}`         |
      </Accordion>
    </AccordionGroup>

    <Note>
      Ensure your callback endpoint forwards to the correct environment. Do not forward sandbox traffic to production or vice versa.
    </Note>
  </Step>
</Steps>

## How it works

The OAuth provider validates the `redirect_uri` in two places: when the authorization request is initiated, and when Junction exchanges the authorization code for an access token. Junction uses the redirect URI stored in your team credentials (set in Step 1) for the code exchange — so both values must match exactly.

By registering your own domain as the redirect URI:

1. You satisfy the provider's domain ownership requirement
2. The provider redirects the user to your endpoint after authorization
3. Your endpoint forwards the callback to Junction via a `307` redirect
4. Junction completes the token exchange using your registered redirect URI


# Bring Your Own OAuth
Source: https://docs.junction.com/wearables/connecting-providers/bring-your-own-oauth/overview

Use Bring Your Own OAuth (BYOO) to connect wearable providers through Junction using your own branded OAuth client credentials.

<Info>
  Bring Your Own OAuth (BYOO) is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

<Info>
  WHOOP BYOO is available for [the Launch, Grow and Scale plans](https://tryvital.io/pricing). See [our WHOOP Guide](/wearables/guides/whoop) for more information.
</Info>

Junction supports bringing your own OAuth application credentials, so that your users would see the OAuth consent flow
in your own brand when they connect to cloud-based OAuth providers.

This provides a more cohesive experience for your users as it gives a truly white-labeled experience for your application/mobile application.

Typically when you don't use custom credentials your OAuth screen will look like this and use our shared credentials:

<img alt="Default Credentials" />

With custom credentials you can use your own team name and set up your own logo for the provider.

## OAuth Providers supporting BYOO

<Warning>
  Some BYOO providers do not have a developer program with open sign-up.
</Warning>

<Note>
  See [Supported Providers](/wearables/providers/introduction#oauth-providers) for supported OAuth providers, Junction default-app availability, and BYOO developer-program requirements.
</Note>

### Provider Rate Limits

<Note>
  This section documents the *starting* rate limit of a brand new OAuth application, should you choose to BYOO.
</Note>

| Provider                                        | Starting Rate Limit                                                                 |
| ----------------------------------------------- | ----------------------------------------------------------------------------------- |
| [Fitbit](https://www.fitbit.com/global/uk/home) | 150 requests per hour per user                                                      |
| [Garmin](https://www.garmin.com)                | 10,000 days of data per minute                                                      |
| [Google Fit](https://www.google.com/fit/)       | 600 requests per minute                                                             |
| [Oura](https://ouraring.com)                    | 5,000 requests per 5 minutes                                                        |
| [Strava](https://www.strava.com)                | 100 requests per 15 minutes<br />1,000 requests per day                             |
| [Wahoo](https://wahoofitness.com)               | 200 requests per 5 minutes<br />1,000 requests per hour<br />5,000 requests per day |
| [Withings](https://www.withings.com)            | 120 requests per minute                                                             |
| [Dexcom](https://www.dexcom.com)                | 60,000 requests per hour                                                            |
| [WHOOP](https://www.whoop.com)                  | 100 requests per minute<br />10,000 requests per day                                |
| [Polar](https://www.polar.com/accesslink-api/)  | 20 requests per 15 minutes per user<br />100 requests per day per user              |
| [Cronometer](https://www.cronometer.com)        | N/A                                                                                 |
| [Omron](https://www.omron-healthcare.com)       | N/A                                                                                 |

## Setting up your OAuth credentials

<Note>
  If a provider requires redirect URIs to belong to a domain you own, see [Hosting OAuth Callbacks on Your Own Domain](/wearables/connecting-providers/bring-your-own-oauth/callback-endpoints).
</Note>

### Through the Org Management API

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

<Steps>
  <Step title="Set up your OAuth application">
    Use the [Prepare Team Custom Credentials](/api-reference/org-management/team-custom-credentials/prepare-team-custom-credentials)
    endpoint to obtain the instructions to set up your OAuth application.
  </Step>

  <Step title="Set your application credentials">
    Use the [Set Team Custom Credentials](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials) endpoint
    to apply your OAuth application credentials to your Junction Team.

    <AccordionGroup>
      <Accordion title="Credentials that are activated immediately" icon="bolt">
        BYOO credentials for the following providers are immediately active once set through the [Set Team Custom Credentials](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials):

        | Provider                                                  | Slug                |
        | --------------------------------------------------------- | ------------------- |
        | [Garmin](https://www.garmin.com)                          | `garmin`            |
        | [Google Fit](https://www.google.com/fit/)                 | `google_fit`        |
        | [Google Health API](https://developers.google.com/health) | `google_health`     |
        | [Oura](https://ouraring.com)                              | `oura`              |
        | [Strava](https://www.strava.com)                          | `strava`            |
        | [Wahoo](https://wahoofitness.com)                         | `wahoo`             |
        | [Withings](https://www.withings.com)                      | `withings`          |
        | [Dexcom](https://www.dexcom.com)                          | `dexcom_v3`         |
        | [WHOOP](https://www.whoop.com)                            | `whoop_v2`          |
        | [Polar](https://www.polar.com/accesslink-api/)            | `polar`             |
        | [Cronometer](https://www.cronometer.com)                  | `cronometer`        |
        | [Omron](https://www.omron-healthcare.com)                 | `omron`             |
        | [Ultrahuman](https://www.ultrahuman.com)                  | `ultrahuman`        |
        | [MyFitnessPal API](https://www.myfitnesspalapi.com)       | `my_fitness_pal_v2` |
        | [MapMyFitness](https://developer.mapmyfitness.com)        | `map_my_fitness`    |
      </Accordion>

      <Accordion title="Credentials that need support intervention once set" icon="message-question">
        The following providers are not immediately active. You need to take a couple of extra steps to activate them:

        [Fitbit](https://www.fitbit.com/global/uk/home) (`fitbit`)

        1. You must press the "Verify" button on all the Webhook Subscribers in the Fitbit Developer Portal.
        2. Once the buttons are gone and replaced by (usually zeroed) delivery statistics, it implies that Fitbit has completed the verification challenge with Junction.
      </Accordion>
    </AccordionGroup>
  </Step>
</Steps>

### Through the Dashboard

To set up custom OAuth application credentials via the Dashboard, you need to do the following:

1. Create a developer account with the cloud-based OAuth providers.
2. Generate OAuth application credentials from your developer account. You can do this by going to the provider's developer account page and creating a new application.
3. Navigate to **Custom Credentials** under **Config** on the left sidebar in the Junction Dashboard.
4. Find the provider you want to configure and click the **Setup** button.
5. In the input form that appears, enter your **OAuth Client ID** and **OAuth Client Secret**.

<Note>
  Some OAuth providers are unavailable unless you can <b>Bring Your Own OAuth (BYOO)</b> — you will have to apply directly
  to the provider for an OAuth application, and provide Junction your assigned credentials once the application is accepted.
</Note>


# Sharing Credentials
Source: https://docs.junction.com/wearables/connecting-providers/bring-your-own-oauth/sharing-credentials

Share existing OAuth client credentials between Junction BYOO and your own systems when providers allow only one redirect URI.

## Audience

This guide is dedicated to Junction customers looking to share the usage of their OAuth Client Credentials
between Junction [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview) and their
existing production systems.

This guide is not applicable if you:

* do not have existing OAuth Client Credentials; or
* can configure your OAuth Client Credentials to point exclusively at Junction.

## Background

Many providers use OAuth 2.0, in which each partner entity is registered as a confidential application.
Each application is issued one set of OAuth client credentials (client ID + secret).

As a Cross-Site Request Forgery (CSRF) mitigation, they also typically require every application to pre-register
a Redirect URI. The `/authorize` flow would then validate inbound requests against the Redirect URI on record.

## Challenge

Most providers allow **only one** Redirect URI registration per OAuth application.

This means your OAuth application cannot simultaneously support both your own existing OAuth callback endpoint, as well as
the [Junction Link](/wearables/connecting-providers/introduction) OAuth callback endpoint.

## Recommended Approach

To support this credential sharing use case, Junction recommends that you:

1. Continue to point your OAuth application at your existing OAuth callback endpoint;

2. Extend your endpoint to [detect and redirect](#details) callbacks from Junction Link originated OAuth requests to the
   [Junction Link OAuth callback endpoint](#junction-oauth-callback-endpoint-url).

```mermaid theme={null}
sequenceDiagram
    participant Junction Link
    participant Your API
    participant BYOO Provider
    actor User Agent
    autonumber
    Junction Link-->>User Agent: OAuth 2.0 Authorization Request URL
    User Agent->>BYOO Provider: Navigate to URL (1)
    BYOO Provider-->>User Agent: Prompt
    User Agent-->>BYOO Provider: Approve or Deny
    BYOO Provider->>Your API: Redirect
    Your API->>Junction Link: Redirect
```

### Details

<Steps>
  <Step title="Keep your current OAuth application settings">
    The Redirect URI in your OAuth application settings should continue to point at your
    existing OAuth callback endpoint.
  </Step>

  <Step title="Extend your existing OAuth callback endpoint with Junction Link awareness">
    Your OAuth callback endpoints can rely on the `state` query parameter to differentiate the request
    origin, i.e., whether it is from Junction or from your own production systems.

    OAuth requests originating from Junction would have the **Junction Link Token** as the `state` query parameter.
    Junction Link Token is a JSON Web Token (JWT), so you can use the *unverified claims* of the JWT as a discriminator.

    When you detect a valid Junction Link Token, perform a `307 Temporary Redirect`
    to the [Junction OAuth callback endpoint](#junction-oauth-callback-endpoint-url) and pass on *all* the URL query parameters.

    The exact JWT structure of the Junction Link Token is as follows:

    <AccordionGroup>
      <Accordion title="Encoding">
        A JSON Web Token (JWT).

        <Warning>
          You need not verify the signature of this JWT.

          To prevent Cross-Site Request Forgery attacks, you must still check that the `aud` claim matches the
          expected [Junction Link API Base URL](#junction-link-api-base-url).
        </Warning>
      </Accordion>

      <Accordion title="Claim payload schema">
        | Key       | Value                      |
        | --------- | -------------------------- |
        | `aud`     | Junction Link API Base URL |
        | `sub`     | Junction Link Session ID   |
        | `team_id` | Junction Team ID           |
        | `user_id` | Junction User ID           |
      </Accordion>

      <Accordion title="Junction Link API Base URL">
        | Environment   | Base URL                                     |
        | ------------- | -------------------------------------------- |
        | Production US | `https://api.tryvital.io/v2/link`            |
        | Production EU | `https://api.eu.tryvital.io/v2/link`         |
        | Sandbox US    | `https://api.sandbox.tryvital.io/v2/link`    |
        | Sandbox EU    | `https://api.sandbox.eu.tryvital.io/v2/link` |
      </Accordion>

      <Accordion title="Junction OAuth callback endpoint URL">
        | Environment   | Base URL                                                        |
        | ------------- | --------------------------------------------------------------- |
        | Production US | `https://api.tryvital.io/v2/link/connect/{PROVIDER}`            |
        | Production EU | `https://api.eu.tryvital.io/v2/link/connect/{PROVIDER}`         |
        | Sandbox US    | `https://api.sandbox.tryvital.io/v2/link/connect/{PROVIDER}`    |
        | Sandbox EU    | `https://api.sandbox.eu.tryvital.io/v2/link/connect/{PROVIDER}` |
      </Accordion>

      <Accordion title="Example">
        ```python Python (FastAPI) theme={null}
        import fastapi
        from urllib.parse import urlencode

        VITAL_LINK_BASE_URL = "https://api.tryvital.io/v2/link"

        @router.get("/oauth_callback/fitbit")
        def handle_oauth_callback(
            request: fastapi.Request,
            state: Annotated[str, fastapi.Query()],
            ...,
        ) -> fastapi.Response:
            state_string = base64.b64decode(state)

            # Test if this is a JWT.
            # If so, try to extract the unverified claim payload, and see if this is
            # a Junction Link JWT.
            if (
                state_string.startswith('{"')
                and (state_parts := state_string.split(".", maxsplit=3))
                and len(state_parts) == 3
                and (unverified_claims := json.loads(state_parts[1]))
                and unverified_claims.get("aud") == VITAL_LINK_BASE_URL
            ):
                query = urlencode(request.query_params)

                return fastapi.RedirectResponse(f"{VITAL_LINK_BASE_URL}/connect/fitbit?{query}")


            # Not a Junction Link JWT. Fallback to existing logic
            return process_oauth_callback(...)

        ```
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Register your OAuth callback endpoint with Junction">
    When you set your OAuth Client Credentials through the [Set Team Custom Credentials](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials)
    endpoint, you must specify a Redirect URI override that points at your OAuth callback endpoint.

    When a Redirect URI override is present, Junction uses the override value you provided to initiate the OAuth authorization flow.
  </Step>
</Steps>


# Building a Custom Widget
Source: https://docs.junction.com/wearables/connecting-providers/custom_widget

Build a custom Link widget UI to connect wearable providers as an alternative to the default Junction Link hosted widget.

<Warning>
  Junction Link API covers **only** [cloud-based providers](/wearables/providers/introduction#cloud-based-providers).

  Your mobile app has to manage connections with SDK-based providers — such as Apple HealthKit and Android Health
  Connect — through the [Junction Mobile Health SDK](/wearables/sdks/health/overview), separately from Junction Link.
</Warning>

You may want to have your own custom Link widget to match your app's UI. This is useful for customers who don't want to use Junction Link but still want to use the Junction API. It's worth ensuring that you have read the [Auth Types](/wearables/connecting-providers/auth_types) documentation before proceeding. A full code example in React Native can be seen [here](https://github.com/tryVital/vital-connect-rn).

An overview of the process is as follows:

1. Get a list of all available providers ["/v2/providers"](/api-reference/providers)
2. Build your UI using this list of providers
3. When selecting a provider, generate a link token for that provider ["/v2/link/token"](/api-reference/link/generate-link-token)
4. Use the link token to generate an OAuth link, or authorize an email, or email and password.

## 1. List of all available providers

The first step to creating a custom widget is to make a call to the [providers endpoint](/api-reference/providers). This will return a list of all available providers. You can then use this list to build your UI.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const allProviders = await client.providers.getAll();

  // allProviders
  // [
  //   {
  //     "name": "Oura",
  //     "slug": "oura",
  //     "logo": "https://logo_url.com",
  //     "auth_type": "oauth",
  //     "supported_resources": ["workouts", "sleep"]
  //   }
  // ]
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  all_providers = client.providers.get_all()

  # allProviders
  # [
  #   {
  #     "name": "Oura",
  #     "slug": "oura",
  #     "logo": "https://logo_url.com",
  #     "auth_type": "oauth",
  #     "supported_resources": ["workouts", "sleep"]
  #   }
  # ]
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var allProviders = client.providers().getAll();
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Providers.GetAll(context.TODO(), nil)
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

## 2. Build your UI in your app

Once you have a list of all available providers, you can build your UI. You can use the `logo` field to display the provider's logo. You can use the `name` field to display the provider's name. You can use the `supported_resources` field to display the resources that the provider supports.

<Frame>
  <img alt="Custom UI" />
</Frame>

## 3. Generate a link token

Once a user has selected a provider, you can generate a link token. This link token is used to generate an OAuth link, or authorize an email, or email and password. You can read more about the link token [here](/api-reference/link/generate-link-token).

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const tokenResponse = await client.link.token({ userId: "<user_id>" });
  ```

  ```python Python theme={null}
  from junction import Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  token_response = client.link.token(user_id="<user_id>")
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.LinkTokenExchange;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var tokenResponse = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

## 4. Link the user to the provider

Once you have a link token, you can use it to generate an OAuth link, or authorize an email, or email and password.

### Connect an OAuth provider

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const tokenResponse = await client.link.token({ userId: "<user_id>" });

  const auth = await client.link.generateOauthLink({
      oauthProvider: "oura",
      vitalLinkToken: tokenResponse.linkToken,
  });
  ```

  ```python Python theme={null}
  from junction import Junction, OAuthProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  token_response = client.link.token(user_id="<user_id>")

  oauth = client.link.generate_oauth_link(
      OAuthProviders.OURA,
      vital_link_token=token_response.link_token,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.GenerateOauthLinkLinkRequest;
  import com.junction.api.resources.link.requests.LinkTokenExchange;
  import com.junction.api.types.OAuthProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var tokenResponse = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .build()
  );

  var oauth = client.link().generateOauthLink(
      OAuthProviders.OURA,
      GenerateOauthLinkLinkRequest.builder()
          .vitalLinkToken(tokenResponse.getLinkToken())
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  tokenResponse, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }

  oauth, err := c.Link.GenerateOauthLink(context.TODO(), &junction.GenerateOauthLinkLinkRequest{
      OauthProvider:  junction.OAuthProvidersOura,
      VitalLinkToken: &tokenResponse.LinkToken,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", oauth)
  ```
</CodeGroup>

### Connect an Email/Password provider

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.connectPasswordProvider({
      provider: "whoop",
      username: "<username>",
      password: "<password>",
  });
  ```

  ```python Python theme={null}
  from junction import Junction, PasswordProviders
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.connect_password_provider(
      PasswordProviders.WHOOP,
      username="<username>",
      password="<password>",
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.IndividualProviderData;
  import com.junction.api.types.PasswordProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().connectPasswordProvider(
      PasswordProviders.WHOOP,
      IndividualProviderData.builder()
          .username("<username>")
          .password("<password>")
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.ConnectPasswordProvider(context.TODO(), &junction.IndividualProviderData{
      Provider: junction.PasswordProvidersWhoop,
      Username: "<username>",
      Password: "<password>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

### Bringing this all together

Below is an example of how you can bring all of this together to build your own custom widget. This example is in React Native, but the same principles apply to any language.

```javascript React Native theme={null}
import React, {useEffect, useState} from 'react';
import {
  SafeAreaView,
  StatusBar,
  Linking,
  View,
  useColorScheme,
  Platform,
} from 'react-native';
import {VitalHealth, VitalResource} from '@tryvital/vital-health-react-native';

const handleOAuth = async (userId: string, item: Provider) => {
  const linkToken = await Client.Link.getLinkToken(
    userId,
    `${AppConfig.slug}://link`,
  );
  const link = await Client.Providers.getOauthUrl(item.slug, linkToken.link_token);

  Linking.openURL(link.oauth_url);
};

const ListItem = ({
  userId,
  item,
  navigation,
}: {
  userId: string;
  item: Provider;
  navigation: any;
}) => {
  const {colors} = useTheme();
  const isDarkMode = useColorScheme() === 'dark';
  const [isLoading, setLoading] = useState(false);

  const handleNativeHealthKit = async () => {
    const providerSlug =
      Platform.OS == 'ios'
        ? ManualProviderSlug.AppleHealthKit
        : ManualProviderSlug.HealthConnect;

    setLoading(true);

    const isHealthSDKAvailable = await VitalHealth.isAvailable();
    if (!isHealthSDKAvailable) {
      console.warn('Health Connect is not available on this device.');
      navigation.navigate('ConnectionCallback', {
        state: 'failed',
        provider: providerSlug,
      });
      return;
    }

    try {
      await VitalHealth.configure({
        logsEnabled: true,
        numberOfDaysToBackFill: 30,
        androidConfig: {syncOnAppStart: true},
        iOSConfig: {
          dataPushMode: 'automatic',
          backgroundDeliveryEnabled: true,
        },
      });
    } catch (e) {
      setLoading(false);
      console.warn(`Failed to configure ${providerSlug}`, e);
      navigation.navigate('ConnectionCallback', {
        state: 'failed',
        provider: providerSlug,
      });
    }
    await VitalHealth.setUserId(userId);
    try {
      await VitalHealth.askForResources([
        VitalResource.Steps,
        VitalResource.Activity,
        VitalResource.HeartRate,
        VitalResource.Sleep,
        VitalResource.Workout,
        VitalResource.BloodPressure,
        VitalResource.Glucose,
        VitalResource.Body,
        VitalResource.Profile,
        VitalResource.ActiveEnergyBurned,
        VitalResource.BasalEnergyBurned,
      ]);
      await VitalCore.createConnectedSourceIfNotExist(providerSlug);
      setLoading(false);
      navigation.navigate('ConnectionCallback', {
        state: 'success',
        provider: providerSlug,
      });
    } catch (e) {
      setLoading(false);
      navigation.navigate('ConnectionCallback', {
        state: 'failed',
        provider: providerSlug,
      });
    }
  };

  const onPress = async () => {
    if (item.auth_type === 'oauth') {
      await handleOAuth(userId, item);
    } else if (
      item.slug === 'apple_health_kit' ||
      item.slug === 'health_connect'
    ) {
      await handleNativeHealthKit();
    }
  };

  return (
    <Pressable onPress={() => onPress()}>
      {({isHovered, isFocused, isPressed}) => {
        return (
        <HStack space={'md'} justifyContent="flex-start">
            <VStack>
            <Text color={colors.text} fontType="medium">
                {item.name}
            </Text>
            <Text
                fontType="regular"
                color={colors.secondary}
                size="xs"
                flexShrink={1}
                flexWrap="wrap">
                {item.description}
            </Text>
            </VStack>
        </HStack>
        );
      }}
    </Pressable>
  );
};

export const LinkDeviceScreen = ({navigation}) => {
  const isDarkMode = useColorScheme() === 'dark';
  const {colors} = useTheme();
  const styles = makeStyles(colors);
  const [providers, setProviders] = React.useState<Provider[]>([]);
  const [devices, setDevices] = React.useState<Provider[]>([]);
  const [loading, setLoading] = React.useState<boolean>(false);
  const [searchText, setSearchText] = React.useState('');
  const [error, setError] = React.useState<string | null>(null);

  const [userId, setUserId] = React.useState('');

  const handleSearch = (text: string) => {
    setSearchText(text);
    if (text === '' && text.length <= 2) {
      setDevices(providers);
    } else {
      const filtered = providers.filter(item =>
        item.name.toLowerCase().includes(text.toLowerCase()),
      );
      setDevices(filtered);
    }
  };
  useEffect(() => {
    const getDevices = async () => {
      setLoading(true);
      setError(null);
      const user_id = await getData('user_id');
      if (user_id) {
        const providers = await Client.Providers.getProviders();
        setLoading(false);
        setUserId(user_id);
        setProviders(providers);
      } else {
        setUserId('');
        setLoading(false);
        console.warn('Failed to get all supported providers');
        setError('Failed to get devices');
      }
    };
  }, [navigation]);

  return (
    <SafeAreaView style={styles.container}>
      <StatusBar
        barStyle={isDarkMode ? 'light-content' : 'dark-content'}
        backgroundColor={styles.container.backgroundColor}
      />
      <View
        style={{
          paddingVertical: 10,
          paddingHorizontal: 16,
          flex: 1,
          width: '100%',
        }}>
        <VStack pb={10}>
          <HStack
            justifyContent={'space-between'}
            py={'$3'}
            alignItems="center">
            <H1>Connect a Device</H1>
            <Button onPress={() => navigation.goBack()} variant="link">
              <Ionicons name="close-outline" size={25} color={colors.text} />
            </Button>
          </HStack>
          <Input
            variant="outline"
            size="lg"
            isDisabled={false}
            isInvalid={false}
            isReadOnly={false}>
            <InputField
              color={colors.text}
              fontFamily={AppConfig.fonts.regular}
              onChangeText={handleSearch}
              value={searchText}
              placeholder="Search"
            />
          </Input>
        </VStack>

        {!loading && error ? (
          <VStack>
            <Text fontType="light">Failed to get supported Providers</Text>
          </VStack>
        ) : (
          <FlatList
            data={devices}
            renderItem={({item}) => (
              <ListItem userId={userId} item={item} navigation={navigation} />
            )}
            keyExtractor={item => item.slug}
          />
        )}
      </View>
    </SafeAreaView>
  );
};
```


# Link Errors
Source: https://docs.junction.com/wearables/connecting-providers/errors

Handle errors from Junction Link including OAuth flow query parameter errors and JSON response errors for password and email providers.

## Error delivery

### URL Query

Junction performs a 307 Temporary Redirect to the `redirect_url` specified in your Link Token in the following scenarios:

* All connections via [the Junction Link widget](/wearables/connecting-providers/introduction)
* [Connect OAuth Provider](/api-reference/link/link-oauth-provider) endpoint used by your [Custom Widget](/wearables/connecting-providers/custom_widget)

Error parameters would be appended to the `redirect_url` before the redirection. For example,
given a redirect\_url of `https://example.com/vital/callback`:

```
https://example.com/vital/callback
?state=error
&error_type=provider_credential_error
&error=Invalid%20username%20or%20password
```

### JSON Response

Junction responds 200 OK with a JSON response in the following scenarios:

* [Connect Password Provider](/api-reference/link/link-password-provider) endpoint used by your [Custom Widget](/wearables/connecting-providers/custom_widget)
* [Connect Email Provider](/api-reference/link/link-email-provider) endpoint used by your [Custom Widget](/wearables/connecting-providers/custom_widget)

Error parameters would be embedded into the JSON response. For example:

```json theme={null}
{
  "state": "error",
  "error_type": "provider_credential_error",
  "error": "Invalid username or password"
}
```

## Error Parameters

<ResponseField name="state" type="const string">
  Constant: `error`
</ResponseField>

<ResponseField name="error_type" type="string">
  One of the [Link Error Types](#error-types).
</ResponseField>

<ResponseField name="error" type="string">
  A human-readable error explanation for troubleshooting purposes.
  Your application logic should not rely on this message.
</ResponseField>

## Error Types

<Note>
  This list is **non-exhaustive**. Your application logic must gracefully handle any unrecognized error type.
</Note>

| Error Type                    | Description                                                                                                                                                                                                                                                             |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_token`               | The Link Token is invalid or malformed.                                                                                                                                                                                                                                 |
| `token_expired`               | The Link Token has expired.                                                                                                                                                                                                                                             |
| `token_consumed`              | A successful connection establishment has consumed this Link Token.                                                                                                                                                                                                     |
| `provider_credential_error`   | <ul><li>OAuth: An error occurred during the provider authentication flow.</li><li>Password & Email: The supplied user credential is invalid.</li></ul>                                                                                                                  |
| `provider_password_expired`   | The supplied password has expired due to the password rotation policy of the provider. The user must rotate their password directly with the provider before trying to connect again. <Info>Applicable only to the patient-based `abbott_libreview` integration.</Info> |
| `provider_api_error`          | An unexpected error occurred when Junction Link attempts to connect to the provider API.                                                                                                                                                                                |
| `unsupported_region`          | The region you specified is not supported. <Info>Applicable only to the practice-based `freestyle_libre` integration.</Info>                                                                                                                                            |
| `duplicate_connection`        | The provider credential has an existing connection to a different user in your team. <Info>Applicable only if you have opted in to the `reject_duplicate_connection` team setting.</Info>                                                                               |
| `required_scopes_not_granted` | User did not grant **all** required scopes, so Junction cannot establish the connection.                                                                                                                                                                                |
| `incorrect_mfa_code`          | The MFA code entered is incorrect.                                                                                                                                                                                                                                      |


# Introduction to Link
Source: https://docs.junction.com/wearables/connecting-providers/introduction

Introduction to Junction Link, the mechanism for connecting user wearable accounts to Junction as a hosted widget or custom API integration.

Junction Link enables your users to connect their accounts with [supported cloud-based providers](/wearables/providers/introduction)
to Junction.

Junction Link is offered in two forms:

* [Junction Link Widget](/wearables/connecting-providers/link_flow) is a web flow hosted by Junction, providing a complete
  provider connection wizard UX that is ready to integrate.

* [Junction Link API](/wearables/connecting-providers/custom_widget) allows you to build your own Link Widget-like user experience.

<Warning>
  Junction Link API covers **only** [cloud-based providers](/wearables/providers/introduction#cloud-based-providers).

  Your mobile app has to manage connections with SDK-based providers — such as Apple HealthKit and Android Health
  Connect — through the [Junction Mobile Health SDK](/wearables/sdks/health/overview), separately from Junction Link.
</Warning>

![link-gif](https://storage.googleapis.com/vital-assets/vitalLink.gif)


# Launching Link
Source: https://docs.junction.com/wearables/connecting-providers/launching_link

Launch Junction Link by creating a Link Token on your backend and using it to open the provider connection flow in your application.

<Warning>
  Junction Link API covers **only** [cloud-based providers](/wearables/providers/introduction#cloud-based-providers).

  Your mobile app has to manage connections with SDK-based providers — such as Apple HealthKit and Android Health
  Connect — through the [Junction Mobile Health SDK](/wearables/sdks/health/overview), separately from Junction Link.
</Warning>

<Steps>
  <Step title="Your Backend">
    Create a Junction Link Token for a user using the [Create Link Token](/api-reference/link/generate-link-token) endpoint.

    <Note>
      A Junction Link Token expires 60 minutes after its creation.
    </Note>

    <br />

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

      const client = new JunctionClient({
          apiKey: "YOUR_API_KEY",
          environment: JunctionEnvironment.Sandbox,
      });

      const data = await client.link.token({
          userId: "<user_id>",
          provider: "oura",
      });
      ```

      ```python Python theme={null}
      from junction import Junction, Providers
      from junction.environment import JunctionEnvironment

      client = Junction(
          api_key="YOUR_API_KEY",
          environment=JunctionEnvironment.SANDBOX,
      )

      data = client.link.token(user_id="<user_id>", provider=Providers.OURA)
      ```

      ```java Java theme={null}
      import com.junction.api.Junction;
      import com.junction.api.core.Environment;
      import com.junction.api.resources.link.requests.LinkTokenExchange;
      import com.junction.api.types.Providers;

      Junction client = Junction.builder()
          .apiKey("YOUR_API_KEY")
          .environment(Environment.SANDBOX)
          .build();

      var data = client.link().token(
          LinkTokenExchange.builder()
              .userId("<user_id>")
              .provider(Providers.OURA)
              .build()
      );
      ```

      ```go Go theme={null}
      import (
          "context"

          junction "github.com/junction-api/junction-go"
          "github.com/junction-api/junction-go/client"
          "github.com/junction-api/junction-go/option"
      )

      c := client.NewClient(
          option.WithApiKey("YOUR_API_KEY"),
          option.WithBaseURL(junction.Environments.Sandbox),
      )

      provider := junction.ProvidersOura
      response, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
          UserId:   "<user_id>",
          Provider: &provider,
      })
      if err != nil {
          return err
      }
      fmt.Printf("Received data %s\n", response)
      ```

      ```bash cURL theme={null}
      curl --request POST \
           --url {{BASE_URL}}/v2/link/token \
           --header 'Accept: application/json' \
           --header 'Content-Type: application/json' \
           --header 'x-vital-api-key: <API-KEY>'
      ```
    </CodeGroup>

    <br />
  </Step>

  <Step title="Your Frontend">
    Use the Link Token to [launch the Link Widget](/wearables/connecting-providers/link_flow) on your frontend.

    <img />

    <AccordionGroup>
      <Accordion title="Option 1: Opening the Link Widget by URL" icon="link">
        You can launch the Junction Link Widget using the Link Web URL (`link_web_url`) as provided by the
        [Generate a Link Token endpoint](/api-reference/link/generate-link-token):

        * If you have created the Link Token for a specific OAuth provider, Junction Link will dispatch
          the user automatically to the provider's sign-in page.

        * Otherwise, your user will be presented with a list of providers to connect to. You can
          customize the list via the `filter_on_providers` parameter when creating the Link Token.

        When the Link flow completes, the flow will redirect to the `redirect_url` you specified earlier
        when creating the Link Token. We will also append query parameters to the redirection target
        based on the Link flow outcome:

        | Flow state | Query parameters                                           |
        | ---------- | ---------------------------------------------------------- |
        | Success    | `state=success`                                            |
        | Error      | See [Link Errors](/wearables/connecting-providers/errors). |
      </Accordion>

      <Accordion title="Option 2: Using the `useVitalLink` React hook (`@tryvital/vital-link`)" icon="react">
        ```js Launching Junction-Link theme={null}
        import 'react-app-polyfill/ie11';
        import * as React from 'react';
        import * as ReactDOM from 'react-dom';
        import { useCallback } from 'react';

        import { useVitalLink } from '@tryvital/vital-link';
        import { useState } from 'react';

        const API_URL = process.env.API_URL ? process.env.API_URL : "http://localhost:3001"

        const getTokenFromBackend = async (userKey: string, env: string) => {
          const resp = await fetch(<backend_url>);
          return await resp.json();
        };

        const App = props => {
          const userKey = '560596b9-924b-4af9-a670-cf21870fdac5';
          const [isLoading, setLoading] = useState(false);

          const onSuccess = useCallback(metadata => {
            // Device is now connected.
          }, []);

          const onExit = useCallback(metadata => {
            // User has quit the link flow.
          }, []);

          const onError = useCallback(metadata => {
            // Error encountered in connecting device.
          }, []);

          const config = {
            onSuccess,
            onExit,
            onError,
            env: "sandbox"
          };

          const { open, ready, error } = useVitalLink(config);

          const handleVitalOpen = async () => {
            setLoading(true);
            const token = await getTokenFromBackend(userKey, "sandbox");
            open(token);
            setLoading(false);
          };

          return (
            <button
              type="button"
              className="button"
              onClick={handleVitalOpen}
              disabled={isLoading || !ready}
            >
              Open Junction Link
            </button>
          );
        };

        ReactDOM.render(<App />, document.getElementById('root'));
        ```
      </Accordion>
    </AccordionGroup>
  </Step>
</Steps>


# Link Flow
Source: https://docs.junction.com/wearables/connecting-providers/link_flow

Understand the end-to-end Junction Link flow for connecting wearable devices, from API key setup through Link Token generation.

<Warning>
  Junction Link API covers **only** [cloud-based providers](/wearables/providers/introduction#cloud-based-providers).

  Your mobile app has to manage connections with SDK-based providers — such as Apple HealthKit and Android Health
  Connect — through the [Junction Mobile Health SDK](/wearables/sdks/health/overview), separately from Junction Link.
</Warning>

The Junction Link flow starts when a user wants to connect a wearable device with Junction. An overview of the process can be found below.

<Steps>
  <Step title="Prerequisites">
    Make sure that:

    1. You have your Junction Team API Key from the [dashboard](https://app.junction.com) ready to use.

    2. You have created a Junction User that corresponds to a user in your domain. [See here](/home/quickstart#3-creating-your-first-user) for an example.
  </Step>

  <Step title="Generate a Link Token">
    Call the [Generate a Link Token endpoint](/api-reference/link/generate-link-token) with the target User ID using your Junction Team API Key.
  </Step>

  <Step title="Initiate the Link Flow">
    The API response provides a short-lived Link Token (`link_token`), as well as a Web URL (`link_web_url`).

    You can use the Web URL to launch the Junction Link Widget directly. Alternatively, you can use the Link Token
    in concert with the Junction Link API to [build your custom Link Widget experience](/wearables/connecting-providers/custom_widget).
  </Step>
</Steps>

<img />


# Abbott LibreView
Source: https://docs.junction.com/wearables/guides/abbott-libreview

Connect Abbott continuous glucose monitors like Freestyle Libre to Junction through the LibreView cloud platform integration.

## Overview

Junction supports two ways to connect to LibreView for Abbott CGMs:

| Provider Slug      | Remarks                                                                           |
| ------------------ | --------------------------------------------------------------------------------- |
| `abbott_libreview` | Connect via LibreView patient account in any region.                              |
| `freestyle_libre`  | Connect via regional LibreView practices (Junction-managed, or custom practices). |

<Warning> The abbott\_libreview provider is no longer supported for new connections. This change applies to organizations that do not already have at least one active abbott\_libreview connection. Existing abbott\_libreview connections will continue to sync as normal. </Warning>

### `freestyle_libre` for practice-based connections

With the `freestyle_libre` integration, users have to share/connect their LibreView patient account
with either Junction's LibreView practice, or a custom practice of yours.

Junction accesses the patient CGM data as a member of the LibreView practice.

### `abbott_libreview` for patient-based connections

With the `abbott_libreview` integration, users can connect their Abbott CGM devices simply by signing in
with their LibreView patient account. Junction accesses the patient CGM data as their personal account.

* You would not need to maintain any LibreView practices.
* You would not need to instruct your users to connect their account to a LibreView practice, before they can attempt to connect via Junction Link.

#### ⚠️ Prepare for regular disconnection with `abbott_libreview`

Patient-based connections through `abbott_libreview` may **disconnect** at least once a year due to password rotation enforcement.

You can observe the error state through:

1. the [Provider Connection Error](/event-catalog/provider.connection.error) event; and
2. the [Get User Connections endpoint](/api-reference/user/get-users-connected-providers).

The following error types are relevant in this password rotation situation:

| Error Type                  | Remarks                                                                                                                                                    |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider_credential_error` | The patient has changed their password. They need to re-connect with the new password via Junction Link.                                                   |
| `provider_password_expired` | Login is blocked due to password expiration. Once the patient has changed their password, they need to re-connect with the new password via Junction Link. |

When you detect a disconnection, guide your user to:

1. Sign into the LibreLink app or the LibreView web portal.
2. Update their account password.
3. Use the Junction Link Widget or your custom widget to re-establish the `abbott_libreview` connection.

## LibreView data uses a floating time basis

All LibreView (Freestyle Libre) data are on a **floating time basis**.

Please go through the [Timestamps and Time Zones](/wearables/providers/timestamps-and-time-zones) guide to understand the
implications, as well as how to properly handle floating time data.

## Libre Notes Timeseries

LibreView, in addition to standard CGM data, provides insights manually added by the user.
This data is available in the following types:

* `note`: A general note added by the user, often providing context for other entries.
* `carbohydrates`: Grams of carbohydrates logged by the user.
* `insulin_injection`: Units of insulin injected by the user, categorized as either `rapid_acting` or `long_acting`.

<Info>
  Timestamps for these events have hourly granularity and are marked with the beginning of the hour.
</Info>

## Connecting a LibreView patient account (`abbott_libreview`)

### Junction Link Widget

Abbott LibreView is automatically available as an option in the [Junction Link Widget](/wearables/connecting-providers/launching_link).

### Junction Link API (custom widget)

If you intend to build a [custom widget on top of the Junction Link API](/wearables/connecting-providers/custom_widget), Abbott LibreView is
exposed as a **password provider**:

<Steps>
  <Step title="Generate a Link Token">
    Start the linking process by [generating a short-lived Link Token](/api-reference/link/generate-link-token).
  </Step>

  <Step title="Submit username and password">
    Submit the username and password of the LibreView patient account to the [Link Password Provider](/api-reference/link/link-password-provider) endpoint.

    <Note>
      You need not specify any region in your request. Junction will match the correct LibreView region transparently.

      **Important:** Abbott LibreView linking can take longer than other providers. We recommend configuring an extended client-side timeout (up to 60 seconds) when making this Link request. If your timeout is shorter, linking may fail even when credentials are valid.
    </Note>
  </Step>

  <Step title="Handle the multi-factor authentication request">
    The [Link Password Provider](/api-reference/link/link-password-provider) endpoint responds with `state: "pending_provider_mfa"`,
    alongside the MFA method being used, as well as an MFA hint.

    This means the user must complete multi-factor authentication before Junction can establish the connection. You should prompt the user
    to input the verification code they received out-of-band.
  </Step>

  <Step title="Submit the MFA Code">
    Submit the MFA code to the [Complete Password Provider MFA](/api-reference/link/complete-password-provider-mfa) endpoint.

    <Note>You must use the same Link Token you generated in Step 1.</Note>
  </Step>

  <Step title="Linking completed">
    The linking process is completed. Historical pulls are scheduled to start as soon as possible.
  </Step>
</Steps>

## Setting up practice-based connections (`freestyle_libre`)

### Adding users to a Practice

When integrating with Junction, you have two options: use Junction's practice, or yours. We call the latter a custom practice. Depending on the approach, you need to instruct your users to connect their Libre account to a practice.

This allows Junction to start pulling data from their account. This is done through the Libre app:

<img />

### Supported Regions

#### 🇺🇸 Junction US region

| Region Code | Region        |
| ----------- | ------------- |
| `ca`        | Canada        |
| `hk`        | Hong Kong     |
| `in`        | India         |
| `kr`        | South Korea   |
| `ph`        | Philippines   |
| `sg`        | Singapore     |
| `tw`        | Taiwan        |
| `us`\*      | United States |

*(\*) Default if region is unspecified.*

#### 🇪🇺 Junction EU region

| Region Code | Region         |
| ----------- | -------------- |
| `ar`        | Argentina      |
| `at`        | Austria        |
| `au`        | Australia      |
| `be`        | Belgium        |
| `bh`        | Bahrain        |
| `br`        | Brazil         |
| `ch`        | Switzerland    |
| `cl`        | Chile          |
| `co`        | Colombia       |
| `cz`        | Czechia        |
| `de`        | Germany        |
| `dk`        | Denmark        |
| `eg`        | Egypt          |
| `es`        | Spain          |
| `fi`        | Finland        |
| `fr`        | France         |
| `gb`\*      | United Kingdom |
| `gr`        | Greece         |
| `hk`        | Hong Kong      |
| `hr`        | Croatia        |
| `ie`        | Ireland        |
| `il`        | Israel         |
| `in`        | India          |
| `it`        | Italy          |
| `jo`        | Jordan         |
| `kr`        | South Korea    |
| `kw`        | Kuwait         |
| `lb`        | Lebanon        |
| `lu`        | Luxembourg     |
| `mx`        | Mexico         |
| `nl`        | Netherlands    |
| `no`        | Norway         |
| `nz`        | New Zealand    |
| `om`        | Oman           |
| `ph`        | Philippines    |
| `pl`        | Poland         |
| `pt`        | Portugal       |
| `qa`        | Qatar          |
| `sa`        | Saudi Arabia   |
| `se`        | Sweden         |
| `sg`        | Singapore      |
| `si`        | Slovenia       |
| `sk`        | Slovakia       |
| `tr`        | Türkiye        |
| `tw`        | Taiwan         |
| `za`        | South Africa   |

*(\*) Default if region is unspecified.*

### Bring Your Own Practice

<Info>
  `freestyle_libre` custom practice connections are available for [the Scale plan](https://tryvital.io/pricing).
</Info>

<Steps>
  <Step title="Obtain the Junction practice email address">
    Get in touch with [Junction Support](/home/getting-support) to request the region-specific Junction practice email address.
  </Step>

  <Step title="Invite the Junction practice">
    Invite the Junction practice email address to join your LibreView practice as a **Care Team Admin**.
  </Step>

  <Step title="Confirm with Junction support">
    Inform Junction support of the invitation, and wait for the support acknowledgment of successful practice connection.
  </Step>
</Steps>

Once the connection is established, Junction Link would recognize all patient emails having connected to your LibreView practice.
These patients do not need to connect separately to the [Junction practices](#using-junction-practices).

#### Binding a Team to a Practice

<Info>
  This feature is available for [the Scale plan](https://tryvital.io/pricing) and requires a custom practice to be set up first.
</Info>

By default, when a patient connects via Junction Link, the patient search runs across all practices that
Junction is **Care Team Admin** of. You can bind a team to a specific LibreView practice to restrict this. When a
practice binding is configured, a patient will only be able to connect if they are a member of that practice.

You can manage your team's practice bindings through either the **Dashboard** or the **API**:

* **Dashboard**: Navigate to your team's **Custom Credentials** page and set up the `Freestyle Libre` provider
  with your Practice ID.
* **API**: Use the [Team Custom Credentials API](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials)
  with the `freestyle_libre` provider. The `client_id` field should be set to your **LibreView Practice ID**.

The Practice ID is the identifier you chose when creating your practice in the LibreView portal. The match is case-insensitive.

<Note>
  For `freestyle_libre` practice bindings, only the Practice ID is required. The `client_secret`, `details`, and
  `redirect_url_override` fields are not supported and must be omitted when using the API.
</Note>

Practice bindings only affect new connections. Existing connections will continue to sync regardless of
whether a binding is added, changed, or removed.

### Using Junction Practices

<Info>
  You can use a Junction practice to test the integration with Freestyle Libre.
  This modality is available to all subscriptions.
</Info>

Junction has two LibreView Practices that are available in all supported regions:

| Environment | Practice Name    |
| ----------- | ---------------- |
| Sandbox     | tryVital-sandbox |
| Production  | tryVital         |

You need to ask your user to add one of the above practices in their Libre App. Once that's done, data will start flowing.

### Junction Link Widget

To connect your user to Junction via the Link Widget you can follow the guide [here](/wearables/connecting-providers/launching_link).

<img />

### Junction Link API (custom widget)

1. To connect to Freestyle Libre, you must create a Junction Link token.

<CodeGroup>
  ```python Python theme={null}
  token_response = client.link.token(user_id="<user_id>")
  ```

  ```typescript TypeScript theme={null}
  const tokenResponse = await client.link.token({ userId: "<user_id>" });
  ```

  ```java Java theme={null}
  var tokenResponse = client.link().token(
      LinkTokenExchange.builder()
          .userId("<user_id>")
          .build()
  );
  ```

  ```go Go theme={null}
  tokenResponse, err := c.Link.Token(context.TODO(), &junction.LinkTokenExchange{
      UserId: "<user_id>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", tokenResponse)
  ```
</CodeGroup>

2. Libre is an [email](/api-reference/link/link-email-provider) provider.
   This means that when connecting a user we have to use
   the connect email provider method, passing in the email of the user's Libre account and region in short form.

<CodeGroup>
  ```python Python theme={null}
  linkResponse = client.link.connect_email_auth_provider(
      email="<email>",
      vital_link_token=token_response.link_token,
  )
  ```

  ```typescript TypeScript theme={null}
  const connectEmailResponse = await client.link.connectEmailAuthProvider({
      provider: "freestyle_libre",
      email: "<email>",
      vitalLinkToken: tokenResponse.linkToken,
  });
  ```

  ```java Java theme={null}
  var data = client.link().connectEmailAuthProvider(
      "freestyle_libre",
      EmailProviderAuthLink.builder()
          .email("<email>")
          .vitalLinkToken(tokenResponse.getLinkToken())
          .build()
  );
  ```

  ```go Go theme={null}
  emailAuth, err := c.Link.ConnectEmailAuthProvider(context.TODO(), &junction.EmailProviderAuthLink{
      Provider:       "freestyle_libre",
      VitalLinkToken: &tokenResponse.LinkToken,
      Email:          "<email>",
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", emailAuth)
  ```
</CodeGroup>

Once this is complete, we will start syncing data from their [Libre account](/webhooks/introduction).


# Android Health Connect
Source: https://docs.junction.com/wearables/guides/android-health-connect

Integrate Android Health Connect as an SDK-based provider with Junction, including setup, permissions, and required Mobile SDK components.

Android Health Connect is an SDK-based provider. Your Android consumer app would embed the Junction Mobile SDKs on a supported
stack. Data are then pulled from the Health Connect data store on the user's Android device.

Refer to the [Mobile SDK Installation](/wearables/sdks/installation) and [Junction Health SDK](/wearables/sdks/health/overview) guides for SDK installation
instructions and general API usage. This guide contains information on the behavior and required configuration
specific to the Android Health Connect integration.

<Card title="Mobile SDK Installation" icon="helmet-safety" href="/wearables/sdks/installation">
  Learn about the minimum runtime and build-time requirements of Junction Mobile SDKs, and how to add them into
  your project through your package dependency manager.
</Card>

<Card title="Junction Health SDK" icon="heart" href="/wearables/sdks/health/overview">
  Learn about the initial setup required by Junction Health SDK, and the API exposed by Junction Health SDK
  for managing and inspecting health data permissions and automatic data sync.
</Card>

## Getting Started

To enable this integration, you would need to integrate [Junction Core SDK](/wearables/sdks/vital-core) and
[Junction Health SDK](/wearables/sdks/health/overview) into your Native Android, React Native or Flutter mobile app.

<Steps>
  <Step title="Add Junction SDKs as dependencies">
    Review the [installation requirements and package installation instructions](/wearables/sdks/installation).
  </Step>

  <Step title="Integrate with Junction Mobile SDK Authentication">
    Follow the [SDK Authentication](/wearables/sdks/authentication) guidance to integrate your app with the authentication
    mechanism of Junction Core SDK.
  </Step>

  <Step title="Configure Health SDK and ask user for read permissions">
    Follow the [Junction Health SDK](/wearables/sdks/health/overview) starting guidance to
    [configure the Health SDK](/wearables/sdks/health/overview#configure-the-junction-health-sdk) and to
    [ask for health data read permissions](/wearables/sdks/health/overview#ask-user-for-health-data-permissions) from your user.

    <Note>
      Health data sync is activated only on `VitalResource`s for which you have asked your user for permissions. If you
      did not ask for permissions, data sync would not occur.
    </Note>
  </Step>

  <Step title="[Optional] Customize the Foreground Service notification">
    Follow the [Running as Foreground Service](#running-as-foreground-service) guidance below to
    customize the Foreground Service notification which may be shown by the Android OS during
    any prolonged health data sync attempts.
  </Step>

  <Step title="[Optional] Configure Background Sync">
    Follow the [Background Sync](#background-sync) guidance below to
    configure regular background sync.
  </Step>
</Steps>

## Review the Google Play Store policy

Apps using Android Health Connect **require prior Google approval** before they can be released on Google Play Store.

Check out the official [Google Play Store policy on Android Health Connect](https://support.google.com/googleplay/android-developer/answer/12991134?hl=en-GB).
Note that the application form is located under the "How do I access data through Health Connect?" section.

## Configure your AndroidManifest.xml

### Health Connect privacy dialog

<Warning>Missing manifest declarations may result in Health Connect ignoring your app.</Warning>

Check [the Health Connect Getting Started guide](https://developer.android.com/health-and-fitness/guides/health-connect/develop/get-started#show-privacy-policy) for the official requirements.

Here is a minimum declaration example:

```xml theme={null}
<manifest ...>
    <application ...>
        <!-- BEGIN: Mandatory for Health Connect permission flow (Android 13 or below) -->
        <!-- Must either be your MainActivity, or a separate Activity; cannot be an activity-alias. -->
        <activity android:name=".MainActivity" ...>
            <intent-filter>
                <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
            </intent-filter>
        </activity>
        <!-- END -->

        <!-- BEGIN: Mandatory for Health Connect permission flow (Android 14 or above) -->
        <!-- Can be an activity or an activity-alias. -->
        <activity-alias
            android:name="ViewPermissionUsageActivity"
            android:exported="true"
            android:targetActivity=".MainActivity"
            android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
            <intent-filter>
                <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
                <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
            </intent-filter>
        </activity-alias>
        <!-- END -->
    </application>

    <queries>
        <package android:name="com.google.android.apps.healthdata" />
    </queries>
</manifest>
```

### Health Connect permissions

Your app manifest (`AndroidManifest.xml`) must declare all the read or write permissions for all the data types you intend to sync.

For example, if you intend to sync Blood Pressure records and Blood Glucose records, your app manifest must contain the following
`<uses-permission>` declarations:

```xml theme={null}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!-- ... other declarations -->

    <!-- BEGIN: Health Connect permissions -->
    <uses-permission android:name="android.permission.health.READ_BLOOD_GLUCOSE" />
    <uses-permission android:name="android.permission.health.READ_BLOOD_PRESSURE" />
    <!-- END: Health Connect permissions -->
</manifest>
```

| SDK `VitalResource` type | Read permissions required                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Profile`                | `android.permission.health.READ_HEIGHT`                                                                                                                                                                                                                                                                                                                                                                      |
| `Body`                   | `android.permission.health.READ_BODY_FAT`<br />`android.permission.health.READ_WEIGHT`                                                                                                                                                                                                                                                                                                                       |
| `Workout`                | `android.permission.health.READ_EXERCISE`<br />`android.permission.health.READ_HEART_RATE`<br />`android.permission.health.READ_RESPIRATORY_RATE`<br />`android.permission.health.READ_DISTANCE`<br />`android.permission.health.READ_ACTIVE_CALORIES_BURNED`<br />`android.permission.health.READ_ELEVATION_GAINED`<br />`android.permission.health.READ_POWER`<br />`android.permission.health.READ_SPEED` |
| `Activity`               | `android.permission.health.READ_ACTIVE_CALORIES_BURNED`<br />`android.permission.health.READ_BASAL_METABOLIC_RATE`<br />`android.permission.health.READ_TOTAL_CALORIES_BURNED`<br />`android.permission.health.READ_DISTANCE`<br />`android.permission.health.READ_STEPS`<br />`android.permission.health.READ_FLOORS_CLIMBED`<br />`android.permission.health.READ_VO2_MAX`                                 |
| `Sleep`                  | `android.permission.health.READ_SLEEP`<br />`android.permission.health.READ_HEART_RATE`<br />`android.permission.health.READ_RESPIRATORY_RATE`<br />`android.permission.health.READ_HEART_RATE_VARIABILITY`<br />`android.permission.health.READ_OXYGEN_SATURATION`<br />`android.permission.health.READ_RESTING_HEART_RATE`                                                                                 |
| `Glucose`                | `android.permission.health.READ_BLOOD_GLUCOSE`                                                                                                                                                                                                                                                                                                                                                               |
| `BloodPressure`          | `android.permission.health.READ_BLOOD_PRESSURE`                                                                                                                                                                                                                                                                                                                                                              |
| `HeartRate`              | `android.permission.health.READ_HEART_RATE`                                                                                                                                                                                                                                                                                                                                                                  |
| `Steps`                  | `android.permission.health.READ_STEPS`                                                                                                                                                                                                                                                                                                                                                                       |
| `ActiveEnergyBurned`     | `android.permission.health.READ_ACTIVE_CALORIES_BURNED`                                                                                                                                                                                                                                                                                                                                                      |
| `BasalEnergyBurned`      | `android.permission.health.READ_ACTIVE_CALORIES_BURNED`<br />`android.permission.health.READ_BASAL_METABOLIC_RATE`<br />`android.permission.health.READ_TOTAL_CALORIES_BURNED`                                                                                                                                                                                                                               |
| `Water`                  | `android.permission.health.READ_HYDRATION`                                                                                                                                                                                                                                                                                                                                                                   |

### Foreground Service permissions

Junction Health SDK runs all its data sync workers inside short-service Foreground Services.

At build time, Junction Health SDK injects all the required `shortService` Foreground Services declarations
into your AndroidManifest.xml. You need not modify your app's Manifest to enable this.

<Info>
  Junction Health SDK can run work inside the [AndroidX WorkManager](https://developer.android.com/develop/background-work/background-tasks/persistent/how-to/long-running#declare-foreground-service-types-manifest)
  foreground service, as well as our own `SyncOnExactAlarmService`. The merged AndroidManifest.xml of your app
  would include both of them.
</Info>

## Prepare your app architecture

<Warning>Non-compliance with the guidelines below may result in Health Connect permission flow failing to launch on specific Android OS versions.</Warning>

<Tabs>
  <Tab title="Native Android">
    When requesting permission using the `ActivityResultContract` created by Junction Health `createPermissionRequestContract()`, you must launch
    the contract using AndroidX Activity Result API.

    Attempts to launch the contract manually via the legacy `Activity.startActivityForResult` API would result in an `android.content.ActivityNotFoundException`
    exception on Android 14 and later.
  </Tab>

  <Tab title="React Native">
    The `MainActivity` of your Android host app project must be augmented with either of the two options below:

    1. Subclassing `VitalHealthReactActivity` provided by the Junction Health SDK `com.vitalhealthreactnative` package, instead of the default `ReactActivity`.

    2. Overriding `onRequestPermissionsResult(...)` so as to manually propagate permission request results to the Junction Health SDK.

    ```kotlin theme={null}
    // MainActivity in your host app project
    open class MainActivity: ReactActivity() {
      override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
      ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        // Propagate permission flow results to Junction Health SDK.
        VitalHealthReactNativeModule.onRequestPermissionsResult(
          this.reactInstanceManager,
          requestCode,
          permissions,
          grantResults
        )
      }
    }
    ```

    The augmentation above is a workaround to React Native's AndroidX-incompatible handling of permission request result callbacks,
    which breaks the AndroidX Activity Result API required by Android Health Connect.
  </Tab>

  <Tab title="Flutter">
    The `MainActivity` of your Android host app project must subclass `FlutterFragmentActivity` instead of `FlutterActivity`.

    This is because `FlutterFragmentActivity` is AndroidX compatible and therefore supports the Activity Result API as required by
    Android Health Connect, whilst `FlutterActivity` is not.
  </Tab>
</Tabs>

## Synchronization

### Sync On App Launch

When your app resumes from background (i.e., having no Activity), Junction Health SDK triggers a data sync on all the resources
to which the user has granted read permission.

The SDK imposes a **2-minute** throttle on automatic data sync. This is to prevent rapid app switching from causing an
excessive amount of data sync work being scheduled.

<Info>
  Junction Health SDK relies on the [AndroidX ProcessLifecycleOwner](https://developer.android.com/reference/androidx/lifecycle/ProcessLifecycleOwner)
  to get notified of your app's resumption.
</Info>

### Running as Foreground Service

<Info>You cannot opt-out of this behavior.</Info>

Junction Health SDK runs all its data sync workers inside a Foreground Service. Running as a foreground service helps in two ways:

1. It ensures ongoing data sync can run to completion even if the user switches away from your app; and

2. data sync workers have to be **in foreground** to read data from Health Connect. Health Connect prohibits
   reading data from background.

Android requires every Foreground Service to [be associated with a user-visible notification](https://developer.android.com/develop/background-work/services/foreground-services#user-dismiss-notification).
The OS typically grants a grace period of about 10 seconds before making the user aware of this notification. In other words,
if the data sync worker completes within the grace period, no notification would be posted.

Junction Health SDK installs these notification copies by default:

| Item                 | Copy                                                             |
| -------------------- | ---------------------------------------------------------------- |
| Notification Title   | Health Data Sync                                                 |
| Notification Content | `{APP_NAME}` is synchronizing with Health Connect...             |
| Channel Title        | Health Data Sync                                                 |
| Channel Description  | Notifies when `{APP_NAME}` is synchronizing with Health Connect. |

You can customize it in two ways:

<Tabs>
  <Tab title="Native Android">
    Register your custom `SyncNotificationBuilder` through `VitalHealthConnectManager.syncNotificationBuilder`.

    You should register it as soon as your app process is created. One way to ensure this is through the AndroidX Startup
    library. You can define an `Initializer` of your own that depends on the SDK `VitalHealthConnectInitializer`.

    #### An example App Startup Initializer implementation

    ```kotlin theme={null}
    package com.example.app

    import android.content.Context
    import androidx.startup.Initializer
    import io.tryvital.vitalhealthconnect.VitalHealthConnectManager
    import io.tryvital.vitalhealthconnect.VitalHealthConnectInitializer

    class ExampleSyncNotificationBuilderInitializer: Initializer<Unit> {
        override fun create(context: Context) {
            val manager = VitalHealthConnectManager.getOrCreate(context)
            manager.syncNotificationBuilder = ExampleSyncNotificationBuilder
        }

        override fun dependencies(): MutableList<Class<out Initializer<*>>> = mutableListOf(
            VitalHealthConnectInitializer::class.java,
        )
    }
    ```

    #### An example `SyncNotificationBuilder` implementation

    ```kotlin theme={null}
    package io.tryvital.sample

    import android.app.Notification
    import android.app.NotificationChannel
    import android.app.NotificationManager
    import android.content.Context
    import android.content.Context.NOTIFICATION_SERVICE
    import androidx.core.app.NotificationCompat
    import io.tryvital.vitalhealthconnect.SyncNotificationBuilder
    import io.tryvital.vitalhealthconnect.model.VitalResource

    object ExampleSyncNotificationBuilder: SyncNotificationBuilder {
        override fun build(context: Context, resources: Set<VitalResource>): Notification {
            return NotificationCompat.Builder(context, createChannel(context))
                .setContentTitle("Example Sync")
                .setContentText("Syncing your data")
                .setOngoing(true)
                .setSmallIcon(android.R.drawable.ic_popup_sync)
                .build()
        }

        fun createChannel(context: Context): String {
            val importance = NotificationManager.IMPORTANCE_MIN
            val mChannel = NotificationChannel("ExampleSyncNotification", "Example Sync", importance)
            mChannel.description = "Notifies when Example is syncing your data"
            val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(mChannel)
            return mChannel.id
        }
    }
    ```
  </Tab>

  <Tab title="React Native">
    Register your custom copies through `VitalHealth.setSyncNotificationContent` as early as possible in
    your React Native root component.

    ```typescript theme={null}
    import { VitalHealth } from '@tryvital/vital-health-react-native';

    await VitalHealth.setSyncNotificationContent({
        "notificationTitle": "Example Sync",
        "notificationContent": "Syncing your data",
        "channelName": "Example Sync",
        "channelDescription": "Notifies when Example is syncing your data"
    });
    ```
  </Tab>

  <Tab title="Flutter">
    Register your custom copies through `vital_health.setSyncNotificationContent` as early as possible in
    your Flutter app startup logic.

    ```dart theme={null}
    import 'package:vital_health/vital_health.dart' as vital_health;

    vital_health.setSyncNotificationContent(vital_health.SyncNotificationContent(
        "Example Sync",
        "Syncing your data",
        "Example Sync",
        "Notifies when Example is syncing your data"));
    ```
  </Tab>
</Tabs>

## Background Sync

Junction Health SDK supports an **opt-in** Android Background Sync feature. It provides a continuous monitoring
experience with Android Health Connect, similar to [Background Delivery for Apple HealthKit](/wearables/guides/apple-healthkit#setting-up-apple-healthkit-background-delivery) on iOS.

### Sync Frequency

The [Sync On App Launch](#sync-on-app-launch) behavior is always-on.

When Background Sync is enabled, the SDK additionally schedules hourly sync using the Android [Exact Alarms](https://developer.android.com/develop/background-work/services/alarms/schedule) mechanism.

The OS has full discretion on whether to honor or defer the time as scheduled by the Junction Health SDK. This includes the policies
of the base Android OS, as well as any vendor-specific OS augmentation.

For example, the device may enter [Doze mode](https://developer.android.com/training/monitoring-device-state/doze-standby) during device inactivity. Doze mode
would batch and defer most Exact Alarms and other background work to run at a lower frequency.

<Info>
  Not to be confused with Alarm Clock apps, **Exact Alarm** is the technical name of an Android framework for scheduling
  app wake-ups in background at certain wall clock time. It is the Junction Health SDK being silently "alarmed" in background.

  Your user **will not be alarmed hourly** as a result of enabling Background Sync.

  However, if the data sync took longer than 10 seconds, the OS might notify them of this occurrence with a user-visible notification.
  The notification content is configurable by you — see the ["Running as Foreground Service"](#running-as-foreground-service) section for more details.
</Info>

### Runtime Permission Request

Since Android 12 (API Level 31), scheduling Exact Alarm (`SCHEDULE_EXACT_ALARM`) requires a runtime permission request from the user.
More specifically:

1. The user must go through an interactive flow to grant the *"Alarms & Reminders"* permission.
   * This flow is also reachable through the *"Alarms & Reminders"* section in Android Settings.
2. The user may refuse to grant the *"Alarms & Reminders"* permission.
3. The user may revoke the *"Alarms & Reminders"* permission through Android Settings at any time.

Since Android 13 (API Level 33), a new mutually exclusive `USE_EXACT_ALARM` permission was introduced. This permits your app to
schedule Exact Alarms without interactive permission requests.

<Warning>
  `USE_EXACT_ALARM` attracts scrutiny during Google Play Store review. Per the Google Play [Exact alarm permission policy](https://support.google.com/googleplay/android-developer/answer/9888170?hl=en-GB):

  > USE\_EXACT\_ALARM is a restricted permission and apps must only declare this permission if their core functionality supports
  > the need for an exact alarm. Apps that request this restricted permission are subject to review, and those that do not meet
  > the acceptable use case criteria will be disallowed from publishing on Google Play.
  >
  > *(excerpted on 20 March 2024)*

  If you choose to incorporate `USE_EXACT_ALARM`, you should prepare to justify to Google Play Store:

  1. Why hourly background sync is essential to your product experience; and
  2. Why the interactive permission request required by `SCHEDULE_EXACT_ALARM` is suboptimal for your product experience.
</Warning>

Here is a matrix summarizing the Exact Alarm permission request requirements:

| Android OS           | API Level | Requirements                                                                                                                                                                    |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android 11 or below  | \<=30     | No interactive permission request.                                                                                                                                              |
| Android 12           | 31, 32    | `SCHEDULE_EXACT_ALARM`: Requires a runtime permission request. Revocable.                                                                                                       |
| Android 13 and above | >=33      | App must declare either:<ul><li>`SCHEDULE_EXACT_ALARM`: Requires runtime permission request. Revocable.</li><li>`USE_EXACT_ALARM`: No interactive permission request.</li></ul> |

### Updating your AndroidManifest.xml

Your app's AndroidManifest.xml must include the following `uses-permission` claims:

<Tabs>
  <Tab title="The SCHEDULE_EXACT_ALARM route">
    <Note>
      Make sure you read and understand [Runtime Permission Request](#runtime-permission-request) before moving forward.
      This option is mutually exclusive with `USE_EXACT_ALARM` in the second tab.
    </Note>

    ```xml theme={null}
    <?xml version="1.0" encoding="utf-8"?>
    <manifest ...>

        <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

    </manifest>
    ```
  </Tab>

  <Tab title="The USE_EXACT_ALARM route">
    <Warning>
      Make sure you read and understand [Runtime Permission Request](#runtime-permission-request) before moving forward,
      especially the Google Play Store review policy surrounding the `USE_EXACT_ALARM` permission.
      This option is mutually exclusive with `SCHEDULE_EXACT_ALARM` in the first tab.
    </Warning>

    ```xml theme={null}
    <?xml version="1.0" encoding="utf-8"?>
    <manifest ...>

        <uses-permission android:name="android.permission.USE_EXACT_ALARM" android:minSdkVersion="33" />

        <!-- Fallback to SCHEDULE_EXACT_ALARM on API Level 31-32 -->
        <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:maxSdkVersion="32" />

    </manifest>
    ```
  </Tab>
</Tabs>

### Enabling Background Sync

<Tabs>
  <Tab title="Native Android">
    You can enable Background Sync through `enableBackgroundSyncContract()` (an AndroidX `ActivityResultContract`).

    If the runtime [requires an interactive permission request](#runtime-permission-request), this contract will launch
    an Android Intent to request the *"Alarms & Reminders"* permission. The contract result is a boolean, indicating whether
    or not the Background Sync has been enabled successfully. For example, if the user did not grant the permission during the said Android Intent,
    the contract returns `false`.

    Otherwise, the contract returns `true` synchronously when no user interaction is required.

    You must launch this `ActivityResultContract` either through the in-built Android Compose support, or
    through the AndroidX Activity Result API if you do not use Android Compose.

    You can also inspect if Background Sync has been enabled through the `isBackgroundSyncEnabled` property.

    <CodeGroup>
      ```kotlin Android Compose theme={null}
      import androidx.activity.compose.rememberLauncherForActivityResult
      import androidx.compose.runtime.rememberCoroutineScope
      import androidx.compose.material3.*
      import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

      val manager: VitalHealthConnectManager = ...

      val permissionsLauncher = rememberLauncherForActivityResult(
          manager.enableBackgroundSyncContract()
      ) { success ->
          Log.i("VitalBackgroundSync", "Enabled? $success")

          val enabled = manager.isBackgroundSyncEnabled
          Log.i("VitalBackgroundSync", "Enabled? $enabled")
      }

      Button(onClick = { permissionsLauncher.launch(Unit) }) {
          Text("Enable Background Sync")
      }
      ```

      ```kotlin AndroidX Activity Result API theme={null}
      import androidx.activity.ComponentActivity
      import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

      val activity: ComponentActivity = ...
      val manager: VitalHealthConnectManager = ...

      activity.registerForActivityResult(contract) { success ->
          Log.i("VitalBackgroundSync", "Enabled? $success")

          val enabled = manager.isBackgroundSyncEnabled
          Log.i("VitalBackgroundSync", "Enabled? $enabled")
      }
      ```
    </CodeGroup>
  </Tab>

  <Tab title="React Native">
    You can enable Background Sync through `enableBackgroundSync()`.

    If the runtime [requires an interactive permission request](#runtime-permission-request),
    `enableBackgroundSync()` will launch an Android Intent to request the *"Alarms & Reminders"* permission.
    The promise result is a boolean, indicating whether or not the Background Sync has been enabled successfully.
    For example, if the user did not grant the permission during the said Android Intent, the contract returns `false`.

    Otherwise, `enableBackgroundSync()` returns `true` immediately when no user interaction is required.

    You can also inspect if Background Sync has been enabled through the `isBackgroundSyncEnabled` property.

    <Note>
      When running on iOS, both `enableBackgroundSync()` and `isBackgroundSyncEnabled` always return `true` immediately.
    </Note>

    ```typescript theme={null}
    import { VitalHealth } from "@tryvital/vital-health-react-native";

    const success = await VitalHealth.enableBackgroundSync();
    console.log("background sync enabled?", success);

    const enabled = await VitalHealth.isBackgroundSyncEnabled;
    console.log("background sync enabled?", enabled);
    ```
  </Tab>

  <Tab title="Flutter">
    You can enable Background Sync through `enableBackgroundSync()`.

    If the runtime [requires an interactive permission request](#runtime-permission-request),
    `enableBackgroundSync()` will launch an Android Intent to request the *"Alarms & Reminders"* permission.
    The promise result is a boolean, indicating whether or not the Background Sync has been enabled successfully.
    For example, if the user did not grant the permission during the said Android Intent, the contract returns `false`.

    Otherwise, `enableBackgroundSync()` returns `true` immediately when no user interaction is required.

    You can also inspect if Background Sync has been enabled through the `isBackgroundSyncEnabled` property.

    <Note>
      When running on iOS, both `enableBackgroundSync()` and `isBackgroundSyncEnabled` always return `true` immediately.
    </Note>

    ```dart theme={null}
    import 'package:vital_health/vital_health.dart' as vital_health;

    final success = await vital_health.enableBackgroundSync();
    log("background sync enabled? ${success}")

    final enabled = await vital_health.isBackgroundSyncEnabled;
    log("background sync enabled? ${enabled}")
    ```
  </Tab>
</Tabs>

### Disabling Background Sync

<Tabs>
  <Tab title="Native Android">
    You can disable Background Sync through the `disableBackgroundSync()` method.

    You can also inspect if Background Sync has been disabled through the `isBackgroundSyncEnabled` property.

    ```kotlin theme={null}
    import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

    val manager: VitalHealthConnectManager = ...
    manager.disableBackgroundSync()

    val enabled = manager.isBackgroundSyncEnabled
    Log.i("VitalBackgroundSync", "Enabled? $enabled")
    ```
  </Tab>

  <Tab title="React Native">
    You can disable Background Sync through the `disableBackgroundSync()` method.

    You can also inspect if Background Sync has been disabled through the `isBackgroundSyncEnabled` property.

    <Note>
      When running on iOS, this API **is a no-op**. HealthKit Background Delivery is always-on
      on iOS.
    </Note>

    ```typescript theme={null}
    import { VitalHealth } from "@tryvital/vital-health-react-native";

    await VitalHealth.disableBackgroundSync();

    const enabled = await VitalHealth.isBackgroundSyncEnabled;
    console.log("background sync enabled?", enabled);
    ```
  </Tab>

  <Tab title="Flutter">
    You can disable Background Sync through the `disableBackgroundSync()` method.

    You can also inspect if Background Sync has been disabled through the `isBackgroundSyncEnabled` property.

    <Note>
      When running on iOS, this API **is a no-op**. HealthKit Background Delivery is always-on
      on iOS.
    </Note>

    ```dart theme={null}
    import 'package:vital_health/vital_health.dart' as vital_health;

    await vital_health.disableBackgroundSync();

    final enabled = await vital_health.isBackgroundSyncEnabled;
    log("background sync enabled? ${enabled}")
    ```
  </Tab>
</Tabs>

### Miscellaneous

When you `signOut()` a user, Junction SDK will automatically disable Background Sync.


# Apple HealthKit
Source: https://docs.junction.com/wearables/guides/apple-healthkit

Integrate Apple HealthKit as an SDK-based provider with Junction, including sync frequency, background delivery, and Mobile SDK setup.

Apple HealthKit is an SDK-based provider. Your iOS consumer app would embed the Junction Mobile SDKs on a supported
stack. Data are then pulled from the Apple HealthKit data store on the user's iOS device.

Refer to the [Mobile SDK Installation](/wearables/sdks/installation) and [Junction Health SDK](/wearables/sdks/health/overview) guides for SDK installation
instructions and general API usage. This guide contains information on the behavior and required configuration
specific to the Apple HealthKit integration.

<Card title="Mobile SDK Installation" icon="helmet-safety" href="/wearables/sdks/installation">
  Learn about the minimum runtime and build-time requirements of Junction Mobile SDKs, and how to add them into
  your project through your package dependency manager.
</Card>

<Card title="Junction Health SDK" icon="heart" href="/wearables/sdks/health/overview">
  Learn about the initial setup required by Junction Health SDK, and the API exposed by Junction Health SDK
  for managing and inspecting health data permissions and automatic data sync.
</Card>

## Sync Frequency

| App state  | Behavior                                                                  |
| ---------- | ------------------------------------------------------------------------- |
| Foreground | Apple HealthKit delivers any buffered and new changes immediately.        |
| Background | Hourly batch delivery of changes, subject to operating system throttling. |

While the SDK schedules hourly data sync with Apple HealthKit, iOS has full discretion to defer the scheduled time
based on factors like CPU usage, battery usage, connectivity, and Low Power Mode.

In other words, the Junction SDK — or any third-party HealthKit apps — cannot guarantee the sync to happen on a strict
schedule. The hourly schedule is advisory and non-binding from the perspective of iOS. The actual delivery frequency
can therefore fluctuate from hourly, to once a day, or when a certain opportunity arises (e.g., when Sleep Mode ends,
or when the phone starts charging).

## Getting Started

To enable this integration, you would need to integrate [Junction Core SDK](/wearables/sdks/vital-core) and
[Junction Health SDK](/wearables/sdks/health/overview) into your Native iOS, React Native or Flutter mobile app.

<Steps>
  <Step title="Add Junction SDKs as dependencies">
    Review the [installation requirements and package installation instructions](/wearables/sdks/installation).
  </Step>

  <Step title="Integrate with Junction Mobile SDK Authentication">
    Follow the [SDK Authentication](/wearables/sdks/authentication) guidance to integrate your app with the authentication
    mechanism of Junction Core SDK.
  </Step>

  <Step title="Configure Health SDK and ask user for read permissions">
    Follow the [Junction Health SDK](/wearables/sdks/health/overview) guidance to
    [configure your App Delegate](/wearables/sdks/health/overview#ios-apps-configure-your-app-delegate),
    [configure the Health SDK](/wearables/sdks/health/overview#configure-the-junction-health-sdk) and to
    [ask for health data read permissions](/wearables/sdks/health/overview#ask-user-for-health-data-permissions) from your user.

    <Note>
      Health data sync is activated only on `VitalResource`s for which you have asked your user for permissions. If you
      did not ask for permissions, data sync would not occur.
    </Note>
  </Step>

  <Step title="Configure Background Delivery">
    Follow the [Apple HealthKit Background Delivery guidance](#setting-up-apple-healthkit-background-delivery) below to
    set up your iOS app target as an eligible target for Apple HealthKit Background Delivery.
  </Step>
</Steps>

## Setting up Apple HealthKit Background Delivery

<Warning>
  You **MUST** [configure your App Delegate](/wearables/sdks/health/overview#ios-apps-configure-your-app-delegate) for
  Apple HealthKit Background Delivery to function as expected.
</Warning>

Junction Health SDK can subscribe to Apple HealthKit Background Delivery, so that data sync can happen automatically in
the background. The subscription persists even when:

1. Your app user does not open your app regularly;
2. Your app user force-quits your app; or
3. The iPhone has been restarted for any reason.

<Note>
  Enabling Apple HealthKit will attract additional scrutiny in the App Store Review process. You should be prepared
  to explain and demonstrate your usage of these health and fitness data.
</Note>

### 1. Set up app entitlements

Enable the "HealthKit > Background Delivery" entitlement:

<img />

Enable the "Background Modes > Background Processing" entitlement:

<img />

### 2. Update your Info.plist

In your "Info > Custom iOS Target Properties" section — also known as the `Info.plist` file — these entries should be
configured:

| Key                                                                                          | Value                                                                                                              |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Privacy - Health Share Usage Description<br />(`NSHealthShareUsageDescription`)              | An explanation of your usage of the user's HealthKit data.                                                         |
| Permitted background task scheduler identifiers<br />(`BGTaskSchedulerPermittedIdentifiers`) | Include `io.tryvital.VitalHealthKit.ProcessingTask` in the array, alongside any other BGTask identifiers of yours. |

If you request write permissions, you must also specify:

| Key                                                                               | Value                                                                           |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Privacy - Health Update Usage Description<br />(`NSHealthUpdateUsageDescription`) | An explanation of what user health data your app is writing to Apple HealthKit. |

### 3. Epilogue

You are all set!

Note that there is no need to call `syncData()` manually at all. Once you have asked the user for permission on
resources, sync would automatically start every app launch, as well as whenever your app is woken up by Apple HealthKit to
respond to newly available data.

<Note>
  As per the [documentation](https://developer.apple.com/documentation/healthkit/hkhealthstore/1614175-enablebackgrounddelivery):

  > HealthKit wakes your app whenever a process saves or deletes samples of the specified type. The system wakes your app at most once per time period defined by the specified frequency. Some sample types have a maximum frequency of `HKUpdateFrequency.hourly`. The system enforces this frequency transparently.
  >
  > For example, on iOS, `stepCount` samples have an hourly maximum frequency.

  This means that although we have background delivery's frequency set to `.hourly`, we cannot guarantee hourly syncing on the dot.
</Note>


# Dexcom (G6 and older)
Source: https://docs.junction.com/wearables/guides/dexcom

Connect legacy Dexcom G6 devices via the password-based integration, which requires users to enable Dexcom Share for data access.

<Note>
  `dexcom` is the provider slug for Dexcom (G6 and older).<br />
  For the OAuth-based `dexcom_v3` integration, see [Dexcom (OAuth based)](./dexcom_v3).
</Note>

<Warning>
  This provider is being phased out in favor of the new [OAuth-based Dexcom](./dexcom_v3) integration. Consider migrating to the new API at your earliest convenience.
</Warning>

## Enabling Dexcom Share

Your user needs to enable [Dexcom Share](https://www.dexcom.com/faqs/how-do-i-share-my-dexcom-g6-glucose-data-followers) for this connection to work:

<Steps>
  <Step title="Install Dexcom mobile app">
    They **must** use the Dexcom G6 mobile app ([iOS](https://apps.apple.com/us/app/dexcom-g6/id1209262925), [Android](https://play.google.com/store/apps/details?id=com.dexcom.g6)).

    Note that the Dexcom Clarity app would not work for this integration.
  </Step>

  <Step title="Enable Dexcom Share">
    Please refer to the [official help article on how to enable Dexcom Share](https://www.dexcom.com/faqs/how-do-i-share-my-dexcom-g6-glucose-data-followers).

    They **must** have at least one follower<sup>\[1]</sup> on Dexcom Share to keep the sharing activated. Otherwise, the API would
    stop reporting data to Junction.

    <Info>
      <sup>\[1]</sup> You can set up your own Dexcom account for your user to add as a follower in the Dexcom G6 mobile app.
      Alternatively, your user can invite `support@junction.com` as a follower.
    </Info>
  </Step>

  <Step title="Connect via Junction Link">
    They **must** connect to Junction using their *own* Dexcom account — the one which their Dexcom G6 app is signed into.
    This should **not** be the account of a follower.
  </Step>
</Steps>

## Junction Widget

To connect your device with the *widget*, follow the link wizard and enter your username and password.

## Junction Link

You can also connect a Dexcom user to Junction using the Junction client. For additional information, please consult the API reference.

<CodeGroup>
  ```python Python theme={null}
  link_token = client.Link.create(user_id)
  client.Link.password_provider(
      link_token, "dexcom", username, password
  )
  ```

  ```javascript JavaScript theme={null}
  const { link_token } = await client.Link.create(user_id);
  await client.Link.password_provider(
      link_token, "dexcom", username, password
  );
  ```
</CodeGroup>

## Webhooks

On successful connection, you will receive a [Connection created](/webhooks/introduction#connection-created-stage) webhook. [Daily webhooks](/webhooks/introduction#incremental-data-stage) `daily.data.glucose.created` and `daily.data.glucose.updated` will also be issued to your team as new or more up-to-date data becomes available during the day.

For this provider, however, Junction will begin to aggregate the data for the last 24 hours and will not go beyond that. As a result, there will be no [Historical webhooks](/webhooks/introduction#historical-data-backfill-stage) issued. If you would like to pull the data immediately after connection, you can use the `user/refresh` endpoint.


# Dexcom (OAuth based)
Source: https://docs.junction.com/wearables/guides/dexcom_v3

Connect Dexcom CGM devices via the OAuth-based dexcom_v3 integration, including webhook behavior and custom OAuth credential setup.

<Note>
  `dexcom_v3` is the provider slug for Dexcom (OAuth based).<br />
  For the password-based `dexcom` integration, see [Dexcom (G6 and older)](./dexcom).
</Note>

<Info>
  Bring Your Own OAuth (BYOO) is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

## Connecting Dexcom to Junction

You can connect your Dexcom device to Junction through the Junction Link Widget or programmatically like any
other [OAuth](/api-reference/link/link-oauth-provider) provider.
We recommend you obtain custom OAuth credentials from [Dexcom](https://developer.dexcom.com) with production access and follow the guide
for [setting up custom credentials](/wearables/connecting-providers/bring-your-own-oauth/overview) to get started.

## Webhooks

On successful connection, you will receive a [Connection created](/event-catalog/provider.connection.created) webhook,
and subsequently, [Daily](/event-catalog/daily.data.glucose.created) and [Historical](/event-catalog/historical.data.glucose.created) Glucose webhooks.

The historical webhook informs you that the last **30 days** of data prior to connection is available and the daily webhooks follow the usual semantics
for all Junction daily webhooks.


# Fitbit
Source: https://docs.junction.com/wearables/guides/fitbit

Understand Fitbit-specific integration details including always-active BMR webhooks, respiratory rate from sleep stages, and time zone handling.

<Warning>
  Google will turn down the legacy Fitbit Web API in **September 2026**, after which it will no longer synchronize Fitbit user data. This retirement applies to the legacy API, not Fitbit devices. Migrate existing connections to the [`google_health` provider](/wearables/guides/google-health#migrate-from-fitbit) before the shutdown to avoid an interruption. See [Google's retirement notice](https://developers.google.com/health/about).
</Warning>

## Webhooks

There is one caveat with Fitbit's webhooks. Namely, Fitbit computes the basal metabolic rate (BMR) server-side. This is a fairly static value because it depends on data such as height, weight, gender, age, etc. The fact that this is a computed value server-side is important to keep in mind, because it means you will always receive `daily.data.activity.updated` webhooks even if you are not wearing the device.

This can be confusing at times, as someone who is not aware of this might wonder why they are getting only this one webhook and not the others.

If you are only getting `daily.data.activity.updated`, the most likely case is that the user is neither wearing the device, nor did they sync their device with the Fitbit app. If you are sure this is not the case, contact us through our support channels.

## Respiratory Rate timeseries

Fitbit does not provide a detailed breakdown beyond an average breaths-per-minute summary on a per sleep stage basis, i.e.,
REM average, Deep average, Light average and the whole session average.

Given this constraint, Junction constructs a respiratory rate timeseries for Fitbit as follows:

* Junction creates a respiratory rate data point for each hypnogram segment reported.
* The data point value will be one of the four average values mentioned, based on the stage represented by the segment.
* The data point will be timestamped to the **midpoint** of the segment.

## Time Zone resolution

Fitbit stores all their data in floating time. Moreover, they do not tag most of their data points or summaries with time zone information.

Since Junction uses [UTC time basis](/wearables/providers/timestamps-and-time-zones), Junction has to convert the floating
time basis to the UTC time basis, before we can ingest the data and forward it onwards to you. To facilitate this
conversion, we must make an educated guess on the user's time zone.

Junction makes the best time zone guess in the following precedence:

<Steps>
  <Step title="Fitbit Profile">
    If the user has granted the Profile scope, Junction uses the time zone offset as provided by their Fitbit Profile.
  </Step>

  <Step title="Most recent Fitbit activity">
    If the user has granted the Activity scope, Junction uses the time zone offset from the most recently recorded activity (if any).
  </Step>

  <Step title="User Fallback Time Zone">
    If neither of the Fitbit data sources is available, Junction would use [User Fallback Time Zone](/wearables/providers/timestamps-and-time-zones#user-fallback-time-zone)
    if it has been set.
  </Step>

  <Step title="Naively assuming UTC">
    If none of the above is available, Junction assumes naively that everything happens in UTC (+00:00).

    <Note>This is usually incorrect, except for users living in an all-year UTC (+00:00) region.</Note>
  </Step>
</Steps>

<Warning>
  When your user refuses to grant both the activity and the profile scope, Fitbit data are more likely to be timestamped incorrectly
  on a UTC time basis due to inaccurate or unavailable time zone information.
</Warning>


# Garmin
Source: https://docs.junction.com/wearables/guides/garmin

Understand Garmin webhook behavior where historical data is delivered incrementally as daily events rather than being available immediately.

## Webhooks & Events

Garmin connections have a non-standard [webhook lifecycle](/webhooks/introduction):

<Steps>
  <Step title="The Connection Created stage">
    You will receive a [Provider Connection Created event](/webhooks/introduction#connection-created-stage) normally.
  </Step>

  <Step title="The Historical Data Backfill stage">
    You would receive a [Historical Pull Completion event](/webhooks/introduction#historical-data-backfill-stage) **immediately** after the connection creation.
    However, if you query Junction API immediately, you would observe **no historical data**.

    <Warning>
      The Historical Pull Completion event for Garmin connections does not indicate availability of historical data through the Junction API.
    </Warning>
  </Step>

  <Step title="The Incremental Data stage">
    All **historical and new data** are delivered incrementally as [Data Events](/webhooks/introduction#incremental-data-stage) (`daily.data.*.*`).
  </Step>
</Steps>

<Note>
  This behavior is exclusive to Garmin, as a downstream effect of Garmin's unique historical data access mechanism for third-party integrations.
</Note>

## Re-running the historical data backfill stage

Normally, when a user with an existing connection re-authenticates with the provider through [Junction Link](/wearables/connecting-providers/introduction), Junction re-runs
the [Historical Data Backfill stage](/webhooks/introduction#historical-data-backfill-stage) automatically.

However, this **does not apply to Garmin** connections — you must first [deregister](/api-reference/user/deregister-a-provider) said user's existing Garmin connection for the re-run to take effect.

<Note>
  This behavior is exclusive to Garmin, as a downstream effect of Garmin's unique historical data access mechanism for third-party integrations.
</Note>

## Junction Widget

To connect your device with the *widget*, follow the link wizard and enter your username and password.

<Frame>
  <img />

  <img />
</Frame>

<br />


# Google Health API
Source: https://docs.junction.com/wearables/guides/google-health

Connect Fitbit and Pixel Watch data through Junction's Google Health integration.

<Info>
  Junction's default Google Health OAuth application is conditionally approved and available while Junction completes the required Cloud Application Security Assessment (CASA). Teams using Junction's default application do not need to complete a separate assessment. Customers using their own OAuth application remain responsible for Google's verification and CASA requirements.
</Info>

Google Health API is the successor to the Fitbit Web API. It returns health and fitness data from Fitbit devices, Pixel Watches, and other sources synchronized to Google Health.

This guide covers the `google_health` provider through Junction's default application or an optional customer-owned application.

## Connect with Junction's default application

Connect users to Google Health through Junction Link in the same way as any other OAuth provider. Junction manages the OAuth application, callback, subscriber, and webhook infrastructure.

You do not need to create a Google Cloud project, configure custom credentials, or complete the setup under [Bring Your Own OAuth](#configure-bring-your-own-oauth). Continue to [Test authorization and data access](#test-authorization-and-data-access) after connecting a user.

## Migrate from Fitbit

<Warning>
  Google will turn down the legacy Fitbit Web API in **September 2026**, after which it will no longer synchronize Fitbit user data. This retirement applies to the legacy API, not Fitbit devices. Migrate existing `fitbit` connections to `google_health` before the shutdown to avoid an interruption. See [Google's retirement notice](https://developers.google.com/health/about) and [migration guide](https://developers.google.com/health/migration).
</Warning>

Google Health uses Google OAuth 2.0, so every existing Fitbit user must authorize a new `google_health` connection. Fitbit access and refresh tokens cannot be transferred, and [Link Bulk Import](/api-reference/link/bulk-import) cannot convert a Fitbit grant into a Google Health grant.

<Steps>
  <Step title="Send new connections to Google Health">
    Offer `google_health` instead of `fitbit` to new users immediately.
  </Step>

  <Step title="Reconnect existing users">
    Reconnect the user to `google_health` with the same Junction user ID, using the Google Account linked to their Fitbit data.
  </Step>

  <Step title="Verify the new connection">
    Keep the existing Fitbit connection active until you receive the Google Health [connection-created event](/event-catalog/provider.connection.created), its historical pull completes, representative summary and timeseries resources contain data, and a new update webhook arrives.
  </Step>

  <Step title="Complete the cutover">
    Update provider filters, priorities, and downstream handling to use `google_health`, then [deregister the Fitbit connection](/api-reference/user/deregister-a-provider). Roll out in cohorts and follow up with users who dismiss or fail the new authorization flow.
  </Step>
</Steps>

<Warning>
  During a dual-connection window, the same underlying Fitbit observations can arrive under both `fitbit` and `google_health`. Junction does not automatically deduplicate data across providers. Keep the overlap short and filter or deduplicate using provider filters and each resource's `source` field in downstream processing.
</Warning>

## Data parity with Fitbit

Google Health covers the primary Junction resources used by Fitbit integrations and adds several resources through its newer API surface.

| Coverage                          | Junction resources                                                                                                                                                                                                                                                 |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Shared coverage                   | Activity, body, sleep, sleep cycle, workouts, meals, profile, devices, heart rate, active calories, distance, steps, VO2 max, water, body temperature, fat, weight, heart rate variability, blood oxygen, respiratory rate, heart-rate alerts, and workout streams |
| Additional Google Health coverage | Floors-climbed timeseries, carbohydrates, caffeine, glucose, electrocardiogram summaries, and electrocardiogram voltage                                                                                                                                            |

<Note>
  This is resource-level parity, not a guarantee of identical fields or data availability. Granted scopes, device capabilities, recorded source data, and the user's synchronization state determine which fields and resources are populated.
</Note>

## Configure Bring Your Own OAuth

<Info>
  Bring Your Own OAuth (BYOO) is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

Complete this setup separately for each Junction environment and region that uses your application.

### Before you begin

You need:

* A Google Cloud project administered by your organization
* A Google account that is a Project Owner for that project
* DNS access for a domain that your organization owns
* Public application homepage, privacy policy, and terms of service pages
* A customer-hosted HTTPS OAuth callback endpoint

Google's [OAuth policies](https://developers.google.com/identity/protocols/oauth2/policies#use-separate-projects-for-testing-and-production) require separate projects for testing and production. Unverified applications are limited to 100 users. While a project remains in **Testing**, only accounts listed under **Google Auth Platform > Audience > Test users** can authorize, and their refresh tokens expire after seven days. See [Set up Google Cloud and OAuth](https://developers.google.com/health/setup).

### Prepare the Junction environment

Open **Config > Custom Credentials** in the Junction Dashboard, select **Google Health**, and copy the preparation values displayed before the credential form.

Alternatively, call [Prepare team custom credentials](/api-reference/org-management/team-custom-credentials/prepare-team-custom-credentials) with:

```json theme={null}
{
  "team_id": "<team-id>",
  "provider": "google_health"
}
```

Record both returned values:

* **Redirect URI:** the Junction callback for the selected environment and region
* **IAM principal:** the Junction service account that must impersonate your service account

<Info>
  For production, the returned Junction redirect URI is the target of your callback forwarder. It is not the redirect URI registered with Google, because your organization cannot verify ownership of a Junction domain.
</Info>

### Configure Google Cloud and OAuth

<Steps>
  <Step title="Create the Google Cloud project">
    Create or select the project, enable **Google Health API**, and record its project ID and numeric project number. Junction requires the **project number**, not the project ID.
  </Step>

  <Step title="Configure branding and policies">
    In **Google Auth Platform > Branding**, configure your application name, logo, support email, developer contacts, homepage, privacy policy, and terms of service.

    The public pages and an in-product disclosure shown immediately before consent must accurately explain how your application accesses, uses, stores, deletes, and shares Google Health data. Describe Junction as a service that receives and processes the data on your behalf. Your homepage or privacy policy must affirm that your use of Google Health information complies with the [Google Health API data policy](https://developers.google.com/health/policies/health-api-developer-user-data-policy), including its Limited Use requirements.
  </Step>

  <Step title="Verify your domain">
    Using the Google Cloud Project Owner account, add the registrable root domain as a **Domain** property in [Google Search Console](https://search.google.com/search-console/), publish Google's DNS TXT record, and verify ownership. Keep the TXT record in DNS, then add the root domain under **Google Auth Platform > Branding > Authorized domains**.

    Every domain used by your homepage, policy pages, JavaScript origins, or redirect URIs must be authorized and verifiable by your organization. Keep the list minimal: Google permits no more than ten authorized domains in a verification request.
  </Step>

  <Step title="Host the OAuth callback">
    Host an HTTPS callback on your verified domain, for example:

    ```text theme={null}
    https://auth.example.com/oauth/callback/google_health
    ```

    The endpoint must accept Google's `GET` request, preserve the complete query string without parsing or re-encoding it, and return `307 Temporary Redirect` to the Junction redirect URI returned by the preparation step.

    ```text theme={null}
    GET /oauth/callback/google_health?code=abc123&state=xyz

    HTTP/1.1 307 Temporary Redirect
    Location: https://api.us.junction.com/v2/link/connect/google_health?code=abc123&state=xyz
    ```

    Use the Junction target for the same environment and region. See [Hosting OAuth Callbacks on Your Own Domain](/wearables/connecting-providers/bring-your-own-oauth/callback-endpoints) for all targets and implementation requirements.
  </Step>

  <Step title="Create the OAuth client">
    Create a **Web application** OAuth client. Google may call this option **Web Server**, including for native products that use Junction on their backend.

    Register your customer-hosted callback as the authorized redirect URI. Do not use Google's `https://www.google.com` quick-start placeholder or the Junction callback directly. Save the OAuth client ID and client secret securely.
  </Step>

  <Step title="Add the scopes">
    In **Google Auth Platform > Data Access**, add every scope requested by your Junction flow. Without custom team scope requirements, Junction requests all of these supported read-only scopes:

    | Junction data                                                   | Google OAuth scope                                                                      |
    | --------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
    | Identity                                                        | `openid`, `email`                                                                       |
    | Activity, workouts, steps, distance, and VO2 max                | `https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly`            |
    | Sleep                                                           | `https://www.googleapis.com/auth/googlehealth.sleep.readonly`                           |
    | Heart rate, body measurements, glucose, oxygen, and temperature | `https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly` |
    | Meals, water, caffeine, and carbohydrates                       | `https://www.googleapis.com/auth/googlehealth.nutrition.readonly`                       |
    | Electrocardiogram                                               | `https://www.googleapis.com/auth/googlehealth.ecg.readonly`                             |
    | Irregular rhythm notifications                                  | `https://www.googleapis.com/auth/googlehealth.irn.readonly`                             |
    | Workout location                                                | `https://www.googleapis.com/auth/googlehealth.location.readonly`                        |
    | Profile                                                         | `https://www.googleapis.com/auth/googlehealth.profile.readonly`                         |
    | Paired devices and settings                                     | `https://www.googleapis.com/auth/googlehealth.settings.readonly`                        |

    Junction currently requests and uses only the read-only scopes listed above, so do not request Google Health write scopes. If your product does not use every resource, configure [team scope requirements](/api-reference/org-management/team-scope-requirements/upsert-team-scope-requirements) before testing so the consent flow requests only the scopes you can justify. Google requires a justification and an in-product demonstration for every restricted scope requested. See the [Google Health scope reference](https://developers.google.com/health/scopes).
  </Step>
</Steps>

### Configure the service account and IAM

Create a service account in the Google Health project. Junction uses it only to manage the Google Health subscriber and user subscriptions; end-user health data still requires user OAuth consent.

Grant the service account **Google Health API Editor** (`roles/health.editor`) on the project. Then grant the Junction IAM principal returned by the preparation step **Service Account Token Creator** (`roles/iam.serviceAccountTokenCreator`) on that service account.

You can configure both grants in Google Cloud Console or with `gcloud`:

```bash theme={null}
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
  --role="roles/health.editor"

gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT_EMAIL \
  --project=PROJECT_ID \
  --member="serviceAccount:JUNCTION_IAM_PRINCIPAL" \
  --role="roles/iam.serviceAccountTokenCreator"
```

Replace the uppercase placeholders with your project ID, service account email, and the exact environment-specific Junction principal. Do not create or upload a service-account JSON key.

### Add the credentials to Junction

In **Config > Custom Credentials > Google Health**, enter:

* OAuth Client ID
* OAuth Client Secret
* Redirect URL Override: your customer-hosted callback URL
* Project Number: the numeric Google Cloud project number
* Service Account Email

The credential form is shared with providers that can use a Junction-owned redirect URI, so **Redirect URL Override** is optional for those providers. Google requires production redirect URI domains to be owned and verified by the OAuth application developer, so set this field to your customer-hosted callback URL for Google Health.

To configure the credentials through the Org Management API, call [Set team custom credentials](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials):

```json theme={null}
{
  "team_id": "<team-id>",
  "provider": "google_health",
  "client_id": "<oauth-client-id>",
  "client_secret": "<oauth-client-secret>",
  "redirect_url_override": "https://auth.example.com/oauth/callback/google_health",
  "details": {
    "project_number": "123456789012",
    "service_account_email": "google-health@project.iam.gserviceaccount.com"
  }
}
```

Saving valid credentials activates the provider immediately. Junction creates and manages the Google Health subscriber, subscription IDs, webhook endpoint, and verification secret. Do not configure separate Google Health webhook infrastructure.

## Test authorization and data access

<Steps>
  <Step title="Prepare a test user">
    If you use a customer-owned application in **Testing**, add the Google account under **Google Auth Platform > Audience > Test users**. Ensure the account has representative Fitbit or Pixel Watch data and has recently synchronized with Google Health.
  </Step>

  <Step title="Complete authorization">
    Use [Junction Link](/wearables/connecting-providers/launching_link) to connect `google_health`. Confirm that the callback returns to Junction, the consent screen contains the expected scopes, and the [connection-created event](/event-catalog/provider.connection.created) is delivered.
  </Step>

  <Step title="Verify ingestion">
    Confirm the connection through [Get user's connected providers](/api-reference/user/get-users-connected-providers), inspect available resources through [user resources](/api-reference/data/introspection/user-resources), and monitor [historical pulls](/api-reference/data/introspection/historical-pulls). Google Health backfills up to 90 days of available data.
  </Step>

  <Step title="Read and update data">
    Read at least one populated resource through the Data Access API, such as [Activity Summary](/api-reference/data/activity/get-summary) or [Steps](/api-reference/data/timeseries/steps). Then synchronize new source data and confirm that Junction delivers the corresponding update webhook.
  </Step>
</Steps>

Common failures are:

| Symptom                         | Check                                                                                                                                                    |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `redirect_uri_mismatch`         | For a customer-owned application, Google, Junction's Redirect URL Override, and the authorization request must use the exact same customer callback URL. |
| User cannot authorize           | For a customer-owned application in Testing, add the account as a test user and confirm the application's publishing status.                             |
| Custom credentials fail to save | Check the project number, service account email, and both IAM grants.                                                                                    |
| A resource is absent            | Confirm that its scope was granted, source data exists, and the historical pull has completed.                                                           |

## Verify a customer-owned application

### Record the verification video

Use the OAuth client intended for production, along with the branding, callback, and application that you will submit. The client can remain in **Testing** while you record with an allowlisted test user. Record the complete customer-facing flow:

1. Start the Google Health connection from your application and complete the end-to-end OAuth grant flow, including Junction Link.
2. Show the same application name and branding as your submission.
3. Set the consent screen language to English, then show the complete screen with every requested scope and the browser address bar containing the submitted OAuth client ID.
4. Return to your application and demonstrate the user-facing functionality enabled by each requested scope.
5. If your application uses multiple OAuth clients, show how it accesses data through each client.

A successful callback or API response alone does not demonstrate how your product uses the data. Use a test account and safe sample data. Do not expose client secrets, API keys, access tokens, or unrelated personal data.

Upload the recording to YouTube Studio with **Unlisted** visibility, then provide the link in Google's verification form. See [Google's restricted-scope verification requirements](https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification) for the current recording requirements.

### Submit the production application

Before submitting, confirm that your production application:

* Uses only the scopes its user-facing features require
* Has verified domains and consistent branding, homepage, privacy policy, and terms URLs
* Provides an accurate in-product disclosure and data deletion path
* Has working reviewer credentials or test instructions
* Uses the production customer-owned callback and Junction credentials

In the Google Cloud OAuth Verification Center, declare every scope, provide a scope-by-scope justification, add relevant product documentation, include reviewer instructions, and provide the unlisted YouTube demonstration-video link.

Google Health scopes are restricted. Because Junction receives and processes this data through its servers, your application must complete an annual third-party security assessment under CASA in addition to OAuth verification. Google Trust and Safety will provide the assessment instructions. Review [Google Health app verification](https://developers.google.com/health/app-verification) and [restricted-scope verification](https://developers.google.com/identity/protocols/oauth2/production-readiness/restricted-scope-verification) before submitting.

Follow the current Verification Center prompts for publishing status. Before public launch, the final application must be verified and **In Production**.


# Google Fit
Source: https://docs.junction.com/wearables/guides/google_fit

Handle Google Fit cloud data sync delays and plan migration to Health Connect as the successor to the deprecated Google Fit SDK.

## Delays in Syncing Data

### Intro

Google Fit via Cloud can have a significant delay in data availability, more specifically between Google's Google Fit apps & Google's cloud data store - neither of which Junction has any control over. This is unlike using the Google Fit Android SDK directly, which can serve the latest local data immediately from the Google Fit data store, regardless of whether said local data have been uploaded to the Cloud already.

Junction isn't certain how/when Google Fit decides to upload new local data to the cloud, and also whether or not there is additional delay caused by Google's internal data ingestion pipelines in the cloud. But one thing is for sure: it cannot be forced, and it seems to be managed by Android's standard WorkManager framework. This means connectivity, battery usage, Doze mode and App Standby will all dynamically affect the actual upload frequency. In the worst case, this can mean uploads only happen when one is sleeping (the typical favorable time for the OS to run background work — the phone is on a charger, has been idle extensively, and probably on WiFi).

### Why not use Google Fit Android SDK directly?

One way to avoid data delay is to use the Google Fit native SDK directly. However, the Google Fit Android API has been deprecated by Google since May 2022 and Google will shut down Google Fit sometime in 2026. For this reason, we advise using its successor: [Health Connect](https://developer.android.com/guide/health-and-fitness/health-connect). You can find our docs [here](/wearables/guides/android-health-connect).

Keep in mind that Health Connect has its own set of quirks. The most notable one is that Google decided to ban any data access when apps are in background. So it is unlikely to have the same smooth background monitoring experience as Apple HealthKit out of the box.


# Omron
Source: https://docs.junction.com/wearables/guides/omron

Connect Omron blood pressure monitors to Junction, noting the requirement that users must grant all permissions during authorization.

## Permissions with Omron

Omron's API does not provide information on what permissions users granted when connecting. Additionally, it gives the same error for unauthorized resources and when a user uses one account in multiple environments, so we can't tell these apart.
Because of this, we only accept Omron connections with all permissions granted.


# Polar
Source: https://docs.junction.com/wearables/guides/polar

Understand Polar integration limitations where historical data backfill is only available for sleep and sleep stream resources.

## Historical backfill with Polar

Polar, unlike most other providers, doesn't offer historical data for most of its resources.
The only resources that do have it are Sleep and Sleep Stream - 28 days each.


# Samsung Health
Source: https://docs.junction.com/wearables/guides/samsung-health

Set up the Samsung Health integration using Junction Mobile SDKs on Android, including project configuration, permissions, data sync, and background sync.

Samsung Health is an SDK-based provider. Your Android consumer app would embed the Junction Mobile SDKs on a supported
stack. Data are then pulled from the Samsung Health data store on the user's Android device.

Refer to the [Mobile SDK Installation](/wearables/sdks/installation) and [Junction Health SDK](/wearables/sdks/health/overview) guides for SDK installation
instructions and general API usage. This guide contains information on the behavior and required configuration
specific to the Samsung Health integration.

<Card title="Mobile SDK Installation" icon="helmet-safety" href="/wearables/sdks/installation">
  Learn about the minimum runtime and build-time requirements of Junction Mobile SDKs, and how to add them into
  your project through your package dependency manager.
</Card>

<Card title="Junction Health SDK" icon="heart" href="/wearables/sdks/health/overview">
  Learn about the initial setup required by Junction Health SDK, and the API exposed by Junction Health SDK
  for managing and inspecting health data permissions and automatic data sync.
</Card>

## Getting Started

To enable this integration, you would need to integrate [Junction Core SDK](/wearables/sdks/vital-core) and
[Junction Health SDK](/wearables/sdks/health/overview) into your Native Android or React Native mobile app.

<Steps>
  <Step title="Add Junction SDKs as dependencies">
    Review the [installation requirements and package installation instructions](/wearables/sdks/installation).
  </Step>

  <Step title="Samsung Developer Program enrollment and project setup">
    Before you can build against Samsung Health, you need a Samsung account, Samsung Developer Program enrollment, and the
    Samsung Health Data SDK AAR. Follow the [Samsung Health project setup](#samsung-health-project-setup) section below.
  </Step>

  <Step title="Integrate with Junction Mobile SDK Authentication">
    Follow the [SDK Authentication](/wearables/sdks/authentication) guidance to integrate your app with the authentication
    mechanism of Junction Core SDK.
  </Step>

  <Step title="Configure Health SDK and ask user for read permissions">
    Follow the [Junction Health SDK](/wearables/sdks/health/overview) guidance to
    [configure the Health SDK](/wearables/sdks/health/overview#configure-the-junction-health-sdk) and to
    [ask for health data read permissions](/wearables/sdks/health/overview#ask-user-for-health-data-permissions) from your user.

    <Note>
      Health data sync is activated only on `VitalResource`s for which you have asked your user for permissions. If you
      did not ask for permissions, data sync would not occur.

      Samsung Health is read-only through Junction Health SDK. Do not request write permissions or use the write APIs with
      `samsung_health`.
    </Note>
  </Step>

  <Step title="[Optional] Customize the Foreground Service notification">
    Follow the [Running as Foreground Service](#running-as-foreground-service) guidance below to
    customize the Foreground Service notification which may be shown by the Android OS during
    any prolonged health data sync attempts.
  </Step>

  <Step title="[Optional] Configure Background Sync">
    Follow the [Background Sync](#background-sync) guidance below to
    configure regular background sync.
  </Step>
</Steps>

## Samsung Health constraints

### Testing internally

❌ You cannot test the integration in an Android emulator, since Samsung Health is not available for emulators.

✅ You can test the integration on an Android device you control through the [Samsung Health Developer Mode](https://developer.samsung.com/health/data/guide/developer-mode.html).

### Going to production

Before shipping Samsung Health support to production, you must have obtained partnership approval through
the [Samsung Health development process](https://developer.samsung.com/health/android/data/guide/process.html).

The permission request flow will abort for your app, until:

1. Your partnership request has been approved by Samsung; AND
2. Their developer support team has added your app signing key signature to the allowlist.

Check the Android app logs and see if you encounter this exception:

```
java.lang.IllegalStateException: Could not get policy for {PACKAGE_NAME}
```

If you do, that means your app signing key has yet to be included in the allowlist. You should get in touch with
[Samsung Developer Support](https://developer.samsung.com/dashboard/business) through your Samsung business account.

## Samsung Health project setup

Samsung distributes the Samsung Health Data SDK as an AAR, and the partnership and support flows are hosted on
Samsung Developer.

Before integrating Samsung Health:

1. Create a Samsung account and sign in on Samsung Developer Dashboard.
2. Enroll in the Samsung Developer Program and review Samsung Health's current onboarding and partnership requirements.
3. Download the Samsung Health Data SDK AAR from the official [Samsung Health Data SDK page](https://developer.samsung.com/health/data).
4. Add the downloaded AAR to your repository, for example at `android/vendor/samsung-health-data-api-1.0.0.aar`.

Then point your app project at the downloaded AAR using one of the setups below.

<Tabs>
  <Tab title="Native Android / Bare RN">
    For Native Android and bare React Native host apps, configure the Android root project so the Vital Samsung Health
    integration can resolve the downloaded AAR.

    Add the AAR path to your Android root `gradle.properties`:

    ```properties android/gradle.properties theme={null}
    samsungHealthAarPath=vendor/samsung-health-data-api-1.0.0.aar
    ```

    Apply the Samsung Health settings plugin in your Android root `settings.gradle`:

    ```groovy android/settings.gradle theme={null}
    pluginManagement {
      repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
      }
    }

    plugins {
      id 'io.tryvital.shealth-settings-plugin' version '5.0.0'
    }
    ```

    Apply the Samsung Health project plugin in your Android root `build.gradle`:

    ```groovy android/build.gradle theme={null}
    plugins {
      id 'io.tryvital.shealth-project-plugin' version '5.0.0' apply false
    }

    apply plugin: 'io.tryvital.shealth-project-plugin'
    ```

    <Note>
      Use the same plugin version as your `io.tryvital:vital-samsung-health` dependency. The `samsungHealthAarPath`
      value is resolved relative to the Android root project.
    </Note>
  </Tab>

  <Tab title="Expo / React Native">
    Use the bundled Expo config plugin and point `samsungHealth.aarPath` at the downloaded AAR:

    ```json app.json/app.config.js theme={null}
    {
      "expo": {
        "plugins": [
          [
            "expo-build-properties",
            {
              "android": {
                "compileSdkVersion": 36,
                "targetSdkVersion": 36,
                "minSdkVersion": 29
              }
            }
          ],
          "@tryvital/vital-core-react-native",
          [
            "@tryvital/vital-health-react-native",
            {
              "samsungHealth": {
                "aarPath": "../vendor/samsung-health-data-api-1.0.0.aar"
              }
            }
          ]
        ]
      }
    }
    ```

    <Note>
      After making changes to the Expo config plugin settings, re-run Expo Prebuild so the native Android project is
      re-generated with the new Samsung Health configuration.
    </Note>
  </Tab>
</Tabs>

## Prepare your app architecture

<Warning>Non-compliance with the guidelines below may result in Samsung Health permission flow failing to launch on specific Android OS versions.</Warning>

<Tabs>
  <Tab title="Native Android">
    When requesting permission using the `ActivityResultContract` created by Junction Health `createPermissionRequestContract()`, you must launch
    the contract using AndroidX Activity Result API.
  </Tab>

  <Tab title="React Native">
    The `MainActivity` of your Android host app project must be augmented with either of the two options below:

    1. Subclassing `VitalHealthReactActivity` provided by the Junction Health SDK `com.vitalhealthreactnative` package, instead of the default `ReactActivity`.

    2. Overriding `onRequestPermissionsResult(...)` so as to manually propagate permission request results to the Junction Health SDK.

    ```kotlin theme={null}
    // MainActivity in your host app project
    open class MainActivity: ReactActivity() {
      override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
      ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        // Propagate permission flow results to Junction Health SDK.
        VitalHealthReactNativeModule.onRequestPermissionsResult(
          this.reactInstanceManager,
          requestCode,
          permissions,
          grantResults
        )
      }
    }
    ```

    The above is a workaround to React Native's AndroidX-incompatible handling of permission request result callbacks,
    which breaks the AndroidX Activity Result API required by Junction Health SDK on Android.
  </Tab>
</Tabs>

## Synchronization

### Sync On App Launch

When your app resumes from background (i.e., having no Activity), Junction Health SDK triggers a data sync on all the resources
to which the user has granted read permission.

The SDK imposes a **2-minute** throttle on automatic data sync. This is to prevent rapid app switching from causing an
excessive amount of data sync work being scheduled.

<Info>
  Junction Health SDK relies on the [AndroidX ProcessLifecycleOwner](https://developer.android.com/reference/androidx/lifecycle/ProcessLifecycleOwner)
  to get notified of your app's resumption.
</Info>

### Running as Foreground Service

<Info>You cannot opt-out of this behavior.</Info>

Samsung Health uses the same Android sync execution model as Health Connect. Junction Health SDK runs all its data sync workers
inside a Foreground Service so longer-running sync work can continue when the user switches away from your app.

Android requires every Foreground Service to [be associated with a user-visible notification](https://developer.android.com/develop/background-work/services/foreground-services#user-dismiss-notification).
The OS typically grants a grace period of about 10 seconds before making the user aware of this notification. In other words,
if the data sync worker completes within the grace period, no notification would be posted.

You can customize it in two ways:

<Tabs>
  <Tab title="Native Android">
    Register your custom `SyncNotificationBuilder` as soon as your app process is created. One way to ensure this is through the AndroidX Startup
    library. You can define an `Initializer` of your own that depends on the SDK `VitalSamsungHealthInitializer`.

    #### An example App Startup Initializer implementation

    ```kotlin theme={null}
    package com.example.app

    import android.content.Context
    import androidx.startup.Initializer
    import io.tryvital.vitalsamsunghealth.VitalSamsungHealthInitializer
    import io.tryvital.vitalsamsunghealth.VitalSamsungHealthManager

    class ExampleSyncNotificationBuilderInitializer: Initializer<Unit> {
        override fun create(context: Context) {
            VitalSamsungHealthManager.setSyncNotificationBuilder(ExampleSyncNotificationBuilder)
        }

        override fun dependencies(): MutableList<Class<out Initializer<*>>> = mutableListOf(
            VitalSamsungHealthInitializer::class.java,
        )
    }
    ```

    #### An example `SyncNotificationBuilder` implementation

    ```kotlin theme={null}
    package io.tryvital.sample

    import android.app.Notification
    import android.app.NotificationChannel
    import android.app.NotificationManager
    import android.content.Context
    import android.content.Context.NOTIFICATION_SERVICE
    import androidx.core.app.NotificationCompat
    import io.tryvital.vitalhealthcore.model.VitalResource
    import io.tryvital.vitalsamsunghealth.SyncNotificationBuilder

    object ExampleSyncNotificationBuilder: SyncNotificationBuilder {
        override fun build(context: Context, resources: Set<VitalResource>): Notification {
            return NotificationCompat.Builder(context, createChannel(context))
                .setContentTitle("Example Sync")
                .setContentText("Syncing your data")
                .setOngoing(true)
                .setSmallIcon(android.R.drawable.ic_popup_sync)
                .build()
        }

        fun createChannel(context: Context): String {
            val importance = NotificationManager.IMPORTANCE_MIN
            val mChannel = NotificationChannel("ExampleSyncNotification", "Example Sync", importance)
            mChannel.description = "Notifies when Example is syncing your data"
            val notificationManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(mChannel)
            return mChannel.id
        }
    }
    ```
  </Tab>

  <Tab title="React Native">
    Register your custom copies through `VitalHealth.setSyncNotificationContent` as early as possible in
    your React Native root component.

    ```typescript theme={null}
    import {
      AndroidHealthProvider,
      VitalHealth,
    } from '@tryvital/vital-health-react-native';

    const provider = AndroidHealthProvider.SamsungHealth;

    await VitalHealth.setSyncNotificationContent(
      {
        notificationTitle: 'Example Sync',
        notificationContent: 'Syncing your data',
        channelName: 'Example Sync',
        channelDescription: 'Notifies when Example is syncing your data',
      },
      provider
    );
    ```
  </Tab>
</Tabs>

## Background Sync

Junction Health SDK supports an **opt-in** Android Background Sync feature. It provides a continuous monitoring
experience with Samsung Health, identical to Android Health Connect, similar to [Background Delivery for Apple HealthKit](/wearables/guides/apple-healthkit#setting-up-apple-healthkit-background-delivery) on iOS.

### Sync Frequency

The [Sync On App Launch](#sync-on-app-launch) behavior is always-on.

When Background Sync is enabled, the SDK additionally schedules hourly sync using the Android [Exact Alarms](https://developer.android.com/develop/background-work/services/alarms/schedule) mechanism.

The OS has full discretion on whether to honor or defer the time as scheduled by the Junction Health SDK. This includes the policies
of the base Android OS, as well as any vendor-specific OS augmentation.

For example, the device may enter [Doze mode](https://developer.android.com/training/monitoring-device-state/doze-standby) during device inactivity. Doze mode
would batch and defer most Exact Alarms and other background work to run at a lower frequency.

<Info>
  Not to be confused with Alarm Clock apps, **Exact Alarm** is the technical name of an Android framework for scheduling
  app wake-ups in background at certain wall clock time. It is the Junction Health SDK being silently "alarmed" in background.

  Your user **will not be alarmed hourly** as a result of enabling Background Sync.

  However, if the data sync took longer than 10 seconds, the OS might notify them of this occurrence with a user-visible notification.
  The notification content is configurable by you — see the ["Running as Foreground Service"](#running-as-foreground-service) section for more details.
</Info>

### Runtime Permission Request

Since Android 12 (API Level 31), scheduling Exact Alarm (`SCHEDULE_EXACT_ALARM`) requires a runtime permission request from the user.
More specifically:

1. The user must go through an interactive flow to grant the *"Alarms & Reminders"* permission.
   * This flow is also reachable through the *"Alarms & Reminders"* section in Android Settings.
2. The user may refuse to grant the *"Alarms & Reminders"* permission.
3. The user may revoke the *"Alarms & Reminders"* permission through Android Settings at any time.

Since Android 13 (API Level 33), a new mutually exclusive `USE_EXACT_ALARM` permission was introduced. This permits your app to
schedule Exact Alarms without interactive permission requests.

<Warning>
  `USE_EXACT_ALARM` attracts scrutiny during Google Play Store review. Per the Google Play [Exact alarm permission policy](https://support.google.com/googleplay/android-developer/answer/9888170?hl=en-GB):

  > USE\_EXACT\_ALARM is a restricted permission and apps must only declare this permission if their core functionality supports
  > the need for an exact alarm. Apps that request this restricted permission are subject to review, and those that do not meet
  > the acceptable use case criteria will be disallowed from publishing on Google Play.
  >
  > *(excerpted on 20 March 2024)*

  If you choose to incorporate `USE_EXACT_ALARM`, you should prepare to justify to Google Play Store:

  1. Why hourly background sync is essential to your product experience; and
  2. Why the interactive permission request required by `SCHEDULE_EXACT_ALARM` is suboptimal for your product experience.
</Warning>

Here is a matrix summarizing the Exact Alarm permission request requirements:

| Android OS           | API Level | Requirements                                                                                                                                                                    |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Android 11 or below  | \<=30     | No interactive permission request.                                                                                                                                              |
| Android 12           | 31, 32    | `SCHEDULE_EXACT_ALARM`: Requires a runtime permission request. Revocable.                                                                                                       |
| Android 13 and above | >=33      | App must declare either:<ul><li>`SCHEDULE_EXACT_ALARM`: Requires runtime permission request. Revocable.</li><li>`USE_EXACT_ALARM`: No interactive permission request.</li></ul> |

### Updating your AndroidManifest.xml

<Note>
  Samsung Health does not use the `android.permission.health.*` manifest declarations required by Health Connect.
  For Junction Health SDK, the manual manifest change required for Samsung Health Background Sync is the Exact Alarm
  permission choice below.
</Note>

Your app's AndroidManifest.xml must include the following `uses-permission` claims:

<Tabs>
  <Tab title="The SCHEDULE_EXACT_ALARM route">
    <Note>
      Make sure you read and understand [Runtime Permission Request](#runtime-permission-request) before moving forward.
      This option is mutually exclusive with `USE_EXACT_ALARM` in the second tab.
    </Note>

    ```xml theme={null}
    <?xml version="1.0" encoding="utf-8"?>
    <manifest ...>

        <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />

    </manifest>
    ```
  </Tab>

  <Tab title="The USE_EXACT_ALARM route">
    <Warning>
      Make sure you read and understand [Runtime Permission Request](#runtime-permission-request) before moving forward,
      especially the Google Play Store review policy surrounding the `USE_EXACT_ALARM` permission.
      This option is mutually exclusive with `SCHEDULE_EXACT_ALARM` in the first tab.
    </Warning>

    ```xml theme={null}
    <?xml version="1.0" encoding="utf-8"?>
    <manifest ...>

        <uses-permission android:name="android.permission.USE_EXACT_ALARM" android:minSdkVersion="33" />

        <!-- Fallback to SCHEDULE_EXACT_ALARM on API Level 31-32 -->
        <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" android:maxSdkVersion="32" />

    </manifest>
    ```
  </Tab>
</Tabs>

### Enabling Background Sync

<Tabs>
  <Tab title="Native Android">
    You can enable Background Sync through `enableBackgroundSyncContract()` (an AndroidX `ActivityResultContract`).

    If the runtime [requires an interactive permission request](#runtime-permission-request), this contract will launch
    an Android Intent to request the *"Alarms & Reminders"* permission. The contract result is a boolean, indicating whether
    or not the Background Sync has been enabled successfully. For example, if the user did not grant the permission during the said Android Intent,
    the contract returns `false`.

    Otherwise, the contract returns `true` synchronously when no user interaction is required.

    You can also inspect if Background Sync has been enabled through the `isBackgroundSyncEnabled` property.

    <CodeGroup>
      ```kotlin Android Compose theme={null}
      import androidx.activity.compose.rememberLauncherForActivityResult
      import androidx.compose.material3.*
      import io.tryvital.vitalsamsunghealth.VitalSamsungHealthManager

      val manager: VitalSamsungHealthManager = ...

      val permissionsLauncher = rememberLauncherForActivityResult(
          manager.enableBackgroundSyncContract()
      ) { success ->
          Log.i("VitalBackgroundSync", "Enabled? $success")

          val enabled = manager.isBackgroundSyncEnabled
          Log.i("VitalBackgroundSync", "Enabled? $enabled")
      }

      Button(onClick = { permissionsLauncher.launch(Unit) }) {
          Text("Enable Background Sync")
      }
      ```

      ```kotlin AndroidX Activity Result API theme={null}
      import androidx.activity.ComponentActivity
      import io.tryvital.vitalsamsunghealth.VitalSamsungHealthManager

      val activity: ComponentActivity = ...
      val manager: VitalSamsungHealthManager = ...
      val contract = manager.enableBackgroundSyncContract()

      activity.registerForActivityResult(contract) { success ->
          Log.i("VitalBackgroundSync", "Enabled? $success")

          val enabled = manager.isBackgroundSyncEnabled
          Log.i("VitalBackgroundSync", "Enabled? $enabled")
      }
      ```
    </CodeGroup>
  </Tab>

  <Tab title="React Native">
    You can enable Background Sync through `enableBackgroundSync(provider)`.

    If the runtime [requires an interactive permission request](#runtime-permission-request),
    `enableBackgroundSync(provider)` will launch an Android Intent to request the *"Alarms & Reminders"* permission.
    The promise result is a boolean, indicating whether or not the Background Sync has been enabled successfully.

    You can also inspect if Background Sync has been enabled through `isBackgroundSyncEnabledForProvider(provider)`.

    ```typescript theme={null}
    import {
      AndroidHealthProvider,
      VitalHealth,
    } from '@tryvital/vital-health-react-native';

    const provider = AndroidHealthProvider.SamsungHealth;

    const success = await VitalHealth.enableBackgroundSync(provider);
    console.log('background sync enabled?', success);

    const enabled = await VitalHealth.isBackgroundSyncEnabledForProvider(provider);
    console.log('background sync enabled?', enabled);
    ```
  </Tab>
</Tabs>

### Disabling Background Sync

<Tabs>
  <Tab title="Native Android">
    You can disable Background Sync through the `disableBackgroundSync()` method.

    You can also inspect if Background Sync has been disabled through the `isBackgroundSyncEnabled` property.

    ```kotlin theme={null}
    import io.tryvital.vitalsamsunghealth.VitalSamsungHealthManager
    import io.tryvital.vitalsamsunghealth.disableBackgroundSync
    import io.tryvital.vitalsamsunghealth.isBackgroundSyncEnabled

    val manager: VitalSamsungHealthManager = ...
    manager.disableBackgroundSync()

    val enabled = manager.isBackgroundSyncEnabled
    Log.i("VitalBackgroundSync", "Enabled? $enabled")
    ```
  </Tab>

  <Tab title="React Native">
    You can disable Background Sync through the `disableBackgroundSync(provider)` method.

    You can also inspect if Background Sync has been disabled through `isBackgroundSyncEnabledForProvider(provider)`.

    ```typescript theme={null}
    import {
      AndroidHealthProvider,
      VitalHealth,
    } from '@tryvital/vital-health-react-native';

    const provider = AndroidHealthProvider.SamsungHealth;

    await VitalHealth.disableBackgroundSync(provider);

    const enabled = await VitalHealth.isBackgroundSyncEnabledForProvider(provider);
    console.log('background sync enabled?', enabled);
    ```
  </Tab>
</Tabs>

### Miscellaneous

When you `signOut()` a user, Junction SDK will automatically disable Background Sync.


# Strava
Source: https://docs.junction.com/wearables/guides/strava

Connect Strava to Junction using Bring Your Own OAuth credentials.

<Info>
  Bring Your Own OAuth (BYOO) is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

## Connecting Strava to Junction

Strava is a [BYOO](/wearables/connecting-providers/bring-your-own-oauth/overview) only provider in Junction.
Your team must configure custom Strava OAuth credentials before users can create new Strava connections or reconnect existing Strava connections.

To connect Strava:

1. Create a Strava API application in your Strava developer account.
2. Use the [Prepare Team Custom Credentials](/api-reference/org-management/team-custom-credentials/prepare-team-custom-credentials) endpoint to get the OAuth setup values for your Junction team.
3. Set the Strava client ID and client secret with [Set Team Custom Credentials](/api-reference/org-management/team-custom-credentials/upsert-team-custom-credentials), or configure them from **Config > Custom Credentials** in the Junction Dashboard.
4. Connect users through Junction Link after the credentials are active.

<Warning>
  Junction's default Strava OAuth application is unavailable for new Strava connections and reconnects. Existing non-BYOO Strava connections may continue syncing while their existing tokens and the default app remain valid.
</Warning>

## Strava app review and policy

You can self-create a Strava API application for testing. To connect more than 1 athlete, you must [submit your app to Strava for review](https://developers.strava.com/docs/getting-started/).

Review the [Strava API Policy](https://www.strava.com/legal/api_policy) for the obligations that apply to your Strava application, including Strava's terms around application purpose, data handling, retention, and subprocessors.

<Note>
  Your organization is responsible for confirming that your Strava application and data use comply with Strava's developer terms.

  Please note that Junction cannot directly assist with or influence Strava's application review process, including expediting reviews or providing contacts within Strava. If you have questions about your application's review status or approval, you'll need to work directly with Strava through their developer support channels.
</Note>


# WHOOP
Source: https://docs.junction.com/wearables/guides/whoop

Connect WHOOP devices to Junction using Bring Your Own OAuth credentials, with a 10-member development limit before WHOOP app approval.

<Info>
  WHOOP [Bring Your Own OAuth](/wearables/connecting-providers/bring-your-own-oauth/overview) is available for [the Launch, Grow and Scale plans](https://tryvital.io/pricing).
</Info>

## Connecting WHOOP to Junction

WHOOP is a [BYOO](/wearables/connecting-providers/bring-your-own-oauth/overview) only provider, so you will need your own app in [WHOOP's developer dashboard](https://developer-dashboard.WHOOP.com/).

Then, you can configure the WHOOP integration in the Junction Dashboard by navigating to the Config > Custom Credentials page and clicking "Setup" next to WHOOP V2.

<img alt="WHOOP V2 config" />

## Limitations

WHOOP sets forth the following limitation:

> Apps can be used for development immediately with a limit of 10 WHOOP members. To launch your app to all WHOOP members, you must first submit your app for approval.

Learn more about app approval in the [WHOOP developer documentation](https://developer.whoop.com/docs/developing/app-approval/). WHOOP reviews apps on a monthly cadence.


# Data Attributions
Source: https://docs.junction.com/wearables/providers/data-attributions

Understand the Source Type taxonomy that identifies how wearable data was collected, such as from a watch, phone, ring, or companion app.

## Source Type

For summary data types, you can identify the Source Type through `source.type`. This is available in both summary data events and API responses.

For timeseries data types:

* You can identify the Source Type through `$.data.source.type` in timeseries data events you received.
* You can request timeseries data grouped by their Source Type through [the Grouped Timeseries Data API](/api-reference/timeseries-grouped/steps).

### Common

| Source Type        | Description                                                                    |
| ------------------ | ------------------------------------------------------------------------------ |
| `unknown`          | The default value. Junction does not know how the provider collects this data. |
| `app`              | The user manually enters this data through an app.                             |
| `multiple_sources` | This data is derived from multiple sources.                                    |

### Wearables

| Source Type   | Description                                           |
| ------------- | ----------------------------------------------------- |
| `watch`       | A smart watch collects this data automatically.       |
| `phone`       | A smart phone collects this data automatically.       |
| `ring`        | A smart ring collects this data automatically.        |
| `chest_strap` | A smart chest strap collects this data automatically. |

### Health devices

| Source Type   | Description                                                        |
| ------------- | ------------------------------------------------------------------ |
| `manual_scan` | A biosensor that needs to be manually scanned.                     |
| `automatic`   | A biosensor that uploads data continuously in background.          |
| `fingerprick` | A glucose testing device which analyzes fingerprick blood samples. |
| `cuff`        | A blood pressure cuff.                                             |

### Supported providers

The following provider integrations would tag data with Source Type:

| Provider        | Source Types                                                |
| --------------- | ----------------------------------------------------------- |
| Apple HealthKit | `phone`, `watch`, `app`, `multiple_sources`, `unknown` \[1] |
| Fitbit          | `watch`, `scale`, `app`, `multiple_sources`                 |
| Oura            | `ring`, `app`, `multiple_sources`                           |
| Garmin          | `watch`, `scale`, `cuff`, `app`, `multiple_sources`         |
| Freestyle Libre | `automatic`, `manual_scan`                                  |

<sup>\[1]</sup> Only data from Apple Watch, iPhone and Apple Health app are tagged. Data from third-party apps
(like the Oura iOS app) are tagged as `unknown` currently.

## Device ID

Device ID indicates the specific device that collected the data, when this information is available with certainty.

For both summary and timeseries data types, it is available at `source.device_id`. When the originating device cannot be determined, this field is `null`.

### Device Attribution Coverage

Device attribution is available when Junction can confidently identify the originating device:

| Provider                          | Coverage                                            | Notes                                                                |
| --------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------- |
| Withings                          | All resources except Body, Body Fat and Body Weight | Regardless of the number of reported devices                         |
| Polar                             | Workout and Sleep resources                         | Regardless of the number of reported devices                         |
| All other providers and resources | All resources                                       | When exactly one device is reported for the user-provider connection |

For connections with multiple devices where the provider doesn't embed device metadata, `source.device_id` remains `null` to avoid incorrect attribution.

### Supported providers

The following provider integrations can provide Device ID attribution:

| Provider                           |
| ---------------------------------- |
| Withings                           |
| Polar                              |
| Fitbit                             |
| Oura                               |
| Garmin                             |
| Renpho                             |
| Omron                              |
| Peloton                            |
| Dexcom v3                          |
| EightSleep                         |
| Freestyle Libre / Abbott LibreView |
| Apple HealthKit                    |
| Android Health Connect             |

## App ID

App ID indicates the origin application of the data.

For summary data types, it is available at `source.app_id`. Note that App ID is unavailable on timeseries data at this time.

### Supported providers

The following provider integrations would tag data with App ID:

| Provider               | Remarks                                                                                   |
| ---------------------- | ----------------------------------------------------------------------------------------- |
| Apple HealthKit        | App Store Bundle ID, or Unique Device ID (`com.apple.health.{UUID}`) for first-party data |
| Android Health Connect | Android Application Package Name                                                          |

## Workout ID

If a Workout ID is present, the data are associated with the referenced Junction [Workout](/api-reference/data/workouts/get-summary) summary.

Workout ID is only available in the following Workout Stream timeseries resources (`workout_*`):

| Resource                  | Remarks                                                |
| ------------------------- | ------------------------------------------------------ |
| `workout_distance`        | All data must have an associated Workout ID and Sport. |
| `workout_swimming_stroke` | All data must have an associated Workout ID and Sport. |

### Supported providers

The following provider integrations would tag data with Workout ID:

| Provider        |
| --------------- |
| Apple HealthKit |

## Sport

If a sport slug is present, the data are known to be measuring the referenced Sport type.

Sport slug is only available in the following timeseries resources:

| Resource                  | Remarks                                                                                                      |
| ------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `workout_distance`        | All data must have an associated Workout ID and Sport.                                                       |
| `workout_swimming_stroke` | All data must have an associated Workout ID and Sport.                                                       |
| `distance`                | All-day wheelchair distance data are tagged with `sport = wheelchair_push`. Otherwise, sport is unspecified. |

### Supported providers

The following provider integrations would tag data with Sport:

| Provider        |
| --------------- |
| Apple HealthKit |


# Data Ingestion Bounds
Source: https://docs.junction.com/wearables/providers/data-ingestion-bounds

Set per-user data ingestion start and end dates to control the historical pull range and automatically pause connections after expiry.

## Overview

By default, Junction:

* pulls historical data as far back as indicated by the [pull preferences](/wearables/providers/historical-data-pull-range#customizing-historical-data-pull-range)
  you specified (if any), or the [default historical pull range](/wearables/providers/historical-data-pull-range).

* accepts **all** new and changed data discovered through polling, incoming provider webhooks and Junction Mobile SDK integrations.

If you need to impose date bounds on the data being collected, you can set **data ingestion bounds** on a per-user basis:

* The data ingestion bounds restrict the historical pull range of any new connections of the user.

* The data ingestion bounds restrict data ingestion from any connections of the user — out-of-bounds data are dropped.

* When the user ingestion end date is reached, a 7-day late delivery catchment period starts. Once the catchment
  period expires, all connections of the user are automatically de-registered and marked as *paused*.

<Note>
  Because *dates* are in floating time, you should expect an error margin of ±1 day on ingestion bound enforcement.
  If you cannot tolerate an error margin, offset the user data ingestion bounds you set accordingly.
</Note>

## Setting Ingestion Start and End Dates

You can specify the user data ingestion bounds (`ingestion_start` and `ingestion_end`) when you:

* [Create a new user](/api-reference/user/create-user); or

* [Update an existing user](/api-reference/user/patch-user)

```jsonc Example theme={null}
{
  "user_id": "409d9870-21fb-443a-8daa-c5222659f40e",
  "team_id": "3aac677c-557f-40b7-9251-0315c1f48e77",
  "client_user_id": "d734e32e-dd43-4b77-ab56-692524279531",
  /** snipped **/
  "ingestion_start": "2024-01-01",
  "ingestion_end": "2024-12-15"
}
```

## Changing Ingestion Bounds

You can change the ingestion bounds at any time.

However, do note that it has no retrospective effect on data already ingested — Junction does not delete collected data
in response to ingestion bound changes.

As long as the connection has not been marked as *paused*, you can change the ingestion end date to extend the ingestion
bounds at any time, even during the (post ingestion end) 7-day late delivery catchment period.

Otherwise, the ingestion end date has no effect on any *paused* cloud-based connections — since these connections have been
deregistered with the data provider, the end user needs to go through the Junction Link flow to re-connect.


# Primary Key
Source: https://docs.junction.com/wearables/providers/data-primary-key

Reference for the primary key structure used to uniquely identify summary and timeseries data records in the Junction data model.

## Summary types

Each summary is uniquely identified by its ID (`$.id`).

Given the same ID, the latest version of a summary you received **replaces** all its previous versions.

## Timeseries types

Each timeseries sample is uniquely identified by a compound key of:

* the timeseries resource type;
* the source provider;
* the [Source Type](/wearables/providers/data-attributions#source-type); and
* the sample timestamp.

Given the same compound key, the latest value of a sample you received **replaces** all the previous values.


# Heart Rate Zones
Source: https://docs.junction.com/wearables/providers/heart-rate-zones

Learn how Junction calculates heart rate zones using percentage brackets of maximum heart rate with configurable fallback birth dates.

## Heart Rate Zones Calculation

Heart rate zones are segments in a workout session. They are based on the percentage brackets of the user's maximum heart rate: under 50%, 50-60%, 60-70%, 70-80%, 80-90%, and above 90%.

The maximum heart rate for a person is calculated with a straightforward formula: `220 - age`.

## Fallback Birth Date

Some providers may not share the user's birth date, making it impossible for Junction to know the user's age. To solve this, users can provide a Fallback Birth Date as an alternative option.

You can specify the Fallback Birth Date when:

* [Creating a new user](/api-reference/user/create-user); or

* [Patching an existing user](/api-reference/user/patch-user)

You will also get information about the source (by slug) and the last updated time of the Fallback Birth Date [when getting an existing user](/api-reference/user/get-user).

Fallback Birth Date supplied via the REST API would always have a source slug of `manual`.

<Info>
  If no birth date information is available, an assumed age of 30 is used in the calculations.
</Info>

```jsonc Example theme={null}
{
  "user_id": "409d9870-21fb-443a-8daa-c5222659f40e",
  "team_id": "3aac677c-557f-40b7-9251-0315c1f48e77",
  "client_user_id": "d734e32e-dd43-4b77-ab56-692524279531",
  /** snipped **/
  "fallback_birth_date": {
    "value": "1980-09-12",
    "source_slug": "manual",
    "updated_at": "2022-09-11T13:45:56+00:00"
  }
}
```


# Historical Data Pull Range
Source: https://docs.junction.com/wearables/providers/historical-data-pull-range

Customize how much historical data Junction fetches when a new provider is connected using Team Data Pull Preferences per provider.

Junction fetches the historical data immediately when the connection is established. The pull range
can be estimated by `[now - days_to_pull, now]`.

| Provider                                                                     | Default  | Configurable | Remarks                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Abbott LibreView](https://www.libreview.com)                                | 90 days  | ❌            | -                                                                                                                                                                                                                             |
| [Fitbit](https://www.fitbit.com/global/uk/home)                              | 90 days  | ⚠️           | Activity and heartrate timeseries data are fixed to 14 days.                                                                                                                                                                  |
| [Garmin](https://www.garmin.com)                                             | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [Google Fit](https://www.google.com/fit/)                                    | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [Oura](https://ouraring.com)                                                 | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [Peloton](https://www.onepeloton.com)                                        | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [Strava](https://www.strava.com)                                             | 14 days  | ✅            | -                                                                                                                                                                                                                             |
| [Wahoo](https://wahoofitness.com)                                            | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [WHOOP](https://www.whoop.com)                                               | 180 days | ✅            | -                                                                                                                                                                                                                             |
| [Zwift](https://zwift.com)                                                   | 270 days | ✅            | -                                                                                                                                                                                                                             |
| [Withings](https://www.withings.com)                                         | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [8Sleep](https://www.eightsleep.com)                                         | 90 days  | ✅            | -                                                                                                                                                                                                                             |
| [Apple HealthKit](https://www.apple.com/uk/ios/health/) (SDK)                | 30 days  | ✅            | -                                                                                                                                                                                                                             |
| [Android Health Connect](https://developer.android.com/health-connect) (SDK) | 30 days  | ❌            | Google restricts access to historical data to [30 days before the first successful permission request](https://developer.android.com/health-and-fitness/guides/health-connect/develop/frequently-asked-questions#time-range). |
| [Hammerhead](https://www.hammerhead.io)                                      | 30 days  | ✅            | -                                                                                                                                                                                                                             |
| [Dexcom](https://www.dexcom.com)                                             | 30 days  | ✅            | -                                                                                                                                                                                                                             |
| [Dexcom (G6 And Older)](https://www.dexcom.com)                              | 1 day    | ❌            | -                                                                                                                                                                                                                             |
| [MyFitnessPal](https://www.myfitnesspal.com)                                 | 14 days  | ✅            | -                                                                                                                                                                                                                             |
| [Polar](https://www.polar.com/accesslink-api/#polar-accesslink-api)          | 28 days  | ❌            | Polar only supports historical backfill for Sleep and Sleep Stream resources.                                                                                                                                                 |
| [Cronometer](https://www.cronometer.com)                                     | 28 days  | ✅            | -                                                                                                                                                                                                                             |

<Note>
  The configurable maximum is 365 days at this time.
</Note>

### Customizing historical data pull range

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

You can configure the historical pull range using the [Team Data Pull Preferences](/api-reference/org-management/team-data-pull-preferences/upsert-team-data-pull-preferences)
feature of the Org Management API.

You can specify a number of days to backfill that uniformly applies to all resources of a provider, as well as overriding the number of days to backfill for specific resources.

<Info>
  The Team Data Pull Preferences you specified are *advisory*. There are scenarios in which Junction systems
  may not adhere strictly to your stated preferences.
</Info>


# Supported Providers
Source: https://docs.junction.com/wearables/providers/introduction

Browse all 300+ supported wearable device providers organized by connection type, with BYOO availability and developer program details.

We currently support 300+ devices. A full list can be seen below.

## Cloud Based Providers

### OAuth Providers

<Info>
  Bring Your Own OAuth (BYOO) is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

These data provider connections would use a **Junction-branded** OAuth application by default.

Optionally, you can *Bring Your Own OAuth* — existing or newly applied API applications — to your Junction Team:

1. The data provider OAuth authentication flow would use your branding; and
2. Connections of your own OAuth application would be rate limited by the data provider independently from Junction's default application.

| Provider (Slug)                                                                                                                                                                                          | Junction default app | Developer Program for BYOO   | Remarks                                                                                                                                                                                                                                                                                          |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Fitbit](https://www.fitbit.com/global/uk/home)  <br />`fitbit`<br />Activity Trackers (all devices)<br /><br />[↗️ Fitbit Guide](/wearables/guides/fitbit)                                              | 🟢<br />Enabled      | 🟢<br />Self Sign-Up         | You can [create your own Fitbit API Application](https://dev.fitbit.com/build/reference/web-api/developer-guide/getting-started/). Note that for access to Intraday (timeseries) data, you must [submit an application to Fitbit](https://dev.fitbit.com/build/reference/web-api/intraday/).     |
| [Google Health API](https://developers.google.com/health) <br />`google_health`<br />Fitbit and Pixel Watch health and fitness data<br /><br />[↗️ Google Health Guide](/wearables/guides/google-health) | 🟢<br />Enabled      | ⚠️<br />Application required | Junction's default application is available. You can also use a customer-owned application, but public production use requires [Google verification](https://developers.google.com/health/app-verification).                                                                                     |
| [Garmin](https://www.garmin.com)                 <br />`garmin`<br />Fitness watches (all devices)<br /><br />[↗️ Garmin Guide](/wearables/guides/garmin)                                                | 🟢<br />Enabled      | ⚠️<br />Application required | You must [apply for Garmin Developer Program](https://developer.garmin.com/gc-developer-program/program-faq/).                                                                                                                                                                                   |
| [Strava](https://www.strava.com)                 <br />`strava`<br />Running & Cycling Social Network<br /><br />[↗️ Strava Guide](/wearables/guides/strava)                                             | ➖<br />Unavailable   | ⚠️<br />Application required | Strava is BYOO-only. You can self-create a Strava API Application for testing. To connect more than 1 athlete, you must [submit your app to Strava for review](https://developers.strava.com/docs/getting-started/) and review the [Strava API Policy](https://www.strava.com/legal/api_policy). |
| [Oura](https://ouraring.com)                     <br />`oura`<br />Smart Sleep tracking ring                                                                                                             | 🟢<br />Enabled      | ⚠️<br />Application required | You can self-create an Oura API Application for testing. To connect more than 10 Oura users, you must [submit your app to Oura for review](https://cloud.ouraring.com/docs/).                                                                                                                    |
| [Wahoo](https://wahoofitness.com)                <br />`wahoo`<br />Cycling Equipment                                                                                                                    | 🟢<br />Enabled      | ⚠️<br />Application required | You must [apply for Wahoo Cloud API access](https://developers.wahooligan.com/cloud).                                                                                                                                                                                                            |
| [Withings](https://www.withings.com)             <br />`withings`<br />Fitness scales, watches and health monitors                                                                                       | 🟡<br />Congested    | 🟢<br />Self Sign-Up         | You can [create your own Withings API Application](https://developer.withings.com/developer-guide/v3/withings-solutions/withings-api-plans).                                                                                                                                                     |
| [Polar](https://www.polar.com/accesslink-api/)   <br />`polar`<br />Finnish sports tech pioneer<br /><br />[↗️ Polar Guide](/wearables/guides/polar)                                                     | 🟢<br />Enabled      | 🟢<br />Self Sign-Up         | You can [create your own Polar API Application](https://www.polar.com/accesslink-api/#introduction).                                                                                                                                                                                             |
| [Cronometer](https://www.cronometer.com)         <br />`cronometer`<br />Nutrition data                                                                                                                  | 🟢<br />Enabled      | ⚠️<br />Application required | You must [apply to be a Cronometer API partner](https://cronometer.com/about/).                                                                                                                                                                                                                  |
| [Ultrahuman](https://www.ultrahuman.com)         <br />`ultrahuman`<br />Real-time nutrition and fitness tracking                                                                                        | 🟢<br />Enabled      | ⚠️<br />Application required | You must [apply to be an Ultrahuman API partner](https://blog.ultrahuman.com/blog/accessing-the-ultrahuman-partnership-api/).                                                                                                                                                                    |
| [Dexcom](https://www.dexcom.com)                 <br />`dexcom_v3`<br />Dexcom CGM Glucose monitors<br /><br />[↗️ Dexcom Guide](/wearables/guides/dexcom_v3)                                            | ➖<br />Unavailable   | ⚠️<br />Application required | You can self-create a Dexcom Sandbox API Application for testing. To connect Dexcom production users, you must [apply for the Dexcom Digital Health Partner status](https://developer.dexcom.com/docs/dexcom/scopes-access).                                                                     |
| [WHOOP](https://www.whoop.com)                   <br />`whoop_v2`<br />Your Personal Digital Fitness and Health Coach<br /><br />[↗️ WHOOP Guide](/wearables/guides/whoop)                               | ➖<br />Unavailable   | ⚠️<br />Application required | You can self-create a WHOOP API Application for testing. To connect more than 10 WHOOP members, you must [submit your app to WHOOP for review](https://developer.whoop.com/docs/developing/app-approval).                                                                                        |
| [MyFitnessPal API](https://www.myfitnesspalapi.com) <br />`my_fitness_pal_v2`<br />Nutrition data                                                                                                        | ➖<br />Unavailable   | ➖<br />Application closed    | N/A                                                                                                                                                                                                                                                                                              |
| [MapMyFitness](https://developer.mapmyfitness.com)         <br />`map_my_fitness`<br />Technology for all athletes                                                                                       | ➖<br />Unavailable   | ➖<br />Application closed    | N/A                                                                                                                                                                                                                                                                                              |

### Non-OAuth Providers

| Provider (Slug)                                                                                                                               | Description                    | Remarks                    |
| --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | -------------------------- |
| [Beurer](https://www.beurer.com/web/gb/)         <br />`beurer_api`                                                                           | Beurer Blood Pressure monitors | Password Auth              |
| [Dexcom (G6 and Older)](https://www.dexcom.com)  <br />`dexcom`<br /><br />[↗️ Dexcom Guide](/wearables/guides/dexcom)                        | Dexcom CGM Glucose monitors    | Password Auth              |
| [8Sleep](https://www.eightsleep.com)             <br />`eight_sleep`                                                                          | Smart Mattress                 | Password Auth              |
| [Abbott LibreView](https://www.libreview.com/)   <br />`abbott_libreview`<br /><br />[↗️ LibreView Guide](/wearables/guides/abbott-libreview) | Abbott CGM Glucose monitor     | Password Auth              |
| [Freestyle Libre](https://www.libreview.com/)    <br />`freestyle_libre`<br /><br />[↗️ LibreView Guide](/wearables/guides/abbott-libreview)  | Abbott CGM Glucose monitor     | Practice-based Connections |
| [Hammerhead](https://www.hammerhead.io)          <br />`hammerhead`                                                                           | Cycling computers              | Password Auth              |
| [Peloton](https://www.onepeloton.com)            <br />`peloton`                                                                              | Popular Indoor Exercise bike   | Password Auth              |
| [Zwift](https://zwift.com)                       <br />`zwift`                                                                                | Virtual cycling and running    | Password Auth              |

### Deprecated providers

| Provider (Slug)                                                         | Status                                                                                      | Availability       | Remarks                                                                        |
| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------ |
| [Google Fit](https://www.google.com/fit/)           <br /> `google_fit` | Google will shut down Google Fit sometime in 2026. Android Health Connect is the successor. | All Junction Teams | OAuth<br />[↗️ Guide](/wearables/guides/google_fit)                            |
| [MyFitnessPal](https://www.myfitnesspal.com)     <br />`my_fitness_pal` | Deprecated                                                                                  | All Junction Teams | MyFitnessPal Diary Share Key<br />[↗️ Guide](/wearables/guides/my_fitness_pal) |

## SDK Based Providers

| Provider (Slug)                                                                               | Description                                                  | Guide                                                |
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------- |
| [Apple HealthKit](https://www.apple.com/uk/ios/health/)              <br />`apple_health_kit` | Health and fitness data on iPhone and Apple Watch            | [↗️ Guide](/wearables/guides/apple-healthkit)        |
| [Android Health Connect](https://developer.android.com/health-connect)<br />`health_connect`  | Health and fitness data on Android devices                   | [↗️ Guide](/wearables/guides/android-health-connect) |
| [Samsung Health](https://developer.samsung.com/health/data)          <br />`samsung_health`   | Health and fitness data on supported Samsung Android devices | [↗️ Guide](/wearables/guides/samsung-health)         |

## SDK Based Devices

| Provider (Slug)                                                                                  | Description                                 | Guide |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------- | ----- |
| [Accu-Chek](https://www.accu-chek.com) (Bluetooth)                   <br />`accuchek_ble`        | Glucose Strips/MySugr App                   |       |
| [Beurer](https://www.beurer.com/web/gb/) (Bluetooth)                 <br />`beurer_ble`          | Beurer Blood Pressure monitors              |       |
| [Contour](https://www.diabetes.ascensia.com) (Bluetooth)             <br />`contour_ble`         | Glucometers                                 |       |
| [Freestyle Libre BLE](https://www.freestylelibre.co.uk/libre/) (NFC) <br />`freestyle_libre_ble` | Abbott CGM Glucose monitor readings via SDK |       |
| [Omron](https://www.omron-healthcare.com) (Bluetooth)                <br />`omron_ble`           | Blood Pressure monitors and scales          |       |

## Providers under beta

**Beta**: Providers under beta — these are providers that have been recently added. All providers here are available in sandbox. Any feedback you have is greatly appreciated!

| Provider                                          | Slug     | Description                        |
| ------------------------------------------------- | -------- | ---------------------------------- |
| [Kardia](https://kardia.com/)                     | `kardia` | Electrocardiogram readings         |
| [Omron](https://www.omron-healthcare.com) (Cloud) | `omron`  | Blood Pressure monitors and scales |

**Planned**: On our roadmap

| Provider                                        | Slug       | Description               | Stage   |
| ----------------------------------------------- | ---------- | ------------------------- | ------- |
| [Xiaomi](https://www.xiaomi.com)                | `xiaomi`   | All data                  | Enquire |
| [Suunto](https://www.suunto.com)                | `suunto`   | Fitness Watch             | Planned |
| [iGlucose](https://smartmeterrpm.com/iglucose/) | `iglucose` | Glucose Strips            | Planned |
| [KetoMojo](https://keto-mojo.com)               | TBD        | Glucose, Ketones and more | Planned |

## Full Device Support List


# Resources
Source: https://docs.junction.com/wearables/providers/resources

Understand the two wearable data categories in Junction: Summaries for daily aggregates and Timeseries for granular time-stamped measurements.

## Overview

There are two broad resource categories provided by Junction:

* A **Summary** type is a summarization of a specific calendar day or a session.
* A **Timeseries** type is a time-ordered collection of data samples.

There is no relation in resource availability between these two categories. Some providers do offer both,
while some providers offer only one category. For example:

* Fitbit provides daily activity summaries, as well as **some** high-frequency activity timeseries data.
* Oura provides only a daily activity summary, without any complementary high-frequency activity timeseries data.

There is also no strict relation between a field in a *Summary* type and a *Timeseries* type.
For example:

* The existence of a `sleep_efficiency` summary field does not imply that there is a corresponding `sleep_efficiency` timeseries.
* The existence of a Glucose Timeseries type does not imply that there is a corresponding `glucose` summary field.
* A *Timeseries* type could derive multiple summary fields, e.g., heart rate statistics and HR zones.

### Summary types

| Granularity | Resources                                                                                                    |
| ----------- | ------------------------------------------------------------------------------------------------------------ |
| Daily       | `activity`                                                                                                   |
| Session     | `sleep`, `sleep_cycle`, `workouts`, `body`, `meal`, `menstrual_cycle`, `electrocardiogram`, `workout_stream` |
| Single      | `profile`                                                                                                    |

### Discrete Timeseries types

Each value is associated with a single point in time.

| Category          | Resources                                                                                    |
| ----------------- | -------------------------------------------------------------------------------------------- |
| Cardiorespiratory | `respiratory_rate`                                                                           |
| Vitals            | `blood_oxygen`, `blood_pressure`, `glucose`, `heartrate`, `hrv`, `electrocardiogram_voltage` |
| Wellness          | `stress_level`                                                                               |
| Body              | `fat`, `weight`                                                                              |

### Interval Timeseries types

Each value is associated with a half-open time interval (`[start, end)`).

| Category          | Resources                                                                                                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Activity          | `calories_active`, `calories_basal`, `distance`, `floors_climbed`, `steps`, `workout_duration`, `fall`, `wheelchair_push`, `stand_duration`, `stand_hour`                |
| Body              | `body_temperature`, `body_temperature_delta`, `insulin_injection`, `waist_circumference`, `body_mass_index`, `lean_body_mass`, `basal_body_temperature`                  |
| Vitals            | `afib_burden`, `heart_rate_alert`, `forced_expiratory_volume_1`, `forced_vital_capacity`, `peak_expiratory_flow_rate`, `inhaler_usage`, `heart_rate_recovery_one_minute` |
| Cardiorespiratory | `vo2_max`                                                                                                                                                                |
| Nutrition         | `carbohydrates`, `caffeine`, `water`                                                                                                                                     |
| Wellness          | `mindfulness_minutes`, `sleep_apnea_alert`, `sleep_breathing_disturbance`, `uv_exposure`, `daylight_exposure`, `handwashing`                                             |
| Workout Stream    | `workout_distance`, `workout_swimming_stroke`                                                                                                                            |
| Diary             | `note`                                                                                                                                                                   |

## Timeseries Data Sampling Rates

Junction records and forwards timeseries data from the data provider, without any upsampling or downsampling applied.
Even within the same data provider, these data may come from different devices or data sources with varying sampling rates.

Because of this, you should not make assumptions about any particular timeseries resource having a fixed sampling rate.

For technical design and capacity planning purposes, you may model after the following three categories of timeseries resources:

### Very High Frequency Timeseries

Second-level peak sampling rate; typically 1000s to low 10000s samples per day.

| Peak rate                   | Example Scenario     |
| --------------------------- | -------------------- |
| 1 sample every 3 seconds    | Apple Watch workouts |
| 1 sample every 15 seconds   | Garmin               |
| 1 sample every 1-15 minutes | Apple Watch, Fitbit  |

Resources that fall into this category:

| Resource          | Events                         | Notable Providers     |
| ----------------- | ------------------------------ | --------------------- |
| `heartrate`       | `daily.data.heartrate.*`       | Apple, Fitbit, Garmin |
| `calories_active` | `daily.data.calories_active.*` | Apple, Fitbit, Garmin |

### High Frequency Timeseries

Minute-level peak sampling rate; typically 100s samples per day.

| Peak rate                 | Example Scenario                                   |
| ------------------------- | -------------------------------------------------- |
| 1 sample every 1 minute   | Fitbit Intraday timeseries data                    |
| 1 sample every 15 minutes | Garmin all-day activity timeseries data ("epochs") |

Resources that fall into this category:

| Resource            | Events                           | Notable Providers     |
| ------------------- | -------------------------------- | --------------------- |
| `steps`             | `daily.data.steps.*`             | Apple, Fitbit, Garmin |
| `calories_basal`    | `daily.data.calories_basal.*`    | Apple                 |
| `distance`          | `daily.data.distance.*`          | Apple, Fitbit, Garmin |
| `glucose`           | `daily.data.glucose.*`           | LibreView, Dexcom     |
| `stress_level`      | `daily.data.stress_level.*`      | Garmin                |
| `hrv`               | `daily.data.hrv.*`               | Garmin, Fitbit        |
| `respiratory_rate`  | `daily.data.respiratory_rate.*`  | Garmin, Fitbit        |
| `blood_oxygen`      | `daily.data.blood_oxygen.*`      | Fitbit                |
| `daylight_exposure` | `daily.data.daylight_exposure.*` | Apple                 |
| `stand_duration`    | `daily.data.stand_duration.*`    | Apple                 |

### Sparse Timeseries

<Info>
  All timeseries resources not specified as (Very) High Frequency fall into this category.
</Info>

Day-level peak sampling rate; typically no more than 5 per day.

| Peak rate                 | Example Scenario                                   |
| ------------------------- | -------------------------------------------------- |
| 1 sample every day        | Apple Watch wrist temperature delta                |
| 1 sample every 15 minutes | Garmin all-day activity timeseries data ("epochs") |
| Unpredictable / Never     | Heart Rate Alerts, Sleep Apnea Alerts              |

## Summary types by providers

## Timeseries types by providers

## Fetching via the API

<Tip>
  Summary resources can be fetched from our `/summary/<resource>/<user_id>` API, while everything else via `/timeseries/<user_id>/<resource>`.
</Tip>

For example, `Workouts` is made up of fields specific to a workout, like calories, distance and duration. When you make a request to `/summary/workouts/<user_id>` you get the following fields:

```json theme={null}
{
  "workouts": [
    {
      "user_id": "a6fea8eb-402f-4578-96ba-189df1a8ca75",
      "user_key": "a6fea8eb-402f-4578-96ba-189df1a8ca75",
      "id": "12398c74-ea41-4d9c-a1b7-8c529ee67e46",
      "title": null,
      "timezone_offset": 3600,
      "average_hr": 147,
      "max_hr": 178,
      "distance": 31557.85,
      "time_start": "2022-07-29T12:05:49+00:00",
      "time_end": "2022-07-29T13:26:08+00:00",
      "calories": 729.0,
      "sport": {
        "id": 108,
        "name": "Road Biking",
        "slug": "road_biking"
      },
      "hr_zones": [
        5,
        76,
        288,
        1467,
        2148,
        835
      ],
      "moving_time": 4819,
      "total_elevation_gain": 374.0,
      "elev_high": null,
      "elev_low": null,
      "average_speed": 6.548,
      "max_speed": 17.448,
      "average_watts": null,
      "device_watts": null,
      "max_watts": null,
      "weighted_average_watts": null,
      "map": null,
      "provider_id": "9297103021",
      "source": {
        "name": "Garmin",
        "slug": "garmin",
        "logo": "https://storage.googleapis.com/vital-assets/garmin.png"
      }
    }
  ]
}
```

`Heartrate` on the other hand, is a timeseries resource. You can fetch it via `timeseries/<user_id>/heartrate` and it looks like this:

```json theme={null}
[
  {
    "timestamp": "2022-04-29T08:35:57+00:00",
    "timezone_offset": 3600,
    "value": 174.0,
    "type": null,
    "unit": "bpm"
  },
  {
    "timestamp": "2022-04-30T11:22:38+00:00",
    "timezone_offset": 3600,
    "value": 79.0,
    "type": null,
    "unit": "bpm"
  },
  {
    "timestamp": "2022-04-30T11:22:39+00:00",
    "timezone_offset": 3600,
    "value": 80.0,
    "type": null,
    "unit": "bpm"
  },
  {
    "timestamp": "2022-04-30T11:22:40+00:00",
    "timezone_offset": 3600,
    "value": 78.0,
    "type": null,
    "unit": "bpm"
  },
  {
    "timestamp": "2022-04-30T11:22:41+00:00",
    "timezone_offset": 3600,
    "value": 78.0,
    "type": null,
    "unit": "bpm"
  }
]
```

Different providers (e.g., Garmin) have different resources available. As an example, Garmin doesn't have `Glucose` as a resource.

## Fields by Provider

Resources alone are not the whole story. Depending on the provider, a field might not be available. For example, both Garmin and Strava provide `Workouts`, yet the former doesn't have `elev_high` or `elev_low`.

<Note>
  Providers marked with `*` mean that Junction calculates the field's value. In some cases, they might come as `null`, since we weren't able to make the calculation.
</Note>

<Warning>
  Even if a field is listed for a provider, it doesn't mean it will always contain data. For example, Fitbit is capable of generating HRV data. However, HRV data is only available for specific high-end devices. Another common scenario is Apple HealthKit data. If the user doesn't give permission to a particular field, it won't be available. **Be mindful of these situations and similar ones**.
</Warning>

### Activity

### Body

### Sleep

### Workout


# Synthetic Data (Sandbox)
Source: https://docs.junction.com/wearables/providers/test_data

Using demo users to build and test your Junction integration.

<Note>
  This feature is currently available for the following providers:

  * Apple HealthKit
  * Fitbit
  * Oura
  * Freestyle Libre

  The others will be coming soon. If there is a particular provider you would like us to add,
  please do not hesitate to contact us at [support@junction.com](mailto:support@junction.com)!
</Note>

When integrating with Junction, you may not own a physical device for each provider you're planning to support. For this, we provide demo users with test data so you can build and test the whole integration without touching a real device.

A demo user gives you the same experience as using a real one. The only difference is that you won't go through the [Junction Link](/wearables/connecting-providers/introduction) flow.

<Warning>
  Demo users have certain limitations to keep in mind:

  * Only available in sandbox.
  * A demo user cannot have connections to a real device. If you already have a user with real data, you need to create a new one to connect a demo account.
  * There is no limitation on the number of demo users you can create, but they do count towards the overall users limit in sandbox. Up-to-date limits can be found [on this page](/api-details/junction-api#environments).
  * Demo users expire after 7 days, meaning the user and its data will be deleted.
</Warning>

## Usage

First, you need a Junction user. You can create one either through the [dashboard](https://app.junction.com) or through the [API](/api-reference/user/create-user).

After creating the user, the `Users` page of your dashboard will look like this:

<img />

Now you can create a demo connection for the Junction user you just created, in this case `150db84c-537c-4cad-a6e9-24dc589d7fa2`. You can directly hit the [API endpoint](/api-reference/link/link-demo-provider) or use one of our client libraries, as shown below:

<CodeGroup>
  ```bash Creating a demo connection theme={null}
  curl --request POST \
       --url {{BASE_URL}}/v2/link/connect/demo \
       --header 'Accept: application/json' \
       --header 'Content-Type: application/json' \
       --header 'x-vital-api-key: <your_api_key>' \
       --data '{"user_id": "<user_id>", "provider": "<provider>"}'
  ```

  ```typescript TypeScript theme={null}
  import { JunctionClient, JunctionEnvironment } from "@junction-api/sdk";

  const client = new JunctionClient({
      apiKey: "YOUR_API_KEY",
      environment: JunctionEnvironment.Sandbox,
  });

  const data = await client.link.connectDemoProvider({
      userId: "<user_id>",
      provider: "fitbit",
  });
  ```

  ```python Python theme={null}
  from junction import DemoProviders, Junction
  from junction.environment import JunctionEnvironment

  client = Junction(
      api_key="YOUR_API_KEY",
      environment=JunctionEnvironment.SANDBOX,
  )

  data = client.link.connect_demo_provider(
      user_id="<user_id>",
      provider=DemoProviders.FITBIT,
  )
  ```

  ```java Java theme={null}
  import com.junction.api.Junction;
  import com.junction.api.core.Environment;
  import com.junction.api.resources.link.requests.DemoConnectionCreationPayload;
  import com.junction.api.types.DemoProviders;

  Junction client = Junction.builder()
      .apiKey("YOUR_API_KEY")
      .environment(Environment.SANDBOX)
      .build();

  var data = client.link().connectDemoProvider(
      DemoConnectionCreationPayload.builder()
          .userId("<user_id>")
          .provider(DemoProviders.FITBIT)
          .build()
  );
  ```

  ```go Go theme={null}
  import (
      "context"

      junction "github.com/junction-api/junction-go"
      "github.com/junction-api/junction-go/client"
      "github.com/junction-api/junction-go/option"
  )

  c := client.NewClient(
      option.WithApiKey("YOUR_API_KEY"),
      option.WithBaseURL(junction.Environments.Sandbox),
  )

  response, err := c.Link.ConnectDemoProvider(context.TODO(), &junction.DemoConnectionCreationPayload{
      UserId:   "<user_id>",
      Provider: junction.DemoProvidersFitbit,
  })
  if err != nil {
      return err
  }
  fmt.Printf("Received data %s\n", response)
  ```
</CodeGroup>

After the demo connection is created, a Fitbit logo will appear beside your user.

<img />

From here, everything works exactly the same as if you connected a real device. This means you will receive the following webhook updates:

* [Connection created](/webhooks/introduction#connection-created-stage), as soon as the connection is created.
* [Historical webhooks](/webhooks/introduction#historical-data-backfill-stage). We simulate historical data for the demo device and send the corresponding webhook updates, as in the real-world scenario.
* [Daily webhooks](/webhooks/introduction#incremental-data-stage). We also simulate updates to the device data every couple of hours so you can test receiving the data when a user recorded a workout or other activity.
* [Refresh user data](/api-reference/user/refresh-user-data). You can also instantaneously refresh a user's data through this endpoint.

## Backfill Data

For demo connections, there will be 30 days of historical data backfilled. This is in contrast with data backfill for non-demo providers,
where the number of days of data backfilled varies according to the provider. You can find out more [here](/wearables/providers/historical-data-pull-range).

## Data Format

When connecting to the live Junction client, there are three data response structures available:
Raw, Stream and Summary.

When connecting to the demo Junction client, data is only available in the Summary structure.

Summary restructures provider data fields to have a consistent response structure across all providers.
This is the easiest way to use provider data across an application which connects to multiple providers.


# Timestamps and Time Zones
Source: https://docs.junction.com/wearables/providers/timestamps-and-time-zones

Learn about Junction timestamp conventions including UTC-based ISO 8601 formatting and floating time handling for specific device types.

## Timestamps

All Junction data timestamps are ISO 8601 formatted as follows:

| Providers                                                 | Time Basis                    | Pattern                                                                                           |
| --------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------- |
| `abbott_libreview`<br />`freestyle_libre` <sup>\[1]</sup> | Floating Time <sup>\[2]</sup> | `YYYY-mm-ddTHH:mm:ss+00:00`<br />The `+00:00` TZ specifier should be **ignored** <sup>\[3]</sup>. |
| *Everyone else*                                           | UTC                           | `YYYY-mm-ddTHH:mm:ss+00:00`<br />Always specified as `+00:00` (UTC).                              |

<Info>
  <sup>\[1]</sup>
  `freestyle_libre` specific:
  Some older teams may see floating time data (Freestyle Libre) without the `+00:00` TZ specifier,
  as part of an earlier iteration of the feature. Contact Junction support if you wish to disable this
  behavior, or if you intend to adopt the Junction backend SDKs.
</Info>

<Info>
  <sup>\[2]</sup> [Floating time](https://www.w3.org/International/wiki/FloatingTime) is not affixed to any time zone.
  This is not the same as time relative to a local time zone, i.e., a known UTC offset or geographical location.
</Info>

<Warning>
  <sup>\[3]</sup> Junction sends timestamps in floating time with `+00:00` for OpenAPI 3.x interoperability. OAS 3
  requires all timestamps to be [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339), which in turn requires
  a TZ specifier to be mandatory.

  When processing data from Freestyle Libre, you should [**reinterpret literally**](#data-point-in-floating-time-abbott-libreview--freestyle-libre)
  the date and time components as local date-time in the desired time zone. Do not convert from UTC/Zulu.
</Warning>

## Time Zones

Junction formats time zones in data as a *nullable* integer offset to UTC:

* A positive offset indicates east of UTC.
* A negative offset indicates west of UTC.
* `null` indicates the time zone information is absent.

Each provider has their own affinity of time zone information:

| Affinity                 | Time Basis    | Time Zone                                                                                                                                            |
| ------------------------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| ✅                        | UTC           | The data comes with time zone information.                                                                                                           |
| User Fallback, or UTC    | UTC           | We assume the data were captured in the [User Fallback Time Zone](#user-fallback-time-zone). We fall back to UTC if the former is not set.           |
| User Fallback, or Absent | UTC           | We assume the data were captured in the [User Fallback Time Zone](#user-fallback-time-zone). Mark as no time zone (`null`) if the former is not set. |
| Inferred, or Absent      | UTC           | We infer the time zone from the data. Mark as no time zone (`null`) if the inference fails.                                                          |
| Absent                   | UTC           | We do not know the exact time zone the recording took place in. Mark as no time zone (`null`).                                                       |
| Floating Time            | Floating Time | We do not know the exact time zone the recording took place in. Mark as no time zone (`null`).                                                       |

### Cloud-based Providers

| Provider                                                                     | Activity Summary (Daily) | Session Summary (e.g., Sleep) | Timeseries Sample   | Remarks                                                        |
| ---------------------------------------------------------------------------- | ------------------------ | ----------------------------- | ------------------- | -------------------------------------------------------------- |
| [Freestyle Libre](https://www.freestylelibre.co.uk/libre/) `freestyle_libre` | *N/A*                    | *N/A*                         | Floating Time       |                                                                |
| [Abbott LibreView](https://www.libreview.com/) `abbott_libreview`            | *N/A*                    | *N/A*                         | Floating Time       |                                                                |
| [Fitbit](https://www.fitbit.com/global/uk/home)          `fitbit`            | ✅                        | ✅                             | ✅                   | ⚠️ [Time Zones](/wearables/guides/fitbit#time-zone-resolution) |
| [Garmin](https://www.garmin.com)                         `garmin`            | ✅                        | ✅                             | ✅                   |                                                                |
| [Google Fit](https://www.google.com/fit/)                `google_fit`        | User Fallback, or UTC    | User Fallback, or Absent      | Absent              |                                                                |
| [Oura](https://ouraring.com)                             `oura`              | ✅                        | ✅                             | ✅                   |                                                                |
| [Peloton](https://www.onepeloton.com)                    `peloton`           | *N/A*                    | ✅                             | *N/A*               |                                                                |
| [Strava](https://www.strava.com)                         `strava`            | *N/A*                    | ✅                             | ✅                   |                                                                |
| [Wahoo](https://wahoofitness.com)                        `wahoo`             | *N/A*                    | Absent                        | *N/A*               |                                                                |
| [WHOOP](https://www.whoop.com)                           `whoop_v2`          | ✅                        | ✅                             | *N/A*               |                                                                |
| [Zwift](https://zwift.com)                               `zwift`             | *N/A*                    | User Fallback, or Absent      | *N/A*               |                                                                |
| [Withings](https://www.withings.com)                     `withings`          | ✅                        | ✅                             | ✅                   |                                                                |
| [8Sleep](https://www.eightsleep.com)                     `eight_sleep`       | *N/A*                    | ✅                             | ✅                   |                                                                |
| [Hammerhead](https://www.hammerhead.io)                  `hammerhead`        | *N/A*                    | Inferred, or Absent           | Inferred, or Absent |                                                                |
| [Dexcom](https://www.dexcom.com)                         `dexcom_v3`         | *N/A*                    | *N/A*                         | ✅                   |                                                                |
| [Polar](https://www.polar.com)                           `polar`             | ✅                        | ✅                             | ✅                   |                                                                |
| [Kardia](https://kardia.com/)                            `kardia`            | *N/A*                    | *N/A*                         | ✅                   |                                                                |
| [Omron](https://www.omron-healthcare.com)                `omron`             | ✅                        | *N/A*                         | ✅                   |                                                                |

### SDK-based Providers

<Info>
  On-device data often do not capture the time zone at recording time.
  Junction SDK uses the device's current time zone as the closest approximation.
</Info>

| Provider                                                                                            | Activity Summary (Daily) | Session Summary | Timeseries Samples |
| --------------------------------------------------------------------------------------------------- | ------------------------ | --------------- | ------------------ |
| [Apple HealthKit](https://www.apple.com/uk/ios/health/)                       `apple_health_kit`    | ✅                        | ✅               | ✅                  |
| [Android Health Connect](https://developer.android.com/health-connect)        `health_connect`      | ✅                        | ✅               | ✅                  |
| [Omron](https://www.omron-healthcare.com)                                     `omron_ble`           | *N/A*                    | *N/A*           | ✅                  |
| [Contour](https://www.diabetes.ascensia.com)                                  `contour_ble`         | *N/A*                    | *N/A*           | ✅                  |
| [Accu-Chek](https://www.accu-chek.com)                                        `accuchek_ble`        | *N/A*                    | *N/A*           | ✅                  |
| [Freestyle Libre BLE](https://www.freestylelibre.co.uk/libre/)                `freestyle_libre_ble` | *N/A*                    | *N/A*           | ✅                  |

## Examples

### Data point with time zone offset

The data point was recorded on July 7, 2023 at 1:00 PM in UTC-07:00.

```json theme={null}
{
  "source": {
    "provider": "apple_health_kit",
  },
  "data": [
    {
      "timestamp": "2023-07-07T20:00:00+00:00",
      "timezone_offset": -25200
    }
  ]
}
```

### Data point with no time zone offset (`null`)

The data point was timestamped to Aug 13, 2023 at 8:12 AM in UTC+00:00 (Zulu).
We do not know the exact time zone the recording took place in.

```json theme={null}
{
  "source": {
    "provider": "apple_health_kit",
  },
  "data": [
    {
      "timestamp": "2023-08-13T08:12:00+00:00",
      "timezone_offset": null
    }
  ]
}
```

### Data point in floating time (Abbott LibreView & Freestyle Libre)

This Freestyle Libre data point was recorded on Sep 27, 2023 at 7:48 AM in floating time
(whichever time zone the user was in). We do not know the exact time zone the recording took place in.

You can display the date and time components literally to the user, e.g., `2023/09/27 07:48 AM`, as they
are relative to their perception of local time.

If you need to convert it to UTC for persistence, you need to pick a time zone based on your understanding
of the user. Then you should reinterpret the date and time components **literally** in said time zone.

For example, we knew that this example user is based in `America/New_York`. So we would interpret this data
point to be in `2023/09/27 07:48 AM ET`. We then finally convert this to UTC, resulting in `2023/09/27 11:48 AM UTC`.

```json theme={null}
{
  "source": {
    "provider": "freestyle_libre",
  },
  "data": [
    {
      "timestamp": "2023-09-27T07:48:00+00:00",
      "timezone_offset": null
    }
  ]
}
```

## User Fallback Time Zone

Some providers neither expose nor even capture time zone information at source. So Junction can only request data and interpret them strictly in UTC.

If you prefer the data to be contextualized to the geographical location of a user, a Fallback Time Zone (denoted by an [IANA tz database identifier](https://data.iana.org/time-zones/tz-link.html))
can be specified on a per-user basis. Once specified, Junction would use the time zone to pull data and interpret timestamps from any time-zone-unaware providers from that point onwards.

You can specify the Fallback Time Zone when:

* [Creating a new user](/api-reference/user/create-user); or
* [Patching an existing user](/api-reference/user/patch-user)

You will also get information about the source (by slug) and the last updated time of the Fallback Time Zone [when getting an existing user](/api-reference/user/get-user). Fallback Time Zone manually supplied via the REST API would always have a source slug of `manual`.

```jsonc Example theme={null}
{
  "user_id": "409d9870-21fb-443a-8daa-c5222659f40e",
  "team_id": "3aac677c-557f-40b7-9251-0315c1f48e77",
  "client_user_id": "d734e32e-dd43-4b77-ab56-692524279531",
  /** snipped **/
  "fallback_time_zone": {
    "id": "Europe/London",
    "source_slug": "manual",
    "updated_at": "2022-09-11T13:45:56+00:00"
  }
}
```


# Update Frequency
Source: https://docs.junction.com/wearables/providers/update-frequency

Understand how Junction discovers new wearable data through push, polling, and hybrid update models across cloud-based providers.

Whenever Junction detects that new data is available, [data events](/event-catalog) are always sent to your configured endpoints,
regardless of the provider being push-based or polling-based.

## Cloud Based Providers

| Primary Model | Providers                                                                                                                                                                                                                                                                                                     |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Hybrid        | [Fitbit](https://www.fitbit.com/global/uk/home), [Withings](https://www.withings.com), [Oura](https://ouraring.com)                                                                                                                                                                                           |
| Push          | [Garmin](https://www.garmin.com), [Strava](https://www.strava.com), [WHOOP](https://www.whoop.com), [Cronometer](https://www.cronometer.com)                                                                                                                                                                  |
| Polling       | [Wahoo](https://wahoofitness.com), [LibreView](https://www.libreview.com/), [Google Fit](https://www.google.com/fit/), [Peloton](https://www.onepeloton.com), [Zwift](https://zwift.com), [EightSleep](https://www.eightsleep.com), [Hammerhead](https://www.hammerhead.io), [Dexcom](https://www.dexcom.com) |

Cloud Based Providers can operate in the following three models:

<AccordionGroup>
  <Accordion title="Push">
    In the Push model, the data provider proactively notifies Junction of any resource changes.

    Junction goes and fetches the notified resources, compares them against what we have already ingested, and sends out the differences
    (new or updated entities) to your Webhook endpoints or ETL Pipeline destinations.

    While resources using the Push model typically have lower latency than polling, data providers may impose throttling, debouncing
    or batching semantics on these push notifications to Junction on their end.
  </Accordion>

  <Accordion title="Polling">
    Junction schedules a recurring job to query the data provider API resources:

    * For most resources, the schedule is every 15 minutes or so.
    * Some resources may use a sparser schedule due to data provider rate limits.

    Junction then compares the reported data against what we have already ingested, and sends out the differences
    (new or updated entities) to your Webhook endpoints or ETL Pipeline destinations.
  </Accordion>

  <Accordion title="Hybrid">
    Some data providers support the Push model only on a subset of API resources.

    Junction prefers to use the Push model whenever available, but otherwise falls back to the Polling model.
  </Accordion>
</AccordionGroup>

## SDK Based Providers

SDK Based Providers are all push-based, where data are pushed from the Junction SDK embedded inside your Android or iOS app.

| Provider                                                               | Description                                                                                                          |
| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [Apple HealthKit](https://www.apple.com/uk/ios/health/)                | <ul><li>Auto sync on app launch and in foreground</li><li>Hourly Background Sync, subject to OS throttling</li></ul> |
| [Android Health Connect](https://developer.android.com/health-connect) | <ul><li>Sync On App Launch</li><li>Opt-in Hourly Background Sync, subject to OS throttling</li></ul>                 |
| [Samsung Health](https://developer.samsung.com/health/data)            | <ul><li>Sync On App Launch</li><li>Opt-in Hourly Background Sync, subject to OS throttling</li></ul>                 |
| [Omron](https://www.omron-healthcare.com)                              | Manual Post                                                                                                          |
| [Contour](https://www.diabetes.ascensia.com)                           | Manual Post                                                                                                          |
| [Accu-Chek](https://www.accu-chek.com)                                 | Manual Post                                                                                                          |

For more information on Apple HealthKit, Android Health Connect and Samsung Health, please refer to the specific guides:

* [Junction Health SDK: Automatic Data Sync](/wearables/sdks/health/overview#automatic-data-sync)
* [Apple HealthKit: Sync Frequency](/wearables/guides/apple-healthkit#sync-frequency)
* [Android Health Connect: Synchronization](/wearables/guides/android-health-connect#synchronization)
* [Samsung Health: Background Sync](/wearables/guides/samsung-health#background-sync)


# Mobile SDK Authentication
Source: https://docs.junction.com/wearables/sdks/authentication

Authenticate Junction Mobile SDKs using Sign-In Tokens that give each app installation scoped API access as an individual user.

## Junction Sign-In Token

In the *Junction Sign-In Token* scheme, each mobile app installation signs in with Junction Mobile SDK
as an individual user in your Team. The app installation would be granted only Junction API access
scoped to that specific user.

Instead of hardcoding a [Junction Team API Key](#junction-team-api-keys), your app would request your own backend to generate
a **Junction Sign-In Token** for the user, through your own API authentication and authorization systems.

In this model, your Junction Team API Keys are kept strictly as a server-side secret.

<CardGroup>
  <Card title="Persistence" icon="hand-wave">
    The signed-in user is persistent across app relaunches and device reboots.
  </Card>

  <Card title="Access Scope" icon="lock">
    Only user-level resources of the signed-in user can be accessed.
  </Card>
</CardGroup>

### Overview

<AccordionGroup>
  <Accordion title="Sign in once" icon="right-to-bracket">
    In typical circumstances, within each app installation, you would sign in your authenticated user only once
    with the Junction Mobile SDK. You would do so as part of your app's user lifecycle.

    There are some known circumstances where you would have to sign in the user again:

    * You have signed out your user in response to your app's user lifecycle changes; or
    * The Junction SDK sign-in state is out of sync with your app's user lifecycle; or
    * The SDK automatically signed out, because it had detected the deletion of the Junction User.

    <Note>
      It is unnecessary to request and sign in with the Junction Sign-In Token every time your app launches.
    </Note>
  </Accordion>

  <Accordion title="Integrating into your app's user lifecycle" icon="arrows-spin">
    You should sign in and sign out of the Junction Mobile SDK as an integral part of your app's user lifecycle:

    1. When the user signs in to your app, you would request the Junction Sign-In Token from your backend, and then sign in with the Junction Mobile SDK.

    2. When the user signs out from your app, you would also sign out from the Junction Mobile SDK.

    Junction Mobile SDK persists the signed-in Junction User — as well as any settings and sync state — across app relaunches and device reboots.
  </Accordion>

  <Accordion title="A recommendation to perform regular reconciliation" icon="merge">
    We recommend regularly reconciling the state of the Junction Mobile SDK and your app's user lifecycle. This is because:

    1. The SDK sign-in process involves remote API calls, which can fail due to Internet connectivity;

    2. Unknown edge cases in your integration of Junction Core SDK into your app's user lifecycle may result in
       the state being out of sync; and

    3. If you are integrating Junction Mobile SDK into an existing production app, this reconciliation can serve
       as a one-off migration for app installations upgrading from an older version of your app.

    You can achieve this through the [Core SDK](/wearables/sdks/vital-core#core-sdk-status) API in three steps:

    1. Query the [Core SDK](/wearables/sdks/vital-core#core-sdk-status) Status and the Current User ID;
    2. Compare these against your app's user state; and
    3. Only if a discrepancy is detected, perform a Junction SDK sign-in or sign-out in accordance with the discrepancy.

    You would typically schedule this as an asynchronous task that spawns when your application process launches.

    <Warning>
      Do not sign in and sign out every time your application process launches.
    </Warning>
  </Accordion>

  <Accordion title="Technical details" icon="circle-info">
    **Junction Sign-In Token** is a short-lived bearer token issued for a specific Junction User.
    Junction Mobile SDK uses it to sign in with Junction's Identity Provider as the Junction User represented by the
    token.

    Each app installation can only have one active signed-in Junction User.

    Internally, the Junction Mobile SDK:

    1. **exchanges** the short-lived Junction Sign-In Token for a permanent sign-in, which is in the form of
       an OAuth 2.0 access token and an OAuth 2.0 refresh token.
    2. discards the Junction Sign-In Token upon a successful exchange.
    3. stores these token secrets securely in the device persistent storage.
    4. calls the Junction API using the OAuth 2.0 access token.
    5. transparently manages the OAuth 2.0 refresh token flow to keep the access token fresh and valid.

    This is why the documentation emphasizes the "sign in once" expectation. The SDK does not rely on
    the Junction Sign-In Token for day-to-day operation and API calls. It only needs this artifact once upfront
    to exchange for the token secrets it needs.
  </Accordion>
</AccordionGroup>

### Case Study: A typical Backend - Mobile App flow

<Steps>
  <Step title="Your app -> Your identity provider">
    ```mermaid theme={null}
    sequenceDiagram
        participant Your App
        actor Your Identity Provider
        autonumber
        Your App->>Your Identity Provider: User authentication
        Your Identity Provider-->>Your App: Token Secret for authenticating to your Backend API
    ```

    Your end user signs in with your Identity Provider — a separate IdP, or your own API acting as one — through password authentication,
    Social Login, Single Sign-On (SSO) or any other authentication methods.
  </Step>

  <Step title="Your app -> Your backend API">
    ```mermaid theme={null}
    sequenceDiagram
        participant Your App
        actor Your Backend
        autonumber
        Your App->>Your Backend: Request the user profile
        Your Backend-->>Your App: User profile
        Your App->>Your Backend: Request a Junction Sign-In Token for the user
    ```

    Once your user has successfully authenticated, your app is issued with token secrets for accessing your backend API.
    Your app stores these token secrets securely.

    It then makes some API requests to your backend API, in order to gather all information required to set up your app
    into an authenticated state.

    Among these API requests, one would be requesting your backend API to issue a Junction Sign-In Token for your authenticated user.
  </Step>

  <Step title="Your backend service <-> Junction API">
    ```mermaid theme={null}
    sequenceDiagram
        actor Your Backend
        actor Junction API
        autonumber
        Your Backend->>Junction API: Token Request via API Key
        Junction API-->>Your Backend: Issued a Junction Sign-In Token
    ```

    Your backend API receives and validates the token issuance request. Once validated, your backend service:

    * Looks up the Junction User associated with this authenticated user;

    * If one does not yet exist, call the [Create User](/api-reference/user/create-user) endpoint to create a Junction User; and

    * Call the [Create Sign-In Token](/api-reference/user/create-sign-in-token) endpoint to obtain a Junction Sign-In Token for
      this Junction User.

    <Note>
      Only this step would use a [Junction Team API Key](/api-details/junction-api#authentication). Because this step happens in your
      backend service, this enables you to keep the Junction Team API Key strictly as a server-side secret.
    </Note>

    ```bash theme={null}
    curl --request POST
      --url '{{BASE_URL}}/v2/user/{{USER_ID}}/sign_in_token'
      --header 'X-Vital-API-Key: <YOUR-API-KEY>'
    ```

    Your backend API then responds to your app with the obtained Junction Sign-In Token:

    ```json theme={null}
    {
      "user_id": "e209947b-323e-4108-8e18-1518b80da0bd",
      "sign_in_token": "XYZ=="
    }
    ```
  </Step>

  <Step title="Your backend API -> Your app">
    ```mermaid theme={null}
    sequenceDiagram
        participant Your App
        participant Junction Mobile SDK
        actor Your Backend
        autonumber
        Your Backend-->>Your App: The Junction Sign-In Token
        Your App->>Junction Mobile SDK: Sign in with Junction Sign-In Token
    ```

    Your app receives a response from your backend API, which includes the Junction Sign-In Token for the authenticated
    user.

    Your app then calls the Junction Mobile SDK `signIn` method with the Junction Sign-In Token:

    <CodeGroup>
      ```swift Native iOS theme={null}
      import VitalCore

      let response: MyAPIResponse = // ....

      do {
        try await VitalClient.signIn(withRawToken: response.signInToken)

        print("Signed in with Junction successfully")
      } catch let error {
        print("Error signing in with Junction", error)
      }
      ```

      ```kotlin Native Android theme={null}
      import io.tryvital.client.VitalClient
      import android.content.Context
      import android.util.Log

      val response: MyAPIResponse
      val applicationContext: Context

      try {
        VitalClient.signIn(applicationContext, response.signInToken)

        Log.i("vital_sdk", "Signed in with Junction successfully")
      } catch (e: Throwable) {
        Log.e("vital_sdk", "Error signing in with Junction", e)
      }
      ```

      ```typescript React Native theme={null}
      import { VitalCore } from "@tryvital/vital-core-react-native";

      const response: MyAPIResponse

      try {
        await VitalCore.signIn(response.signInToken)

        console.log("Signed in with Junction successfully")
      } catch (error) {
        console.error("Error signing in with Junction", error)
      }
      ```

      ```dart Flutter theme={null}
      import 'package:vital_core/vital_core.dart' as vital_core;

      MyAPIResponse response;

      try {
        await vital_core.signIn(response.signInToken)

        Fimber.i("Signed in with Junction successfully")
      } catch (error) {
        Fimber.e("Error signing in with Junction", error)
      }
      ```
    </CodeGroup>

    <br />

    <Warning>
      `signIn()` throws an error when you have already signed in with the Junction SDK.

      You can [inspect the Core SDK status](/wearables/sdks/vital-core#core-sdk-status) at any time for troubleshooting. You can
      also rely on the Core SDK status to reconcile differences between your own user session state and Junction SDK sign-in.
    </Warning>
  </Step>

  <Step title="Voilà!">
    The Junction Mobile SDK is now signed-in with the Junction User associated with your authenticated user.
    You can now proceed to use all the Junction SDK features.
  </Step>
</Steps>

## Junction Team API Keys

<Warning>
  API Key is **discouraged** for production mobile apps, since it would be distributed as cleartext.
  API Key support is intended only for customer early evaluation in Sandbox.

  Use [Junction Sign-In Token](#junction-sign-in-token) whenever possible.
</Warning>

Junction Mobile SDK can be configured to authenticate using API Key alongside a target user ID.

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalCore

  VitalClient.configure(
    apiKey: "sk_us_...",
    environment: .sandbox(.us)
  )
  VitalClient.setUserId(UUID(uuidString: "ccd6f98d-3a2a-433b-a114-8fe064f301ed")!)
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.VitalClient
  import android.content.Context

  val applicationContext: Context

  VitalClient.configure(
    applicationContext,
    Region.US,
    Environment.Sandbox,
    "sk_us_..."
  )
  VitalClient.setUserId(applicationContext, "ccd6f98d-3a2a-433b-a114-8fe064f301ed")
  ```

  ```typescript React Native theme={null}
  import { VitalCore } from "@tryvital/vital-core-react-native";

  await VitalCore.configure(
      "sk_us_...",
      "sandbox",
      "us",
      true,
  );
  await VitalCore.setUserId("ccd6f98d-3a2a-433b-a114-8fe064f301ed");
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  await vital_core.configure(
    "sk_us_...",
    vital_core.Environment.sandbox,
    vital_core.Region.us
  );
  await vital_core.setUserId("ccd6f98d-3a2a-433b-a114-8fe064f301ed");
  ```
</CodeGroup>

<br />

<Info>
  When running on iOS, the SDK must be **explicitly** configured before other SDK methods can be invoked.

  Alternatively, follow the [Apple HealthKit guide](/wearables/guides/apple-healthkit#setting-up-apple-healthkit-background-delivery)
  to enable SDK Automatic Configuration during app launch. This auto-configures the SDK using the last known configuration you supplied — including but
  not limited to the API Key, the environment and the target User ID. Not only is this a prerequisite to enable HealthKit Background Delivery, this
  allows you also to only call `configure(...)` and `setUserId(...)` once to "sign-in" the user persistently when using the Junction Team API Key scheme.
</Info>

## Sign Out

Regardless of the authentication scheme you used, you can sign out with the Junction SDK using `signOut()`.
This erases any persistent user session and configuration stored by the Junction Core and Health SDKs.

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalCore

  await VitalClient.shared.signOut()
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.VitalClient
  import android.content.Context

  val applicationContext: Context

  VitalClient.getOrCreate(applicationContext).signOut()
  ```

  ```typescript React Native theme={null}
  import { VitalCore } from "@tryvital/vital-core-react-native";

  await VitalCore.signOut();
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  await vital_core.signOut();
  ```
</CodeGroup>

## Migrate from Junction Team API Keys to Junction Sign-In Tokens

<Warning>
  Always use [Junction Sign-In Token](#junction-sign-in-token) for your production mobile apps.
</Warning>

An existing app installation signed-in with Junction Team API Key + User ID can be seamlessly migrated to
use Junction Sign-In Tokens.

It is as simple as performing a one-off migration logic during app launch:

<Steps>
  <Step title="Condition to migrate">
    Check whether the Junction SDK status includes `useApiKey` (i.e., the user is signed in using Junction Team API Key).
  </Step>

  <Step title="Obtain a Junction Sign-In Token">
    Similar to the new user sign-in flow, your app needs to obtain a Junction Sign-In Token through your backend service.
  </Step>

  <Step title="Sign-in with the token">
    Your app can simply sign-in with the Junction Sign-In Token.

    Note that it is **unnecessary to reset** the SDK beforehand — the SDK `signIn` method would automatically migrate, as
    long as the supplied Sign-In Token is compatible with the existing API Key sign-in (i.e., having the same Junction user ID,
    same Junction environment, and same Junction region).
  </Step>
</Steps>

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalCore

  if VitalClient.status.contains(.useApiKey) {
    do {
      let response = await callBackend(...)
      try await VitalClient.signIn(withToken: response.signInToken)
      print("Signed in with Junction successfully")
    } catch let error {
      print("Error signing in with Junction", error)
    }
  }
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.VitalClient
  import android.content.Context
  import android.util.Log

  val applicationContext: Context

  VitalClient.getOrCreate(applicationContext)

  if (VitalClient.Status.useApiKey in VitalClient.status) {
    try {
        val response = callBackend(...)
        VitalClient.signIn(applicationContext, response.signInToken)

        Log.i("vital_sdk", "Signed in with Junction successfully")
    } catch (e: Throwable) {
        Log.e("vital_sdk", "Error signing in with Junction", e)
    }
  }
  ```

  ```typescript React Native theme={null}
  import { VitalCore } from "@tryvital/vital-core-react-native";

  const status = await VitalCore.status();

  if (status.includes("useApiKey")) {
    try {
        const response = await callBackend(...);
        await VitalCore.signIn(response.signInToken);

        console.log("Signed in with Junction successfully");
    } catch (error) {
        console.error("Error signing in with Junction", error);
    }
  }
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  Set<vital_core.ClientStatus> status = await vital_core.clientStatus();

  if (status.contains(vital_core.ClientStatus.useApiKey)) {
    try {
        MyAPIResponse response = await callBackend(...);
        await vital_core.signIn(response.signInToken)

        Fimber.i("Signed in with Junction successfully")
    } catch (error) {
        Fimber.e("Error signing in with Junction", error)
    }
  }
  ```
</CodeGroup>


# Health SDK Connection Policies
Source: https://docs.junction.com/wearables/sdks/health/connection-policies

Choose between Auto Connect and Explicit Connect modes to control how the Health SDK manages Apple HealthKit and Health Connect connections.

## Auto Connect mode (default)

A connection is automatically created — or reinstated — when:

* You [ask for health data permission](/wearables/sdks/health/overview#ask-user-for-health-data-permissions) through the Health SDK; or

* the Health SDK begins a data sync attempt for any resource.

<Warning>
  In Auto Connect mode, any de-registration attempt through the [Deregister Connection endpoint](/api-reference/user/deregister-a-provider)
  has no effect.

  The Auto Connect mode will eventually reinstate the connection, typically at the next data sync attempt.
</Warning>

<Info>
  Calling the Health SDK `connect()` or `disconnect()` methods in Auto Connect mode will throw an error.
</Info>

## Explicit Connect mode

<Note>
  Explicit Connect mode is available on iOS SDK 1.8.0+, Android Health SDK 4.2.0+,
  React Native SDK 5.4.0+ and Flutter SDK 4.6.0+.
</Note>

A connection is created only when:

* You call the Health SDK `connect()` method.

A connection is destroyed only when:

* You call the Health SDK `disconnect()` method; or

* You use the [Deregister Connection endpoint](/api-reference/user/deregister-a-provider) to de-register the HealthKit,
  Health Connect or Samsung Health connection remotely from your backend servers.

<Info>
  `connect()` and `disconnect()` in Explicit Connect mode require a call to Junction API over the Internet.

  If the Health SDK fails to confirm connection or disconnection with the Junction API, an error would be thrown.
</Info>

In the Explicit Connect mode:

1. The Health SDK does not automatically create or reinstate a connection:

   * Not when you [ask for health data permission](/wearables/sdks/health/overview#ask-user-for-health-data-permissions) through the Health SDK;

   * Not when it detects either any iOS resource which your app has asked permission for, or any Android resource which your
     app has asked permission for; and

   * Not at the beginning of any sync attempt.

2. Your application logic has full control over when to connect and disconnect.

3. You can stop data sync by de-registering HealthKit, Health Connect or Samsung Health connection via the [Deregister Connection endpoint](/api-reference/user/deregister-a-provider).

## Pausing synchronization temporarily

The Health SDK supports a device-side [Pause Synchronization](/wearables/sdks/health/overview#pausing-data-synchronization) toggle, which
can suspend and resume data synchronization temporarily without disconnection or signing out.

This toggle works in both the Auto Connect mode and the Explicit Connect mode.


# Junction Health SDK
Source: https://docs.junction.com/wearables/sdks/health/overview

Overview of the Junction Health SDK for integrating with Apple HealthKit and Android Health Connect, including setup and common API usage.

## Overview

<Note>
  Make sure you have successfully integrated with a Junction Mobile SDK authentication scheme, before you proceed
  to try out Junction Health SDK.

  <Card title="Mobile SDK Authentication" icon="id-card" href="/wearables/sdks/authentication">
    Learn how to authenticate your mobile app user with Junction Mobile SDK.
  </Card>
</Note>

This Junction Health SDK guidance page focuses on the initial setup and API usage of the Health SDK that is common to
Apple HealthKit, Android Health Connect, and Samsung Health integrations, where supported.

Please consult the following guides on requirements and restrictions of Apple HealthKit, Android Health Connect, and
Samsung Health:

<Card title="Apple HealthKit" icon="apple" href="/wearables/guides/apple-healthkit">
  Learn how to integrate with Apple HealthKit through Junction Mobile SDKs, available in
  Native iOS, React Native and Flutter.
</Card>

<Card title="Android Health Connect" icon="android" href="/wearables/guides/android-health-connect">
  Learn how to integrate with Android Health Connect through Junction Mobile SDKs, available in
  Native Android, React Native and Flutter.
</Card>

<Card title="Samsung Health" icon="heart" href="/wearables/guides/samsung-health">
  Learn how to integrate with Samsung Health through Junction Mobile SDKs, available in
  Native Android and React Native.
</Card>

## Initial Setup

### Review the Connection Policy options

Junction Health SDK has two Connection Policy options:

* The default [Auto Connect](/wearables/sdks/health/connection-policies#auto-connect-mode-default) mode; and

* The opt-in [Explicit Connect](/wearables/sdks/health/connection-policies#explicit-connect-mode) mode.

If your device connection management UX is built upon an explicit notion of connecting and disconnecting Apple HealthKit,
Health Connect and Samsung Health connections — as if they are like their cloud-based counterparts — you might find the [Explicit Connect](/wearables/sdks/health/connection-policies#explicit-connect-mode)
mode more appealing.

### iOS apps: Configure your App Delegate

This section applies to:

* 🔘 Native iOS apps
* 🔘 React Native iOS apps

This section does not apply to:

* ❌ Flutter iOS apps (using Junction Flutter SDK 4.3.8 or later).

<Note>
  Junction **Flutter** SDK (4.3.8 and later) automatically integrates with your App Delegate through the Flutter Plugin machinery.
  You need not manually edit your AppDelegate in **Flutter** iOS projects.
</Note>

<Warning>
  You must configure your App Delegate as instructed if you use Junction Health SDK on iOS. Not doing so
  may result in missing background delivery, or app process termination by the operating system.
</Warning>

In your AppDelegate's `application(_:didFinishLaunchingWithOptions:)` implementation, it **must** call Junction Health SDK
`automaticConfiguration()` synchronously before it returns.

<Accordion title="Details" icon="apple">
  Junction needs to register various handlers with the system frameworks like Apple HealthKit and BackgroundTasks. These frameworks
  require said registrations to be completed **before** the "app finished launching" moment [\[1\]](https://developer.apple.com/documentation/healthkit/hkhealthstore/1614175-enablebackgrounddelivery#3801028)
  [\[2\]](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler/register\(fortaskwithidentifier:using:launchhandler:\)#Discussion).

  For React Native apps, the AppDelegate is part of the template Swift or Objective-C code in your generated Xcode project.

  <CodeGroup>
    ```swift Swift theme={null}
    import VitalHealthKit

    class AppDelegate: NSObject, UIApplicationDelegate {
      func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
      ) -> Bool {

        VitalHealthKitClient.automaticConfiguration()

        // ...

        return true
      }
    }
    ```

    ```objc Objective-C / React Native theme={null}
    #import "AppDelegate.h"
    #import "VitalHealthKitConfiguration.h"

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

      [VitalHealthKitConfiguration automaticConfiguration];

      // ...

      return YES;
    }
    ```
  </CodeGroup>
</Accordion>

### Configure the Junction Health SDK

The Health SDK must be configured before use.

The configuration is persistent, so you typically only need to do this a single time — immediately after
[a successful authentication with the Core SDK](/wearables/sdks/authentication).

<Tip>
  Health Connect and Samsung Health are two independent SDK integrations on Android, despite sharing the same
  Health SDK API. If you intend to use both integrations, you must configure both of them explicitly.
</Tip>

<Note>
  For Android *via* Expo and React Native, the Health SDK API assumes Health Connect by default. To target Samsung Health, you must
  pass `"samsung_health"` to the `provider` argument when calling the API.
</Note>

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalHealthKit

  VitalHealthKitClient.configure(
    VitalHealthKitClient.Configuration(
      backgroundDeliveryEnabled: true,

      // Using Auto Connect mode (default)
      connectionPolicy: .autoConnect

      // Using Explicit Connect mode
      connectionPolicy: .explicit
    )
  )
  ```

  ```kotlin Native Android theme={null}

  /**
   * Configure Health Connect
   */
  import io.tryvital.vitalhealthconnect.VitalHealthConnectManager
  import io.tryvital.vitalhealthcore.model.ConnectionPolicy

  val manager = VitalHealthConnectManager.configure(context)
  manager.configureHealthConnectClient(
    syncNotificationBuilder = null,

    // Using Auto Connect mode (default)
    connectionPolicy = ConnectionPolicy.AutoConnect

    // Using Explicit Connect mode
    connectionPolicy = ConnectionPolicy.Explicit

  )

  /**
   * Configure Samsung Health
   */
  import io.tryvital.vitalsamsunghealth.VitalSamsungHealthManager
  import io.tryvital.vitalhealthcore.model.ConnectionPolicy

  val manager = VitalSamsungHealthManager.configure(context)
  manager.configureSamsungHealthClient(
    syncNotificationBuilder = null,

    // Using Auto Connect mode (default)
    connectionPolicy = ConnectionPolicy.AutoConnect

    // Using Explicit Connect mode
    connectionPolicy = ConnectionPolicy.Explicit

  )
  ```

  ```typescript Expo & React Native theme={null}
  import { Platform } from "react-native";
  import {
    HealthConfig,
    VitalHealth,
  } from "@tryvital/vital-health-react-native";

  let config = new HealthConfig();

  config.iOSConfig.backgroundDeliveryEnabled = true;

  // Using Auto Connect mode (default)
  config.connectionPolicy = "autoConnect";

  // Using Explicit Connect mode
  config.connectionPolicy = "explicit";

  // Configure only Apple HealthKit (iOS) or Health Connect (Android)
  await VitalHealth.configure(config);

  // Configure only Samsung Health
  await VitalHealth.configure(config, "samsung_health");

  // Configure both Health Connect and Samsung Health
  await VitalHealth.configure(config, "health_connect");
  await VitalHealth.configure(config, "samsung_health");
  ```

  ```dart Flutter theme={null}
  import 'package:vital_health/vital_health.dart' as vital_health;

  await vital_health.configure(
    config: const vital_health.HealthConfig(
      iosConfig: vital_health.IosHealthConfig(
        backgroundDeliveryEnabled: true,
      ),

      // Using Auto Connect mode (default)
      connectionPolicy: vital_health.ConnectionPolicy.autoConnect,

      // Using Explicit Connect mode
      connectionPolicy: vital_health.ConnectionPolicy.explicit,
    ),
  );
  ```
</CodeGroup>

## Health Data Permissions

### Ask user for health data permissions

Before the SDK can read any data, you need to ask the end user to grant health data permissions.

<Note>
  For Android *via* Expo and React Native, the Health SDK API assumes Health Connect by default. To target Samsung Health, you must
  pass `"samsung_health"` to the `provider` argument when calling the API.
</Note>

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalHealthKit

  await VitalHealthKitClient.shared.ask(
    readPermissions: [.activity, .workout, .sleep],
    writePermissions: []
  )
  ```

  ```kotlin Native Android theme={null}

  //
  // Android Compose
  //

  import androidx.activity.compose.rememberLauncherForActivityResult
  import androidx.compose.runtime.rememberCoroutineScope
  import androidx.compose.material3.*
  import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

  val manager: VitalHealthConnectManager = ...
  val contract = manager.createPermissionRequestContract(
    readResources = set(
      VitalResource.Activity,
      VitalResource.Workout,
      VitalResource.Sleep,
    ),
    writeResources = emptySet(),
  )

  val coroutineScope = rememberCoroutineScope()
  val permissionsLauncher = rememberLauncherForActivityResult(contract) { outcomeAsync ->
    coroutineScope.launch {
      val outcome = outcomeAsync.await()
      Log.i("VitalPermissionOutcome", outcome.toString())
    }
  }

  Button(onClick = { permissionsLauncher.launch(Unit) }) {
    Text("Ask for permission")
  }

  //
  // AndroidX Activity Result API
  //
  import androidx.activity.ComponentActivity
  import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

  val activity: ComponentActivity = ...
  val manager: VitalHealthConnectManager = ...

  activity.registerForActivityResult(contract) { outcomeAsync ->
    CoroutineScope(Dispatchers.Main).launch {
      val outcome = outcomeAsync.await()
      Log.i("VitalPermissionOutcome", outcome.toString())
    }
  }
  ```

  ```typescript Expo & React Native theme={null}
  import { Platform } from "react-native";
  import {
    VitalHealth,
    VitalResource,
  } from "@tryvital/vital-health-react-native";

  // Apple HealthKit (iOS) or Health Connect (Android)
  await VitalHealth.ask(
      [VitalResource.Activity, VitalResource.Workout, VitalResource.Sleep],
      [],
      undefined,
  );

  // Samsung Health
  await VitalHealth.ask(
      [VitalResource.Activity, VitalResource.Workout, VitalResource.Sleep],
      [],
      undefined,
      "samsung_health"
  );
  ```

  ```dart Flutter theme={null}
  import 'package:vital_health/vital_health.dart' as vital_health;

  final List<vital_health.HealthResource> readResources = [
    vital_health.HealthResource.activity,
    vital_health.HealthResource.sleep,
    vital_health.HealthResource.workout,
  ];
  await vital_health.askForPermission(readResources, []);
  ```
</CodeGroup>

Regardless of whether the user has granted or denied the permissions, any subsequent re-request would
be non-interactive and return instantly. This is because the operating system only prompts the end user
once for each resource type.

<Warning>
  The health data read permission prompt is managed by the operating system. Junction cannot customize or
  alter its behavior.
</Warning>

### Check if your app has asked for permissions before

You can check if your app has already asked the user *at least once* before, for permissions on a specific `VitalResource`.

Because [Ask for Permission](#ask-user-for-health-data-permissions) is gracefully ignored by the operating system beyond
the first time, checking before asking can help you skip the parts of your UX journey that only make sense to users
going through [Ask for Permission](#ask-user-for-health-data-permissions) for the first time.

The only exception to this behavior is when a `VitalResource` pulls a new data type introduced in a new OS release.
In this scenario, calling [Ask for Permission](#ask-user-for-health-data-permissions) would result in an OS permission
prompt that asks for permissions on *specifically* those new data types.

If you wish
to provide a way for users to review their permission grants or denials, you can
[inform them of the system app location where they can do so](#check-if-a-read-permission-was-granted-or-denied-spoiler-you-cant).

<Note>
  For Android *via* Expo and React Native, the Health SDK API assumes Health Connect by default. To target Samsung Health, you must
  pass `"samsung_health"` to the `provider` argument when calling the API.
</Note>

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalHealthKit

  VitalHealthKitClient.shared.hasAskedForPermission(resource: .activity)
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

  val manager = VitalHealthConnectManager.configure(context)
  manager.hasAskedForPermission(VitalResource.Activity)
  ```

  ```typescript Expo & React Native theme={null}
  import { Platform } from "react-native";
  import {
    VitalHealth,
    VitalResource,
  } from "@tryvital/vital-health-react-native";

  // Apple HealthKit (iOS) or Health Connect (Android)
  await VitalHealth.hasAskedForPermission(VitalResource.Activity, provider);

  // Samsung Health
  await VitalHealth.hasAskedForPermission(VitalResource.Activity, "samsung_health");
  ```

  ```dart Flutter theme={null}
  import 'package:vital_health/vital_health.dart' as vital_health;

  await vital_health.hasAskedForPermission(vital_health.HealthResource.Activity);
  ```
</CodeGroup>

### Check if a read permission was granted or denied (⚠️ Spoiler: You can't)

Junction Health SDK allows you to [ask for permission](#ask-user-for-health-data-permissions) and
[check if your app has asked for permission before](#check-if-your-app-has-asked-for-permissions-before).

However, Junction Health SDK **cannot** tell you:

1. Whether or not a user has granted or denied a read permission after the [Ask for Permission](#ask-user-for-health-data-permissions) has concluded.
2. The state of a read permission at any exact moment — granted or denied.

Apple made a deliberate choice to conceal the state of HealthKit data type read permissions from third-party apps.
Quoting [Apple's own words](https://developer.apple.com/documentation/healthkit/hkhealthstore/1614154-authorizationstatus) (as at 27 September 2024):

> To help prevent possible leaks of sensitive health information, your app cannot determine whether or not a user has granted permission to read data.
>
> If you are not given permission, it simply appears as if there is no data of the requested type in the HealthKit store.
>
> \[...]

In other words, all third-party apps — including anyone using the Junction Health SDK — would:

1. not be able to determine if a particular read permission has been granted or denied by the user;

2. not be able to manage or review read permissions inside their app.

The best course of action out of this is to inform your users of the location where they can review and manage their health data read permissions:

| Operating System     | System App         | Location                              |
| -------------------- | ------------------ | ------------------------------------- |
| iOS 8.0 and above    | Health app         | Sharing > Apps and Services           |
| iOS 15.0 and above   | Settings app       | Privacy & Security > Health           |
| Android 13 or below  | Health Connect app | Apps and Permissions                  |
| Android 14 and above | Settings app       | Health Connect > Apps and Permissions |

<Note>
  When designing your user experience, **assume** you would have zero knowledge of
  permission grants and denials after the user has gone through the [Ask for Permission](#ask-user-for-health-data-permissions) flow.
</Note>

## Automatic Data Sync

Junction Health SDK automates data sync for you on both Android and iOS. Please first consult
the provider-specific guides for the requirements and setup instructions:

* [Apple HealthKit guide](/wearables/guides/apple-healthkit)
* [Android Health Connect guide](/wearables/guides/android-health-connect)
* [Samsung Health guide](/wearables/guides/samsung-health)

### iOS

| Sync is automatically... |                                                                                                                                                                                             |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Activated on             | All resource types you have [asked permission for](#ask-user-for-health-data-permissions).                                                                                                  |
| Triggered by             | Two behaviors: <ul><li>Foreground: Apple HealthKit immediately delivers buffered and new changes.</li><li>Background: Hourly batch delivery of changes, subject to OS throttling.</li></ul> |

<Note>
  HealthKit change notification is an always-on behavior.

  If you did not configure [Apple HealthKit Background Delivery](/wearables/guides/apple-healthkit)
  as instructed, your app will not receive any HealthKit change notification while it is in background.
  In turn, background sync would not occur.
</Note>

### Android

| Sync is automatically... |                                                                                                |
| ------------------------ | ---------------------------------------------------------------------------------------------- |
| Activated on             | All resource types you have [asked permission for](#ask-user-for-health-data-permissions).     |
| Triggered by             | Two mechanisms: <ul><li>Sync On App Launch</li><li>Background Sync if it is enabled.</li></ul> |

<Note>
  Sync On App Launch is an always-on behavior. Background Sync is an **opt-in** behavior.

  Please refer to the [Android Health Connect guide](/wearables/guides/android-health-connect) or
  the [Samsung Health guide](/wearables/guides/samsung-health) for the full context and required configuration
  of these two behaviors.
</Note>

### Background Sync Frequency

Junction Health SDK schedules hourly background sync with Apple HealthKit, Android Health Connect and Samsung Health,
provided that you have:

1. Configured [Apple HealthKit Background Delivery](/wearables/guides/apple-healthkit) as per instructions; and
2. Configured Android Background Sync for the provider you are using — [Health Connect](/wearables/guides/android-health-connect#background-sync)
   or [Samsung Health](/wearables/guides/samsung-health#background-sync) — and request user permissions (when necessary) as per instructions.

However, this schedule is only advisory. The OS has full discretion to defer the scheduled time based on runtime
constraints like battery power, as well as platform background execution policies. For a more detailed explanation, please refer to:

* [Apple HealthKit: Sync Frequency](/wearables/guides/apple-healthkit#sync-frequency)
* [Android Health Connect: Synchronization](/wearables/guides/android-health-connect#synchronization)
* [Samsung Health: Background Sync](/wearables/guides/samsung-health#background-sync)

In other words, your product experience should not assume near real-time availability of wearable data, since the norm
is a variable delay ranging from an hour to a day. Longer delay is also possible due to the lack of Internet
connectivity, e.g., a long-haul flight, or an off-the-grid trip.

The only possible exception is when your product experience does involve
**active usage** of your consumer app with an active Internet connection. For example, iOS 17+ supports third-party
apps initiating an Apple Watch workout session that would be live synced with your iPhone app.

### Pausing Data Synchronization

You can pause and unpause data sync at any time, without having to sign out the user from the Junction Mobile SDK.
Note that pausing data sync does not reset the incremental sync progress and does not trigger a historical stage
upon unpause. It only "freezes" the sync progress.

<Note>
  For Android *via* Expo and React Native, the Health SDK API assumes Health Connect by default. To target Samsung Health, you must
  pass `"samsung_health"` to the `provider` argument when calling the API.
</Note>

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalHealthKit

  // Pause Synchronization
  VitalHealthKitClient.shared.pauseSynchronization = true

  // Unpause Synchronization
  VitalHealthKitClient.shared.pauseSynchronization = false

  // Check if synchronization is paused
  print("Paused? \(VitalHealthKitClient.shared.pauseSynchronization)")
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.vitalhealthconnect.VitalHealthConnectManager

  val manager = VitalHealthConnectManager.getOrCreate(context)

  // Pause Synchronization
  manager.pauseSynchronization = true

  // Unpause Synchronization
  manager.pauseSynchronization = false

  // Check if synchronization is paused
  Log.i("sync", "Paused? ${manager.pauseSynchronization}")
  ```

  ```typescript Expo & React Native theme={null}
  import { Platform } from "react-native";
  import {
    VitalHealth,
  } from "@tryvital/vital-health-react-native";

  // Pause Synchronization
  await VitalHealth.setPauseSynchronization(true);
  await VitalHealth.setPauseSynchronization(true, "apple_health_kit");
  await VitalHealth.setPauseSynchronization(true, "health_connect");
  await VitalHealth.setPauseSynchronization(true, "samsung_health");

  // Unpause Synchronization
  await VitalHealth.setPauseSynchronization(false);
  await VitalHealth.setPauseSynchronization(false, "apple_health_kit");
  await VitalHealth.setPauseSynchronization(false, "health_connect");
  await VitalHealth.setPauseSynchronization(false, "samsung_health");

  // Check if synchronization is paused
  const isPaused = await VitalHealth.isProviderSynchronizationPaused();
  const isPaused = await VitalHealth.isProviderSynchronizationPaused("apple_health_kit");
  const isPaused = await VitalHealth.isProviderSynchronizationPaused("health_connect");
  const isPaused = await VitalHealth.isProviderSynchronizationPaused("samsung_health");
  ```

  ```dart Flutter theme={null}
  import 'package:vital_health/vital_health.dart' as vital_health;

  // Pause Synchronization
  await vital_health.setPauseSynchronization(true);

  // Unpause Synchronization
  await vital_health.setPauseSynchronization(false);

  // Check if synchronization is paused
  final isPaused = await vital_health.pauseSynchronization;
  ```
</CodeGroup>

## Sync Status

You can observe the `syncStatus` stream for continuous updates on the health data sync status.

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalHealthKit

  VitalHealthKitClient.shared.status.sink { status in
    print("Sync Status: \(status)")
  }
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.vitalhealthconnect.VitalHealthConnectManager
  import kotlinx.coroutines.*
  import kotlinx.coroutines.flow.*

  val coroutineScope: CoroutineScope
  val manager = VitalHealthConnectManager.getOrCreate(context)

  manager.status
    .onEach { status -> Log.i("HealthDataSync", "$status") }
    .launchIn(coroutineScope)
  ```

  ```typescript Expo & React Native theme={null}
  import { Platform } from "react-native";
  import {
    AndroidHealthProvider,
    IOSHealthProvider,
    VitalHealth,
  } from "@tryvital/vital-health-react-native";

  // Apple HealthKit (iOS) or Health Connect (Android)
  const subscription = VitalHealth.observeSyncStatusChange((status) => {
    console.log("Sync Status", status);
  });

  // Samsung Health
  const subscription = VitalHealth.observeSyncStatusChange((status) => {
    console.log("Sync Status", status);
  }, "samsung_health");

  // later
  subscription.remove();
  ```

  ```dart Flutter theme={null}
  import 'package:vital_health/vital_health.dart' as vital_health;

  Stream<SyncStatus> status = vital_health.syncStatus;
  ```
</CodeGroup>

## Sync Progress Debugging UI (iOS only)

<AccordionGroup>
  <Accordion title="Sync Progress View on Native iOS">
    Junction Health SDK for iOS includes a `ForEachVitalResource` — a pre-baked SwiftUI View providing a
    live-updated sync progress feed of all the `VitalResource`s whose permissions have been asked for.

    You can use `ForEachVitalResource()` in any position which you would use `ForEach`. For example,
    you can use it inside a `Section` of a `List`:

    ```swift Native iOS theme={null}
    import VitalHealthKit

    struct SyncProgressView: View {
      var view: some View {
        List {
          Section(header: Text("Sync Progress")) {
            ForEachVitalResource()
          }
        }
      }
    }
    ```

    If you are using UIKit, you can still access it through `UIHostingController`:

    ```swift Native iOS theme={null}
    let viewController: UIViewController

    let hostingController = UIHostingController(rootView: SyncProgressView())
    viewController.present(hostingController, animated: true, completion: nil)
    ```
  </Accordion>

  <Accordion title="Sync Progress UI on Flutter / React Native">
    You can open the Apple HealthKit Sync Progress Inspector UI through a simple call:

    <CodeGroup>
      ```dart Flutter theme={null}
      import 'package:vital_health/vital_health.dart' as vital_health;

      unawaited(vital_health.openSyncProgressView());
      ```

      ```typescript Expo & React Native theme={null}
      import {
        IOSHealthProvider,
        VitalHealth,
      } from "@tryvital/vital-health-react-native";

      await VitalHealth.openSyncProgressView();
      ```
    </CodeGroup>

    This Sync Progress Inspector UI provides a live-updated sync progress feed of all the VitalResources whose permissions have been asked for.

    <Note>
      Note that it is currently a no-op on Android.
    </Note>
  </Accordion>
</AccordionGroup>

## Persistent Logging (iOS only)

While Junction Dashboard provides a fairly comprehensive Mobile SDK Sync Progress view, the reporting mechanism
can only include condensed and headline information in order to conserve network bandwidth usage.

In order to troubleshoot data sync issues that are non-reproducible in controlled environments, the Health
SDK supports **opt-in** Persistent Logging which would keep logs on disk across application launches.

<Tip>
  The archive file is simply an Apple Archive (aar) containing various JSON files and plain text logs. Your app developers
  are welcome to dig into it for a self-troubleshooting attempt.
</Tip>

<CodeGroup>
  ```swift iOS theme={null}
  import VitalCore
  import VitalHealthKit

  // Toggle Persistent Logging
  // ℹ️ Note that this persists across application launches.
  VitalPersistentLogger.isEnabled = true

  // Generate the archive file, and open the system Share Sheet.
  VitalHealthKitClient.createAndShareLogArchive()
  ```

  ```typescript Expo & React Native theme={null}
  import {
    VitalHealth,
  } from "@tryvital/vital-health-react-native";

  // Toggle Persistent Logging
  // ℹ️ Note that this persists across application launches.
  await VitalHealth.setPersistentLoggingEnabled(true);

  // Check if Persistent Logging is enabled.
  const isEnabled = await VitalHealth.isPersistentLoggingEnabled();

  // Generate the archive file, and open the system Share Sheet.
  await VitalHealth.sharePersistentLogArchive(true);
  ```
</CodeGroup>

You can introduce a couple of simple UI controls in a non-prominent location in your user experience, e.g., the app settings screen:

* A "Capture Sync Issues" switch, which toggles the SDK Persistent Logging.
* A "Share the Capture" button, which triggers the archive file generate-and-share flow.

Then on an as-needed basis, your support team can:

1. Instruct users to enable capturing; and
2. In a few days' time, share the capture (archive file) with you.

<Note>
  Junction may also require your support team's help to obtain an archive file, in order to make progress on any non-reproducible issue reports.
</Note>


# Installation
Source: https://docs.junction.com/wearables/sdks/installation

Install Junction Mobile SDKs on iOS and Android with platform-specific requirements, minimum OS versions, and dependency configuration.

## Project requirements

### iOS

<Info>Applies also to Flutter and React Native projects.</Info>

| Parameter                 | Requirement |
| ------------------------- | ----------- |
| Minimum deployment target | iOS 15.1    |

<Note>
  If you are integrating on React Native or Flutter, or integrating on Native iOS via CocoaPods,
  make sure you have updated the minimum iOS deployment target in:

  * your `Podfile`; and
  * your iOS App Target.

  ```diff Podfile theme={null}
  # e.g., React Native default
  -platform :ios, min_ios_version_supported

  # Set minimum deployment target to be iOS 15.1.
  +platform :ios, '15.1'
  ```
</Note>

### Android

<Info>Applies also to Flutter, React Native and Expo projects.</Info>

| Parameter                     | Requirement                              |
| ----------------------------- | ---------------------------------------- |
| Minimum SDK Level             | 26 (Health Connect), 29 (Samsung Health) |
| Compile SDK Level             | 36                                       |
| Kotlin compiler version       | 2.1.20 or above                          |
| Android Gradle Plugin version | 8.9.1 or above                           |
| Gradle version                | 8.11.1 or above                          |

### Flutter

| Parameter   | Requirement   |
| ----------- | ------------- |
| Flutter SDK | 3.13 or above |

### React Native

#### Expo

| Parameter | Requirement |
| --------- | ----------- |
| Expo      | 53 or above |

#### Bare React Native projects

| Parameter         | Requirement     |
| ----------------- | --------------- |
| React Native Core | 0.72.0 or above |
| Node.js           | 20.0 or above   |

## Set up package dependencies

### iOS

Junction Mobile SDKs are available through both Swift Package Manager and CocoaPods.

#### Swift Package Manager

Add the vital-ios package (`https://github.com/tryVital/vital-ios`) as a dependency of your project.

Link `VitalHealthKit` and `VitalDevices` as appropriate. Note that `VitalCore` is mandatory.

#### CocoaPods

Add the following declarations to your app target in your Podfile:

```ruby theme={null}
pod 'VitalCore'
pod 'VitalDevices'
pod 'VitalHealthKit'
```

### Android

Make sure Maven Central is included in the list of repositories in your `build.gradle`.

```groovy theme={null}
repositories {
  mavenCentral()
}
```

Then include our Android SDK artifacts as dependencies of your modules as needed:

```groovy theme={null}
def vital_version = '3.2.1'

implementation 'io.tryvital:vital-client:$vital_version'
implementation 'io.tryvital:vital-health-connect:$vital_version'
implementation 'io.tryvital:vital-samsung-health:$vital_version'
implementation 'io.tryvital:vital-devices:$vital_version'
```

<Note>
  Samsung Health also requires the downloaded Samsung Health Data SDK AAR to be wired into your Android root project via
  `samsungHealthAarPath` (Native Android / bare React Native) or `samsungHealth.aarPath` (Expo config plugin). Check out the
  [Samsung Health guide](/wearables/guides/samsung-health#samsung-health-project-setup) for more details.
</Note>

### React Native

#### Expo projects

##### Installation

<Info>
  Only [Expo Prebuild](https://docs.expo.dev/guides/adopting-prebuild/) is supported.
  Expo Go is incompatible, since it supports neither native library binaries, nor [Expo Config Plugin](https://docs.expo.io/guides/config-plugins/).
</Info>

First install the package with [`npx expo install`](https://docs.expo.dev/more/expo-cli/#installation).

```sh theme={null}
npx expo install @tryvital/vital-core-react-native
npx expo install @tryvital/vital-health-react-native
```

After installing the Junction React Native SDK packages, add the bundled Junction Expo Config Plugins to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your Expo app configuration file (`app.json` or `app.config.js`):

```json app.json/app.config.js theme={null}
{
  "expo": {
    "plugins": [
      "@tryvital/vital-core-react-native",
      "@tryvital/vital-health-react-native"
    ]
  }
}
```

##### Android specific configuration

In your Expo app configuration file (`app.json` or `app.config.js`), use the `expo-build-properties` config plugin to customize the minimum, target and compile Android SDK version:

```json app.json/app.config.js theme={null}
{
  "expo": {
    // ...
    "plugins": [
      [
        "expo-build-properties",
        {
          "android": {
            "compileSdkVersion": 34,
            "targetSdkVersion": 34,
            "minSdkVersion": 26
          }
        }
      ],
      // ...
    ]
  }
}
```

<Note>
  Refer to [Project Requirements > Android](#android) for the constraints on these SDK version levers.
</Note>

Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.dev/workflow/customizing/) guide.

##### Customizations

You can customize the integration by passing additional properties to the Expo config plugin:

| Key                                | Value                                                                                                                                         |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `healthSharePermission` (*string*) | Sets the iOS `NSHealthShareUsageDescription` permission message to the `Info.plist`. Defaults to `Allow $(PRODUCT_NAME) to check health info` |
| `samsungHealth.aarPath` (*string*) | Path to the downloaded Samsung Health Data SDK AAR. Required when enabling Samsung Health on Android through Expo.                            |

```json app.json/app.config.js theme={null}
{
  "expo": {
    "plugins": [
      "@tryvital/vital-core-react-native",
      [
        "@tryvital/vital-health-react-native",
        {
          "healthSharePermission": "Custom health share permission",
          "samsungHealth": {
            "aarPath": "../vendor/samsung-health-data-api-1.0.0.aar"
          }
        }
      ]
    ]
  }
}
```

<Info>
  After making changes to your Config Plugin settings, you must re-run Expo Prebuild to re-generate your iOS and Android projects.
</Info>

#### Bare React Native projects

Install Junction SDK packages through NPM or Yarn:

```commandline theme={null}
npm install @tryvital/vital-core-react-native
npm install @tryvital/vital-health-react-native
npm install @tryvital/vital-devices-react-native
```

Pull all dependencies for your React Native iOS project:

```commandline theme={null}
cd ios
pod install
```

<Info>
  `pod install` on your iOS project might error due to version lock conflicts on the Junction iOS libraries.
  This usually happens after you have bumped the React Native SDK package versions.

  Use `pod update` to resolve the conflict:

  ```commandline theme={null}
  pod update VitalCore VitalDevices VitalHealthKit VitalLogging --repo-update
  ```
</Info>

### Flutter

Add Junction SDK packages to your `pubspec.yaml`:

```yaml theme={null}
dependencies:
  vital_core: ^3.1.1
  vital_health: ^3.1.1
  vital_devices: ^3.1.1
```

<Info>
  `pod install` on your iOS project might error due to version lock conflicts on the Junction iOS libraries.
  This usually happens after you have bumped the Flutter SDK package versions.

  Use `pod update` to resolve the conflict:

  ```commandline theme={null}
  pod update VitalCore VitalDevices VitalHealthKit --repo-update
  ```
</Info>


# Junction Core SDK
Source: https://docs.junction.com/wearables/sdks/vital-core

Use the Junction Core SDK to access the Junction API from mobile apps through typed request methods across iOS, Android, Flutter, and React Native.

## Overview

Check out the [SDK Authentication guide](/wearables/sdks/authentication) on the available SDK authentication
schemes, and how to authenticate with the Junction Mobile SDK.

<Card title="Mobile SDK Authentication" icon="id-card" href="/wearables/sdks/authentication">
  Learn how to authenticate your mobile app user with Junction Mobile SDK.
</Card>

## Accessing Junction API

You can access Junction API through `VitalClient` with typed request methods and response models.

<CodeGroup>
  ```swift Native iOS theme={null}
  // e.g. Link Service
  let linkService = VitalClient.shared.link
  ```

  ```kotlin Native Android theme={null}
  // e.g. Link Service
  val linkService = VitalClient.getOrCreate(context).linkService
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  vital_core.VitalClient client;

  // For customers using Junction Team API Keys for early evaluation.
  client = vital_core.VitalClient()
    ..init(region: Region.eu, environment: Environment.sandbox, apiKey: 'sk_eu_...');

  // For customers using the Junction Sign-In Tokens scheme
  client = vital_core.VitalClient.forSignedInUser(environment: Environment.sandbox, region: Region.eu);

  // e.g. Link Service
  final linkService = client.linkService;
  ```
</CodeGroup>

<br />

<Info>Typed API access is not yet available in React Native.</Info>

## Generating Junction Link URL

You can obtain a URL to the Junction Link Widget through `VitalClient.linkWidgetUrl`. Note that the URL contains
a short-lived token with 10-minute expiry.

For the `redirectUrl`, you should set it to a URL with a custom URL scheme which your app would register to handle.

<CodeGroup>
  ```swift Native iOS theme={null}
  let widgetUrl = await VitalClient.shared.link.createProviderLink(
    redirectURL: "x-vital-app://"
  )
  ```

  ```dart Flutter theme={null}
  Uri widgetUrl = await client.linkWidgetUrl(redirectUrl: "x-vital-app://");
  ```
</CodeGroup>

## Core SDK Status

You can inspect the status of the Core SDK for troubleshooting, as well as for your own business logic.
For example, you may reconcile your user session and the Junction SDK sign-in based on the Core SDK status.

The SDK normally reports one of the following status combinations:

| Combinations                                | Auth Scheme            | Remarks                                                                          |
| ------------------------------------------- | ---------------------- | -------------------------------------------------------------------------------- |
| *Empty*                                     | N/A                    | The SDK has neither been configured nor has an active sign-in.                   |
| `Configured`                                | API Key                | The SDK has been configured, but does not have an active sign-in.                |
| `Configured`, `SignedIn` & `UseApiKey`      | API Key                | The active sign-in was done by setting an API Key and a target Junction User ID. |
| `Configured`, `SignedIn` & `UseSignInToken` | Junction Sign-In Token | The active sign-in was done by consuming a Junction Sign-In Token.               |

### Get the current status

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalCore

  let status: VitalClient.Status = VitalClient.status
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.VitalClient
  import android.content.Context

  val applicationContext: Context

  VitalClient.getOrCreate(applicationContext)
  val status = VitalClient.status
  ```

  ```typescript React Native theme={null}
  import { VitalCore } from "@tryvital/vital-core-react-native";

  await VitalCore.status();
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  await vital_core.clientStatus();
  ```
</CodeGroup>

### Observe status changes over time

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalCore

  // Combine Publisher
  let cancellable = VitalClient.statusDidChange.sink { _ in }

  // AsyncStream
  Task {
    for status in VitalClient.statuses {
      // ...
    }
  }
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.VitalClient
  import android.content.Context

  val applicationContext: Context

  val statuses: Flow<Set<VitalClient.Status>>
  statuses = VitalClient.statuses(applicationContext)

  val job = statuses
    .onEach { /** ... */ }
    .launchIn(...)
  ```

  ```typescript React Native theme={null}
  import { VitalCore } from "@tryvital/vital-core-react-native";

  VitalCore.observeStatusChange((statuses) => {
    // ...
  });
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  // NOTE: This stream does not yield the current statuses on listen.
  Stream<Set<ClientStatus>> statuses;
  statuses = vital_core.clientStatusStream;
  ```
</CodeGroup>

## Get Current Junction User ID

Aside from the Core SDK Status, you can also query the Junction User ID of the signed-in user:

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalCore

  let userId: String? = VitalClient.currentUserId
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.VitalClient
  import android.content.Context

  val applicationContext: Context

  VitalClient.getOrCreate(applicationContext)
  val userId = VitalClient.currentUserId
  ```

  ```typescript React Native theme={null}
  import { VitalCore } from "@tryvital/vital-core-react-native";

  const userId = await VitalCore.currentUserId();
  ```

  ```dart Flutter theme={null}
  import 'package:vital_core/vital_core.dart' as vital_core;

  final userId = await vital_core.currentUserId();
  ```
</CodeGroup>

## Reset the SDK (Sign Out)

Refer to [the Sign Out section in the SDK Authentication guide](/wearables/sdks/authentication#sign-out).

## Verbose Logging to Console/Logcat

You can enable verbose logging to have a deeper look into how Junction Mobile SDK is reacting to your calls, as well as
all the automatic behaviors that the SDK is performing in background.

To avoid any missing log entry, you should enable verbose logging as early as possible — preferably before any usage of
any Junction Mobile SDKs.

<Note>
  On Android, verbose logging is enabled by default when running as a debuggable build
  (i.e., with `ApplicationInfo.FLAG_DEBUGGABLE`).
</Note>

<CodeGroup>
  ```swift Native iOS (Swift) theme={null}
  import VitalCore

  class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
      _ application: UIApplication,
      didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {

      VitalLogger.stdOutEnabled = true

      // ...

      return true
    }
  }
  ```

  ```objc Native iOS (Objective-C) theme={null}
  #import "AppDelegate.h"

  @import VitalCore;

  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    VitalLoggerObjC.stdOutEnabled = YES;

    // ...

    return YES;
  }
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.client.utils.VitalLogger

  VitalLogger.getOrCreate().enabled = true
  ```
</CodeGroup>


# Junction Devices SDK
Source: https://docs.junction.com/wearables/sdks/vital-devices

Use the Junction Devices SDK for Bluetooth-based device connectivity with required permission setup for iOS and Android projects.

## Prerequisites

Your app project must declare the required permission usage. This applies equally to native,
Flutter, and React Native app projects.

<AccordionGroup>
  <Accordion title="iOS projects">
    Add the following to your `Info.plist` file:

    ```xml theme={null}
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>Our app uses bluetooth to find, connect and transfer data between different devices</string>
    ```

    Bluetooth permission on iOS is automatically requested on the first scanning attempt.
  </Accordion>

  <Accordion title="Android projects">
    Add the following to your `AndroidManifest.xml`:

    ```xml theme={null}
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

    <!-- Request legacy Bluetooth permissions on older devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>
    ```

    You will have to request the appropriate permissions in your app before you can call the Junction Devices SDK.
  </Accordion>
</AccordionGroup>

## Bluetooth devices

### Choose Device Model

The SDK provides a list of supported Bluetooth devices. Select the matching instance to prepare for scanning.

<CodeGroup>
  ```swift Native iOS theme={null}
  import VitalDevices

  let devicesManager = DevicesManager()

  /// Get the brands
  let brands = DevicesManager.brands()

  /// Each brand has devices
  let devices = DevicesManager.devices(for: brands[0])
  ```

  ```kotlin Native Android theme={null}
  import io.tryvital.vitaldevices.*

  val deviceManager = VitalDeviceManager.create(context)

  val deviceModels = devices(Brand.AccuChek)
  val deviceModel = deviceModels[0]
  ```

  ```typescript React Native theme={null}
  import { VitalDevicesManager } from "@tryvital/vital-devices-react-native";

  const devicesManager = new VitalDevicesManager();

  let deviceModels = VitalDevicesManager.supportedDevices;
  let deviceModel = deviceModels[0];
  ```

  ```dart Flutter theme={null}
  import 'package:vital_devices/vital_devices.dart';

  final deviceManager = DeviceManager();

  final deviceModels = await deviceManager.getDevices(Brand.accuChek);
  final deviceModel = deviceModels[0];
  ```
</CodeGroup>

### Scanning for devices

First, you have to scan for one of the supported devices.

<CodeGroup>
  ```swift Native iOS theme={null}
  let cancellable = devicesManager
    .search(for: state.device)
    .sink { scannedDevice in
      print("Discovered: \(scannedDevice)")
    }

  // Stop scanning
  cancellable.cancel()
  ```

  ```kotlin Native Android theme={null}
  val job = deviceManager
    .search(deviceModel)
    .catch {
        Log.i("Devices", "Error scanning ${it.message}", it.cause)
    }
    .onEach { scannedDevice ->
        Log.i("Devices", "Discovered ${scannedDevice}")
    }
    .launchIn(Dispatchers.Main)

  // Stop scanning
  job.cancel()
  ```

  ```typescript React Native theme={null}
  let scanner = deviceManager.scanForDevice(deviceModel, {
      onDiscovered: (device) => {
          console.log("Discovered: " + device)
      },
      onError: (error) => console.log(error)
  })

  // Stop scanning
  scanner.cancel()
  ```

  ```dart Flutter theme={null}
  deviceManager.scanForDevices(deviceModel).listen((newDevice) {
    Fimber.i("Discovered: $newDevice");
  });

  // Stop scanning
  await deviceManager.stopScan();
  ```
</CodeGroup>

Depending on the type of device you are connecting to, you will have to call different methods to connect to it.

### Blood Pressure Monitor

<CodeGroup>
  ```swift Native iOS theme={null}
  let scannedDevice: ScannedDevice
  let reader = deviceManager.bloodPressureReader(for: scannedDevice)

  reader.read(device: scannedDevice).sink { samples in
    print("Read samples: \(samples)")
  }
  ```

  ```kotlin Native Android theme={null}
  val scannedDevice: ScannedDevice

  val samples = deviceManager.bloodPressure(context, scannedDevice).read()
  ```

  ```typescript React Native theme={null}
  let scannedDevice: ScannedDevice

  let samples = await devicesManager.readBloodPressure(scannedDevice.id)
  ```

  ```dart Flutter theme={null}
  List<BloodPressureSample> bloodPressureSamples;
  bloodPressureSamples = await deviceManager.readBloodPressureData(scannedDevice);
  ```
</CodeGroup>

### Glucose Meter

<CodeGroup>
  ```swift Native iOS theme={null}
  let scannedDevice: ScannedDevice
  let reader = deviceManager.glucoseMeter(for: scannedDevice)

  reader.read(device: scannedDevice).sink { samples in
    print("Read samples: \(samples)")
  }
  ```

  ```kotlin Native Android theme={null}
  val scannedDevice: ScannedDevice

  val samples = deviceManager.glucoseMeter(context, scannedDevice).read()
  ```

  ```typescript React Native theme={null}
  let scannedDevice: ScannedDevice

  let samples = await devicesManager.readGlucoseMeter(scannedDevice.id)
  ```

  ```dart Flutter theme={null}
  List<QuantitySample> glucoseSamples;
  glucoseSamples = await deviceManager.readGlucoseMeterData(scannedDevice);
  ```
</CodeGroup>

After you have received samples, depending on the type of device, you might need to start scanning again to receive the
next set of samples.

## Freestyle Libre 1

<Warning>
  Readings taken with the SDK are not guaranteed to match the official Freestyle
  Libre app. This mismatch happens due to algorithm differences between our
  implementation and the official Freestyle Libre.
</Warning>

We currently support Libre 1 sensors via NFC readings. Please make sure you add NFC capabilities in your app:

<img />

Also add the key `NFCReaderUsageDescription` in your Info.plist. This key should explain why your app needs to use NFC.

To use the reader:

<CodeGroup>
  ```swift Native iOS theme={null}
  let reader = Libre1Reader(
      readingMessage: "Ready for reading",
      errorMessage: "Failed reading from sensor",
      completionMessage: "Completed successfully!",
      queue: mainQueue
  )

  let reading = try await reader.read()
  ```

  ```kotlin Native Android theme={null}
  import android.app.Activity
  import io.tryvital.vitaldevices.devices.Libre1Reader

  val currentActivity: Activity

  val reading = Libre1Reader.create(currentActivity).read()
  ```

  ```typescript React Native theme={null}
  import { VitalDevicesManager } from "@tryvital/vital-devices-react-native";

  let devicesManager: VitalDevicesManager

  let result = await devicesManager.readLibre1("reading", "errored", "completed")
  ```
</CodeGroup>

`read()` returns a result object with two fields:

1. the sensor information; and
2. a list of glucose samples.

## Upload device samples to Junction

<Note>
  This automatic behavior is available since Junction iOS SDK 1.0.0, Junction Android SDK 3.0.0, Junction Flutter SDK 4.0.0
  and Junction React Native SDK 4.0.0.
</Note>

Junction Devices SDK automatically uploads any blood pressure and glucose samples discovered during
the read operation, provided that the Junction Core SDK [has a signed-in user](/wearables/sdks/vital-core#core-sdk-status).

The SDK does not currently buffer samples that fail to upload. You might want to ensure that the
device has Internet connectivity before initiating the read operation.


# Introduction
Source: https://docs.junction.com/wearables/vital-app/introduction

Use the Junction Connect iOS app to sync Apple HealthKit data to Junction without building a custom iOS app, using a code-based pairing flow.

<Info>
  Junction Connect iOS app is available for [the Grow and Scale plans](https://tryvital.io/pricing).
</Info>

<Note>
  The app is available on the App Store, and you can download it
  [here](https://apps.apple.com/us/app/vital-connect/id6444167196).
</Note>

The Junction iOS app allows your customers to quickly sync their Apple HealthKit data with you. This is quite handy if you don't have an iOS app, but you still want to analyze the data stored in the Apple Health app.

<div>
  <img />

  <img />
</div>

## How does it work?

The flow is quite straightforward:

1. Your user is already part of our system (e.g., they have a `userId`). If they aren't, you can create one by using our [Create User](/api-reference/user/create-user) endpoint.
2. You create a code, against a `userId`. You can use our [Create Code](/api-reference/link/create-code) endpoint for this.
3. You provide this code to your user. They will then go to the Settings and input this code. Once successful, their data will be synced.

<Warning>
  A Junction code can only be used once. If your user is facing issues, please ask
  them if they tried the code multiple times.
</Warning>

## Stop sharing data

After the user enters the code successfully, they will have the option to:

1. Stop sharing data.
2. Add other providers (e.g., Garmin, Oura).

If the user stops sharing data, the app will clean the user's credentials stored locally. It will also reset the SDK. However, it won't delete the connection between that user and Apple HealthKit on our servers. This means that even if the user stops sharing data, the already collected data won't be deleted. This is true not only for Apple HealthKit but also for any other provider the user might be connected to (e.g., Garmin, Oura).

If you want to disconnect your user from a provider, you need to call the [deregister provider endpoint](/api-reference/user/deregister-a-provider).


# Debugging
Source: https://docs.junction.com/webhooks/debugging

Debug webhook events using the Junction Dashboard Webhooks section or the Svix API to inspect delivery history and retry failed events.

You can debug webhook events in one of two ways: the Dashboard or using the Svix API.

### [Junction Dashboard](https://app.junction.com)

The Junction [Dashboard](https://app.junction.com)
has a Webhooks section that allows you to test and observe webhook events.

<img />


# Azure Event Hubs
Source: https://docs.junction.com/webhooks/etl-pipelines/azure-event-hubs

Stream Junction events to Azure Event Hubs as an ETL pipeline destination, with details on event structure, properties, and compression.

<Info>
  ETL Pipelines integration is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

## Event Structure

Each event is published with these [Azure EventData Properties](https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.eventhubs.eventdata.properties?view=azure-dotnet#azure-messaging-eventhubs-eventdata-properties):

| Type          | Key              | Value                                                                                                |
| ------------- | ---------------- | ---------------------------------------------------------------------------------------------------- |
| Property      | event-type       | Junction event type, e.g., `daily.data.activity.created`                                             |
| Property      | user-id          | Junction User ID of the event subject                                                                |
| Property      | team-id          | Junction Team ID of the event subject                                                                |
| Property      | content-encoding | `gzip`: The blob is compressed by gzip. absent: The blob is uncompressed.                            |
| Property      | idempotency-key  | The event delivery ID; only unique amongst all events from the same user and of the same event type. |
| Partition Key | -                | Junction User ID                                                                                     |
| Data          | -                | The JSON blob. This may or may not be compressed — see `content-encoding`.                           |

<Info>
  By default, payloads smaller than 1KiB would not be compressed. You must check `content-encoding` to identify if
  decompression of the event payload blob is necessary.

  To force Junction to always compress the payload, set `compression: always` on your
  [Team ETL Pipeline](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) configuration.
</Info>

<Info>
  At this time, Junction publishes exclusively JSON blobs.

  Having said that, you are strongly encouraged to check for and drop any events with unrecognized `content-encoding`.
</Info>

## Broker authentication

Junction connects to your Azure Event Hub namespace through a connection URL you supplied in your
[Team ETL Pipeline](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) configuration:

* It must embed an Event Hub Shared Access Signature (SAS); and
* It must **not** specify an Event Hub — the default Event Hub should be declared separately as part of your
  [Team ETL Pipeline](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) configuration.

## Configuration

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

You can manage your Azure Event Hubs destination through the Org Management API
[Team ETL Pipeline](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) endpoints.

A basic configuration would look like this:

```json theme={null}
{
  "team_id": "TEAM_ID",
  "preferences": {
    "enabled": ["azure_amqp"],
    "preferred": "azure_amqp"
  },
  "azure_amqp": {
    "connection_string": "Endpoint://...;...",
    "default_event_hub": "vital_event_ingestion"
  },
  "push_historical_data": true
}
```

### Multiple Event Hubs

You can [configure your Azure Event Hub destination](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines)
to route certain events to designated Event Hubs that are *not* the default Event Hub.

You can do so by declaring a list of Event Hub Matchers (JSON Path: `$.azure_amqp.event_hub_matchers`). For every outbound event,
Junction sends it to the first ever Event Hub which has a matching Event Type prefix, or the default Event Hub if none matches.

```json Example theme={null}
"azure_amqp": {
  ...,

  "event_hub_matchers": [
    {
      "event_type_prefix": "labtest.",
      "event_hub": "labtest-events"
    },
    {
      "event_type_prefix": "daily.",
      "event_hub": "wearable-events"
    },
    {
      "event_type_prefix": "historical.",
      "event_hub": "wearable-events"
    },
    {
      "event_type_prefix": "provider.",
      "event_hub": "wearable-events"
    }
  ]
}
```

### Azure Blob Storage

You can [configure your Azure Event Hub destination](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines)
to route certain events to an Azure Blob Storage container. This is useful if you have enabled the Provider Raw Data firehose, which may
publish raw data events beyond the 1 MiB size limit of Azure Event Hub.

Junction writes each matched event as a Block Blob. The blob name follows the standard format of:

```
# uncompressed
{team-id}/{user-id}/{timestamp_rfc3339}#{event-type}#{idempotency-key}.json

# gzip compressed
{team-id}/{user-id}/{timestamp_rfc3339}#{event-type}#{idempotency-key}.json.gz
```

You can do so by declaring an Event Hub Matcher that targets a Blob Storage container instead of an Event Hub:

```json Example theme={null}
"azure_amqp": {
  ...,

  "event_hub_matchers": [
    {
      "event_type_prefix": "raw.garmin.",
      "blob_storage": {
        "connection_string": "Endpoint://...;...",
        "container": "garmin-raw-data"
      }
    },
    {
      "event_type_prefix": "raw.",
      "blob_storage": {
        "connection_string": "Endpoint://...;...",
        "container": "provider-raw-data"
      }
    },
    {
      "event_type_prefix": "daily.",
      "event_hub": "wearable-events"
    },
    ...
  ]
}
```

<Warning>
  Some polling-based providers can produce 96 writes per day per user on certain event types, e.g., `daily.data.activity.*` and
  `raw.fitbit.daily_activity_summary`. Azure Blob Storage typically charges based on volume and classes of operations, in addition
  to the retention cost.
</Warning>


# Google Cloud Pub/Sub
Source: https://docs.junction.com/webhooks/etl-pipelines/google-cloud-pubsub

Stream Junction events to Google Cloud Pub/Sub as an ETL pipeline destination, with message attributes, compression, and ordering details.

<Info>
  ETL Pipelines integration is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

## Event Structure

Each event is published with these Cloud Pub/Sub message attributes:

| Type         | Key               | Value                                                                                                |
| ------------ | ----------------- | ---------------------------------------------------------------------------------------------------- |
| Attribute    | `event-type`      | Junction event type, e.g., `daily.data.activity.created`                                             |
| Attribute    | `blob-type`       | `json`: UTF-8 JSON document                                                                          |
| Attribute    | `blob-codec`      | `gzip`: The blob is compressed by gzip. `null` or absent: The blob is uncompressed.                  |
| Attribute    | `idempotency-key` | The event delivery ID; only unique amongst all events from the same user and of the same event type. |
| Ordering Key | -                 | Junction User ID (If you have enabled Event Ordering)                                                |

<Info>
  Payloads smaller than 1KiB would not be compressed. You must check `blob-codec` to identify if decompression of
  the message payload blob is necessary.
</Info>

<Info>
  At this time, Junction publishes exclusively JSON blobs (`blob-type == json`).

  Having said that, you are strongly encouraged to check for and drop any events with unrecognized `blob-type` and `blob-codec`.
</Info>

## Pub/Sub event ordering

If you wish to receive events for each Junction user in their publication order, Junction supports Pub/Sub Event Ordering, and uses the Junction User ID as the event ordering key.

Junction publishes all events through the `us-central1` and `europe-west1` PubSub regional endpoints.

<Warning>
  Pub/Sub Exactly Once Delivery is **discouraged** from being used together with Event Ordering. Our own trial suggested that the combination would struggle to move a high volume of events.
</Warning>

## IAM Permissions

Grant the relevant IAM Principals on your topic with these pre-defined roles:

* Pub/Sub Publisher (`roles/pubsub.publisher`)
* Pub/Sub Viewer (`roles/pubsub.viewer`)

Or more specifically these permissions if you wish to create a custom IAM role with a minimized grant:

* `pubsub.topics.get`
* `pubsub.topics.publish`

| Environment | Region | Service Account                                                                                                                     |
| ----------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| Sandbox     | US     | [customer-topics-us@vital-sandbox.iam.gserviceaccount.com](mailto:customer-topics-us@vital-sandbox.iam.gserviceaccount.com)         |
| Sandbox     | Europe | [customer-topics-eu@vital-sandbox.iam.gserviceaccount.com](mailto:customer-topics-eu@vital-sandbox.iam.gserviceaccount.com)         |
| Production  | US     | [customer-topics-us@vital-prod-307415.iam.gserviceaccount.com](mailto:customer-topics-us@vital-prod-307415.iam.gserviceaccount.com) |
| Production  | Europe | [customer-topics-eu@vital-prod-307415.iam.gserviceaccount.com](mailto:customer-topics-eu@vital-prod-307415.iam.gserviceaccount.com) |

## Configuration

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

You can manage your Google Cloud Pub/Sub destination through the Org Management API
[Team ETL Pipeline](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) endpoints.

A basic configuration would look like this:

```json theme={null}
{
  "team_id": "TEAM_ID",
  "preferences": {
    "enabled": ["cloud_pubsub"],
    "preferred": "cloud_pubsub"
  },
  "cloud_pubsub": {
      "project": "my-gcp-project-123456",
      "topic": "event-ingestion",
      "message_ordering": true
  },
  "push_historical_data": true
}
```


# ETL Pipelines
Source: https://docs.junction.com/webhooks/etl-pipelines/overview

Use ETL Pipelines to stream Junction events to supported destinations over authenticated TLS as a scalable alternative to webhooks.

<Info>
  ETL Pipelines integration is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

With ETL Pipelines, Junction streams all [Junction events](/webhooks/event-structure) continuously to a supported destination
over direct authenticated TLS connections. Compared to [Webhooks](/webhooks/introduction), this simplifies your operational
complexity by removing the need to operate public unauthenticated HTTPS endpoints for receiving incoming Webhooks.

<CardGroup>
  <Card title="Scalable" icon="arrow-up-right-dots">
    Offload the pressure on your public HTTP services.
  </Card>

  <Card title="Secure" icon="shield">
    Events are published through authenticated channels over TLS.
  </Card>

  <Card title="Compressed" icon="gift">
    Data events larger than 1 KiB are compressed before publication.
  </Card>

  <Card title="Ordered" icon="traffic-cone">
    Publication order can be preserved for select destination types.
  </Card>
</CardGroup>

## Destinations

The following destinations are supported:

<Card href="/webhooks/etl-pipelines/google-cloud-pubsub" title="Google Cloud Pub/Sub" icon="google" />

<Card href="/webhooks/etl-pipelines/rabbitmq" title="RabbitMQ" icon="rabbit" />

<Card href="/webhooks/etl-pipelines/azure-event-hubs" title="Azure Event Hubs" icon="microsoft" />

## Event Schema

Webhooks, ETL Pipelines and our API endpoints all share the same JSON object schema. Webhooks and ETL Pipelines use
identical JSON event payload structure.

Check out our [Event Catalog](/event-catalog).

## Features

### Data compression

Junction does not compress payload blobs that are smaller than 1 KiB. Please refer to the destination-specific
documentation on how to detect a compressed blob versus an uncompressed blob.

### Static outbound IPs

Junction establishes connections to your ETL Pipeline destination through a static pool of IPs. Contact Junction support to
obtain the up-to-date static IP list.

### Double writing for migration

To ensure smooth switchover from Webhooks to your new ETL Pipelines destination, Junction supports double writing events
to the combination of:

* all your Webhook endpoints; and
* your ETL Pipelines destination.

<Info>
  This is not a proper fan-out feature, and is only intended to aid customers in evaluating and one-off migrating
  between destinations.

  There can only be one *preferred* destination, and the secondary destination(s) do not enjoy the same reliability
  guarantee as the *preferred* one.
</Info>

### Pushed Historical Data

When you use ETL Pipelines, Junction can *push* all the historical data as `daily.data.*` data events to your ETL Pipelines
destination.
The data-less `historical.data.*.created` event would still be emitted to denote the completion of historical pull.

## Configuring ETL Pipelines

You can change the ETL Pipelines configuration on your Teams at any time through the
[Set Team ETL Pipelines](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) endpoint
of the Org Management API.

Your new configuration is typically active as soon as the endpoint has acknowledged your request.

Refer to the destination-specific documentation for configuration examples.

<Info>
  Feel free to reach out to Junction support if you need assisted setup, or if you need a Junction Management Key to access the Org Management API.
</Info>


# RabbitMQ
Source: https://docs.junction.com/webhooks/etl-pipelines/rabbitmq

Stream Junction events to RabbitMQ as an ETL pipeline destination, with details on message headers, routing keys, and compression behavior.

<Info>
  ETL Pipelines integration is available for [the Scale plan](https://tryvital.io/pricing).
</Info>

## Event Structure

Each event is published with these RabbitMQ message custom headers:

| Key                | Value                                                                          |
| ------------------ | ------------------------------------------------------------------------------ |
| Routing Key        | Junction event type, e.g., `daily.data.activity.created`                       |
| `Content-Type`     | application/json: UTF-8 JSON document                                          |
| `Content-Encoding` | gzip: The blob is compressed by gzip. null/absent: The blob is uncompressed.   |
| Data               | Encoded JSON blob. This may or may not be compressed — see `Content-Encoding`. |

<Info>
  Payloads smaller than 1KiB would not be compressed. You must check `Content-Encoding` to identify if
  decompression of the message payload blob is necessary.
</Info>

<Info>
  At this time, Junction publishes exclusively JSON blobs (`Content-Type == application/json`).

  Having said that, you are strongly encouraged to check for and drop any events with unrecognized `Content-Type` and `Content-Encoding`.
</Info>

## Broker authentication

Junction only supports password authentication at this time.

Junction publishes events with the following settings:

* Publisher Confirm mode is required
* Events are published with the Mandatory flag but without the Immediate flag.
* Events are published with Event Type as the Routing Key.

Please get in touch if these need to be configured differently for your RabbitMQ exchange.

## Configuration

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

You can manage your RabbitMQ destination through the Org Management API
[Team ETL Pipeline](/api-reference/org-management/team-etl-pipeline/upsert-team-etl-pipelines) endpoints.

A basic configuration would look like this:

```json theme={null}
{
  "team_id": "TEAM_ID",
  "preferences": {
    "enabled": ["rabbitmq"],
    "preferred": "rabbitmq"
  },
  "rabbitmq": {
      "uri": "amqps://...",
      "exchange": "default"
  },
  "push_historical_data": true
}
```


# Event Structure
Source: https://docs.junction.com/webhooks/event-structure

Reference for the standard webhook event payload structure including event_type, data, team_id, user_id, and client_user_id fields.

All our webhook events have the following standard structure:

* A top-level `event_type` field, specifying the payload type contained in the `data` field.
* A top-level `data` field containing the event-specific payload.
* A set of top-level fields describing the event subject:
  * `team_id`: Your Junction Team ID.
  * `user_id`: The unique Junction User ID.
  * `client_user_id`: Your application user reference you declared for [the Junction User](/api-reference/user/create-user).

Schemas and examples are available through [the Event Catalog tab in the Webhooks section of our Web app](https://app.junction.com).

<CodeGroup>
  ```json Basic event structure theme={null}
  {
    "data": {
      # ... event specific data
    },
    "event_type": "daily.data.glucose.created",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```

  ```json Example: Provider connection created theme={null}
  {
    "data": {
      "source": {
        "logo": "https://storage.googleapis.com/vital-assets/freestyle.png",
        "name": "Freestyle Libre BLE",
        "slug": "freestyle_libre_ble"
      },
      "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
    },
    "event_type": "provider.connection.created",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```
</CodeGroup>

## Wearable data events

Wearable data events have the following extended standard structure:

* An `event_type` field for differentiating the payload contained in the data field.
* A `data` field, which comprises:
  * The UUID of the Junction user for which this event is intended — note that this is not the `client_user_id` of the Junction user. (JSON path: `$.data.user_id`)
  * The metadata about the data source of this event (JSON path: `$.data.source`)
    * `name`: The human-readable name of the source
    * `slug`: The stable identifier Junction assigned to the source — see [Supported providers](/wearables/providers/introduction) for the complete list.
    * `logo`: The URL to an icon representing the source.
  * Any number of event-specific fields.

```json Wearable data event structure theme={null}
{
  "data": {
    # ... event specific fields

    # Common wearable data event fields
    "source": {
      "logo": "https://path/to/example",
      "name": "Fitbit",
      "slug": "fitbit"
    },
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
  },
  "event_type": "daily.data.glucose.created",
  "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
  "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
  "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
}
```

### Summary types

Each time a [summary object](/wearables/providers/resources) is created or updated, Junction emits a data event for the occurrence.
The object is made available directly at the top-level `data` field (JSON path: `$.data`), alongside the standard fields listed above.

<Note>
  Each summary is uniquely identified by its ID (`$.id`).

  Given the same ID, the latest version of a summary you received **replaces** all its previous versions.
</Note>

<CodeGroup>
  ```json Summary data event structure theme={null}
  {
    "data": {
      # ... All summary object fields

      # Common wearable data event fields
      "source": {
        "logo": "https://path/to/example",
        "name": "Fitbit",
        "slug": "fitbit"
      },
      "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
    },
    "event_type": "daily.data.activity.created",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```

  ```json Example: Activity updated theme={null}
  {
    "data": {
      "calendar_date": "2023-05-17",
      "calories_active": 134,
      "calories_total": 1006,
      "daily_movement": 1162,
      "date": "2023-05-17T00:00:00+00:00",
      "distance": 1162,
      "floors_climbed": 2,
      "high": null,
      "id": "715dd6a5-0c67-471c-b5fe-9681a980a2c2",
      "low": null,
      "medium": null,
      "steps": 1496,
      "timezone_offset": 3600,

      # Common wearable data event fields
      "source": {
        "logo": "https://storage.googleapis.com/vital-assets/apple_health.png",
        "name": "Apple HealthKit",
        "slug": "apple_health_kit"
      },
      "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
    },
    "event_type": "daily.data.activity.updated",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```
</CodeGroup>

### Timeseries types (except for Workout Streams)

Each time a batch of [timeseries samples](/wearables/providers/resources) has been created or updated, Junction emits a data event for the batch of samples.
The samples are made available under an inner `data` field under the top-level `data` field (JSON path: `$.data.data`).

<Note>
  Each timeseries sample is uniquely identified by a compound key of:

  * the timeseries resource type;
  * the source provider;
  * the [Source Type](/wearables/providers/data-attributions#source-type); and
  * the sample timestamp.

  Given the same compound key, the latest value of a sample you received **replaces** all the previous values.
</Note>

<Info>While most timeseries data are immutable, there have been exceptions of some sources aggregating some data types using time bucketing,
and some may even send updates ASAP on time buckets that have incomplete data. For example, activity timeseries data from Apple HealthKit
(integration in production) and Garmin (integration planned) do exhibit such behavior.<br /><br />These exceptions are the rationale behind the recommended deduplication semantics as stated above.</Info>

<CodeGroup>
  ```json Timeseries data event structure theme={null}
  {
    "data": {
      "data": [
        {
          "timestamp": "2023-05-16T10:00:00+00:00",
          "timezone_offset": 3600,
          "type": null,
          "unit": "count",
          "value": 237
        },
        {
          "timestamp": "2023-05-16T11:00:00+00:00",
          "timezone_offset": 3600,
          "type": null,
          "unit": "count",
          "value": 451
        },
        # ... other timeseries samples in the batch
      ],
      "source_id": 16,

      # Common wearable data event fields
      "source": {
        "logo": "https://storage.googleapis.com/vital-assets/apple_health.png",
        "name": "Apple HealthKit",
        "slug": "apple_health_kit"
      },
      "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
    },
    "event_type": "daily.data.steps.created",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```

  ```json Example: Calories Active created theme={null}
  {
    "data": {
      "data": [
        {
          "timestamp": "2023-05-17T07:00:00+00:00",
          "start": "2023-05-17T07:00:00+00:00",
          "end": "2023-05-17T08:00:00+00:00",
          "timezone_offset": 3600,
          "type": "",
          "unit": "kcal",
          "value": 39.518054167148996
        },
        {
          "timestamp": "2023-05-17T08:00:00+00:00",
          "start": "2023-05-17T08:00:00+00:00",
          "end": "2023-05-17T09:00:00+00:00",
          "timezone_offset": 3600,
          "type": "",
          "unit": "kcal",
          "value": 31.270257990822234
        },
        {
          "timestamp": "2023-05-17T09:00:00+00:00",
          "start": "2023-05-17T09:00:00+00:00",
          "end": "2023-05-17T09:01:00+00:00",
          "timezone_offset": 3600,
          "type": "",
          "unit": "kcal",
          "value": 5.733999999999999
        }
      ],
      "source_id": 16,

      # Common wearable data event fields
      "source": {
        "logo": "https://storage.googleapis.com/vital-assets/apple_health.png",
        "name": "Apple HealthKit",
        "slug": "apple_health_kit"
      },
      "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2"
    },
    "event_type": "daily.data.calories_active.created",
    "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
    "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
    "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
  }
  ```
</CodeGroup>

### Workout Streams

While Workout Streams are classified as a timeseries type, they contain a huge amount of timeseries data,
which cannot be delivered by our webhook message transport.

So events for Workout Streams are shallow, with only some minimal metadata about the workout. Successful
reception of the event indicates that the data are available to be read via our REST API,
e.g., [the Workout Stream endpoint](/api-reference/data/workouts/get-stream) `GET /v2/timeseries/workouts/{workout_id}/stream`.

```json Workout Stream created theme={null}
{
  "data": {
    "message": "Due to payload size limits, to access the workout stream, please use the /workouts/edc80dd0-8cc8-4ec8-8b80-931fd9a3309a/stream endpoint.",
    "provider_id": "51278201336",
    "sport": {
      "id": 57,
      "name": "Other",
      "slug": "other"
    },
    "workout_id": "fdc70dd0-8cc8-4ec8-8b80-121fd9a3302a",

    # Common wearable data event fields
    "source": {
      "logo": "https://storage.googleapis.com/vital-assets/fitbit.png",
      "name": "Fitbit",
      "slug": "fitbit"
    },
    "user_id": "bf6222fb-3b81-4201-9630-118bfee01e03"
  },
  "event_type": "daily.data.workout_stream.created",
  "user_id": "4a29dbc7-6db3-4c83-bfac-70a20a4be1b2",
  "client_user_id": "01HW3FSNVCHC3B2QB5N0ZAAAVG",
  "team_id": "6b74423d-0504-4470-9afb-477252ccf67a"
}
```


# Introduction
Source: https://docs.junction.com/webhooks/introduction

Introduction to Junction webhooks that push new data and connection lifecycle updates to your configured HTTP endpoints in real time.

With Webhooks and [ETL Pipelines](/webhooks/etl-pipelines/overview), Junction pushes any new data or updates to your endpoint
or your ETL destination as soon as they are discovered.

## Connection Created stage

When a user successfully connects a provider through [Junction Link](/wearables/connecting-providers/introduction), Junction sends a
Provider Connection Created event to acknowledge it:

<AccordionGroup>
  <Accordion title="Event Type">
    `provider.connection.created`
  </Accordion>

  <Accordion title="Example">
    Check out the [Provider Connection Created event](/event-catalog/provider.connection.created) in the Event Catalog.

    ```json Webhook Event theme={null}
    {
      "event_type": "provider.connection.created",
      "data": {
        "user_id": "4eca10f9-...",
        "provider": {
          "name": "Strava",
          "slug": "strava",
          "logo": "https://storage.googleapis.com/vital-assets/strava.png"
        },
        "resource_availability": { ... }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Historical Data Backfill stage

Junction also schedules jobs to backfill *historical* data upon connection establishment.

Historical data are data that exist prior to the connection establishment. Because this can [amount to months of data](/wearables/providers/introduction#historical-data-pull-range)
— and might potentially run into temporary provider-side API rate limits — these backfill jobs may take minutes to hours to complete.

<Note>
  You can inspect the status of historical data backfill jobs of each individual user through [Junction Dashboard](https://app.junction.com) or
  [Junction API](/api-reference/data/introspection/historical-pulls).
</Note>

Once the historical data backfill job of a specific [resource](/wearables/providers/resources) has **fully** completed, Junction sends a **Historical Pull Completion** event for the resource:

<AccordionGroup>
  <Accordion title="Event Type">
    `historical.data.{RESOURCE}.created`
  </Accordion>

  <Accordion title="Usage">
    This event is a data-less notification.

    Use the [Junction Data Access API](/api-reference/data/sleep/get-summary) to fetch the resource you are interested in.

    Constrain your API call using the information in the Historical Pull Completion event. For example, the event includes the
    [provider](/wearables/providers/introduction) slug as well as the full datetime range that Junction has looked through.
  </Accordion>

  <Accordion title="Example">
    Check out the [Historical Workout Data Pull Completion event](/event-catalog/historical.data.workouts.created) in the Event Catalog.

    ```json theme={null}
    {
      "event_type": "historical.data.workouts.created",
      "data": {
        "user_id": "e9e072e8-...",
        "start_date": "2020-06-21T08:23:01+00:00",
        "end_date": "2024-11-02T14:39:28+00:00",
        "provider": "zwift"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

<Note>
  [ETL Pipelines](/webhooks/etl-pipelines/overview) can opt in to receive historical data as *Junction Data Events*
  (as outlined below in the [Incremental Data stage](#incremental-data-stage) section).

  Note that Junction would still send the Historical Pull Completion event to signal the completion of the historical data backfill stage.
</Note>

## Incremental Data stage

Once the historical data backfill stage completes, Junction [monitors for new data and changes](/wearables/providers/update-frequency).

Whenever new data or changes are discovered, Junction sends them out as **Data Events** as soon as possible:

<Note>
  You can inspect the state of incremental data updates of each individual user through [Junction Dashboard](https://app.junction.com) or
  [Junction API](/api-reference/data/introspection/user-resources).
</Note>

<AccordionGroup>
  <Accordion title="Event Types">
    * Initial discovery — `daily.data.{RESOURCE}.created`
    * Subsequent updates — `daily.data.{RESOURCE}.updated`

    <Warning>
      Treat the `daily.data` prefix as a misnomer for *Incremental Data*. It **does not** imply a daily delivery schedule.
    </Warning>
  </Accordion>

  <Accordion title="Usage">
    Junction sends out *Data Events* as soon as we discover new or updated entries as reported by a provider. This also implies:

    1. Junction does not prioritize or aggregate Data Events across providers:

       * You may receive multiple events of the same type from a single provider, because the provider supports multiple
         sources, devices or apps (e.g., iPhone sleeps and Apple Watch sleeps from Apple HealthKit);
       * You may receive multiple events of the same type from multiple providers, because the user has connected to multiple
         providers.

    2. There is no standard Data Event delivery frequency or schedule across providers *and* resources:

       * Some may be sent only once per day;
       * Some may be sent multiple times at irregular times;
       * Some may appear to be sent on a regular schedule throughout the day.

    Your system typically would ingest relevant Junction Data Events into a database.

    You can query the database with any business rules and query constraints suitable to your product experience. This may include
    your own data prioritization rules based on, e.g., [Source Type](/wearables/providers/data-attributions#source-type).

    <Note>
      Junction also offers the [Junction Sense Aggregation API](/sense/overview) for your data aggregation
      and consolidation needs.
    </Note>
  </Accordion>

  <Accordion title="Example">
    Check out the [Workout Created event](/event-catalog/daily.data.workouts.created) in the Event Catalog.

    <Warning>The event structure and deduplication semantics of [summary types](/wearables/providers/resources) and [timeseries types](/wearables/providers/resources) have a few key differences. Please refer to [Event Structure](/webhooks/event-structure) for a more in-depth discussion.</Warning>

    ```json theme={null}
    {
      "event_type": "daily.data.workouts.created",
      "data": {
        "id": "c90bd6fb-...",
        "average_hr": 198,
        "distance": 8544,
        "time_start": "2024-11-24T22:57:01+00:00",
        "time_end": "2024-11-25T04:57:31+00:00",
        "calories": 4470,
        "hr_zones": [8279],
        "user_id": "ab3247dc-...",
        ...,
        "provider_id": "51278201336",
        "source": {
          "provider": "zwift",
          "type": "unknown"
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Deep Dive into Junction Webhooks

<div>
  <iframe />
</div>

### Managing Webhooks

You can manage your team's webhooks programmatically using our [Webhooks API](/api-reference/org-management/team-webhook/list). This API allows you to:

* **CRUD** (create/read/update/delete) your webhooks
* Manage webhook **headers**
* Update webhook **secrets**

### Authenticating Webhooks

You can verify the webhook signature. Each webhook request we send includes these three headers:

* `svix-id`
* `svix-timestamp`
* `svix-signature`

You'll also need your webhook secret, which you can find in your Junction Dashboard > Webhooks > Endpoint tabs under "Signing Secret".

**Option 1:** Use a Svix SDK

If you're using a compatible language, the easiest way is to utilize the [official Svix SDKs](https://docs.svix.com/receiving/verifying-payloads/how), which handle signature verification for you.

**Option 2:** Manual Verification

If the Svix SDKs are not an option, you can manually verify them following the instructions [here](https://docs.svix.com/receiving/verifying-payloads/how-manual).


# Retry Policy
Source: https://docs.junction.com/webhooks/retry-policy

Understand the Junction webhook retry schedule with exponential backoff across eight attempts, from immediate retry up to 10 hours between attempts.

Junction attempts to deliver each webhook message based on a retry schedule with exponential backoff.

## Delivery Schedule

Each message is retried according to the following fixed schedule after a failed attempt:

1. Immediately
2. 5 seconds
3. 5 minutes
4. 30 minutes
5. 2 hours
6. 5 hours
7. 10 hours
8. After an additional 10 hours

**Notes:**

* Retries **always occur in the order listed above**, with each period starting only after the previous attempt fails.
* If an endpoint is removed or disabled, further delivery attempts to that endpoint will be stopped.
* Example: If a message fails four times before eventually succeeding, it will be delivered roughly **35 minutes and 5 seconds** after the initial attempt (Immediate + 5 seconds + 5 minutes + 30 minutes).

## Indicating successful delivery

The way to indicate that a webhook has been processed is by returning a `2xx` (status code `200-299`) response to the webhook message within a reasonable timeframe (15s). Any other status code, including `3xx` redirects, is treated as a failure.

## Failed delivery handling

After the conclusion of the above attempts, the message will be marked as `Failed` for this endpoint, and you will get a webhook of type `message.attempt.exhausted` notifying you of this error.

## Disabling failing endpoints

If all attempts to a specific endpoint fail for a period of 5 days, the endpoint will be disabled. The clock only starts after multiple deliveries failed within a 24-hour span, with at least 12 hours difference between the first and the last failure.

## Manual retries

You can also use the webhook portal in the dashboard to manually retry each message at any time, or automatically retry ("Recover") all failed messages starting from a given date.