> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/unified-api/user-authentication/implementation-terra-widget.md).

# Implementation (Terra widget)

The Terra widget is a hosted screen where your end user picks their data provider and logs in. You create a **widget session** from your backend with one API call, then send the user to the URL it returns.

{% hint style="info" %}
**Already live with `/auth/generateWidgetSession`?** It keeps working today, with deprecation planned for 3 November 2026. That endpoint is documented on the [Legacy widget](/unified-api/user-authentication/implementation-terra-widget/implementation-terra-widget-legacy.md) page. When you want the redesigned widget, the [migration notes](#migrating-from-auth-generatewidgetsession) at the bottom of this page cover the handful of differences.
{% endhint %}

## 1. Create a widget session

From your **backend**, call `POST https://access.tryterra.co/api/widget/session` with your `dev-id` and `x-api-key` headers.

{% tabs %}
{% tab title="cURL" %}
{% code title="Command Line" %}

```bash
curl --request POST --url https://access.tryterra.co/api/widget/session \
  --header 'dev-id: <YOUR-DEV-ID>' \
  --header 'x-api-key: <YOUR-API-KEY>' \
  --header 'Content-Type: application/json' \
  --data '{
      "reference_id": "my_first_connection",
      "auth_success_redirect_url": "https://example.com/success",
      "auth_failure_redirect_url": "https://example.com/failure"
  }'
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}

```python
import requests

response = requests.post(
    "https://access.tryterra.co/api/widget/session",
    headers={"dev-id": "<YOUR-DEV-ID>", "x-api-key": "<YOUR-API-KEY>"},
    json={
        "reference_id": "my_first_connection",
        "auth_success_redirect_url": "https://example.com/success",
        "auth_failure_redirect_url": "https://example.com/failure",
    },
)

widget_url = response.json()["url"]
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
import axios from "axios";

const { data } = await axios.post(
  "https://access.tryterra.co/api/widget/session",
  {
    reference_id: "my_first_connection",
    auth_success_redirect_url: "https://example.com/success",
    auth_failure_redirect_url: "https://example.com/failure",
  },
  { headers: { "dev-id": "<YOUR-DEV-ID>", "x-api-key": "<YOUR-API-KEY>" } }
);

const widgetUrl = data.url;
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
**Pro tip**: Use <mark style="color:green;">`reference_id`</mark> to pass your own end user's ID. It comes back on the [authentication event](/unified-api/user-authentication/handling-authentication-events.md) and on the success redirect, so you can match the Terra [User](https://docs.tryterra.co/reference/health-and-fitness-api/core-concepts#user) to your end user without any extra lookups.
{% endhint %}

### Request body

All fields are optional. With no `providers` field, the widget shows the data sources you have enabled in your [Terra Dashboard](https://dashboard.tryterra.co/).

| Field                       | Type      | Description                                                                                                                                                               |
| --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reference_id`              | string    | Your identifier for the end user (a user ID or email on your system).                                                                                                     |
| `providers`                 | string    | Comma-separated list of providers to show, in the order given, e.g. `"GARMIN,OURA,FITBIT"`. A single provider skips the selection screen and goes straight to that login. |
| `auth_success_redirect_url` | string    | Where to send the user after a successful connection.                                                                                                                     |
| `auth_failure_redirect_url` | string    | Where to send the user after a failed or abandoned connection.                                                                                                            |
| `show_disconnect`           | boolean   | Show a disconnect button next to providers already connected under this `reference_id`. Defaults to `true` when a `reference_id` is given; set `false` to hide it.        |
| `multi_auth`                | boolean   | Keep the user on the widget after each successful connection so they can connect several providers in one session. Default `false`.                                       |
| `connected_uids`            | string\[] | Terra `user_id`s already connected for this end user; their providers show as connected with a disconnect option. Unknown IDs return a `400`.                             |
| `bypass_feedback`           | boolean   | Set `false` to keep the user on the widget's own result screen instead of redirecting them to your URL immediately.                                                       |
| `use_terra_avengers_app`    | boolean   | Allow Apple Health connections through the Terra mobile app. Default `false`.                                                                                             |
| `apple_app_url`             | string    | URL of your own iOS app to hand Apple Health connections to, instead of the Terra mobile app.                                                                             |
| `samsung_app_url`           | string    | URL of your own Android app to hand Samsung Health connections to.                                                                                                        |

The widget is shown in the end user's own language, negotiated from their browser's `Accept-Language` header (English fallback). There is nothing to set on the request.

***

## 2. Parse the widget URL from the response

A successful call returns `201` with the **widget URL** in the <mark style="color:purple;">`"url"`</mark> field.

{% code title="JSON" lineNumbers="true" %}

```json
{
  "session_id": "23dc2540-7139-44c6-8158-f81196e2cf2e",
  "url": "https://access.tryterra.co/widget/session/23dc2540-7139-44c6-8158-f81196e2cf2e",
  "status": "success",
  "expires_in": 86400
}
```

{% endcode %}

The URL is valid for `expires_in` seconds (24 hours). Create a fresh session each time a user starts the connect flow rather than storing URLs.

| Status | Meaning                                                                                                                         |
| ------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `201`  | Session created; `url` is ready to open.                                                                                        |
| `400`  | A field is malformed. `message` explains which; `invalid_user_ids` lists any `connected_uids` that do not exist.                |
| other  | `status` is `"error"` and `message` explains why. Most often the `dev-id` / `x-api-key` pair is not valid for this environment. |

***

## 3. Open the widget URL

Pass the <mark style="color:purple;">`"url"`</mark> to your client side and open it in:

* an **in-app browser**, if using a mobile app,
* or a **new tab**, if using a web app.

Your [end user](https://docs.tryterra.co/reference/health-and-fitness-api/core-concepts#end-user) picks a provider, logs in, grants permissions, and is sent to your `auth_success_redirect_url` with their Terra `user_id` and your `reference_id` as query parameters. At the same time, an authentication event is sent to your destination: see [Handling authentication events](/unified-api/user-authentication/handling-authentication-events.md).

{% hint style="info" %}
**Make it yours.** The widget's name, logo and colours are set in your Terra Dashboard under **Authentication**, where you can also preview the widget and test a connection before shipping. The permissions list can be [customised through your Terra Dashboard](/unified-api/integration-setup/customising-data-types.md) too.
{% endhint %}

You can find a list of all your **authenticated users** in:

* Your [Terra Dashboard > Users](https://dashboard.tryterra.co/dashboard/users)
* Or by using [the /subscriptions endpoints](https://docs.tryterra.co/reference/)

***

## ❌ Common Mistakes

{% hint style="danger" %}

### Common Mistakes and Best Practices

* Do not expose your API credentials. Always create the widget session from your **backend**.
* Do not call the API from your **frontend**, as this will lead to a **CORS error**.
* Do not use **WebView** or **iFrame** for the authentication flow. Using them poses security risks due to the invisible URL bar, meaning that the user cannot know the domain onto which they are entering their username & password. Providers may completely block authentication leading to an error during the flow. Instead, use a **new tab** or an **in-app browser** to open the URL.
* Do not reuse widget URLs across users or sessions. Each `url` belongs to one `reference_id`; create a new session per connect attempt.
  {% endhint %}

***

## Migrating from `/auth/generateWidgetSession`

Existing integrations keep working today, and the legacy endpoint is planned for deprecation on 3 November 2026. Accounts created after 3 September 2026 cannot use it at all. If you want the redesigned widget, the changes are small:

| Legacy (`/v2/auth/generateWidgetSession`)                    | Redesigned widget (`/api/widget/session`)                                                                              |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `POST https://api.tryterra.co/v2/auth/generateWidgetSession` | `POST https://access.tryterra.co/api/widget/session`, with the same `dev-id` / `x-api-key` headers.                    |
| `"language": "en"` in the body                               | Remove it. The widget follows the end user's browser language automatically.                                           |
| `"providers": "multi-auth"`                                  | Send `"multi_auth": true` instead, alongside your `providers` list.                                                    |
| `"providers": "ALL"`                                         | Omit `providers`. The widget then shows exactly the data sources you have enabled in your Dashboard.                   |
| Response `expires_in: 900`                                   | Response `expires_in: 86400`. Otherwise the response shape (`session_id`, `url`, `status`, `expires_in`) is unchanged. |
| Widget styled from the legacy dashboard settings             | Styled from **Dashboard > Authentication**. Your name and logo carry over; bespoke legacy themes do not.               |

Everything else works the same way: `reference_id`, redirect URLs, `show_disconnect`, `connected_uids`, `use_terra_avengers_app`, and the authentication events you receive.
