> 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/mobile-only-sources/android-kotlin.md).

# Android (Kotlin)

## Overview

This guide will walk you through the necessary steps to use the Terra SDK with Android-based integrations in your Android app. It covers everything from SDK initialization, user connection, permission handling, and background data updates.

The TerraAndroid SDK supports the following integrations:

* Samsung Health
* Health Connect

## 1. Install the SDK

#### **1. Install Terra in `build.gradle`**

* Inside the dependencies section of your `build.gradle` file, add the following line of code.

<pre class="language-gradle" data-title="build.gradle"><code class="lang-gradle"><strong>implementation 'co.tryterra:terra-android:{VERSION_NUMBER}'
</strong></code></pre>

* You can find the latest version number on [Maven Central](https://central.sonatype.com/artifact/co.tryterra/terra-android/).

#### 2. Sync your project with **`build.gradle`**

Sync your project with Gradle by clicking on the "**Sync Project with Gradle Files**" button in the toolbar. This will download the **TerraAndroid SDK** and make it available to your project.

{% hint style="info" %}

#### Requirements

* Your app build config must be for **Android 28 (minSDK 28)** and above.
* The device must have the required app installed, e.g. Health Connect or Samsung Health
  {% endhint %}

#### 3. Configure Health Permissions

{% tabs %}
{% tab title="Apply to use Samsung Health" %}
Terra has a privileged partnership with Samsung that gives you direct access to the Samsung Health SDK — no Health Connect intermediary needed. This is the recommended route for Samsung devices.

**1. Apply for Samsung Health access**

Submit your application through the [Samsung Health partnership portal](https://developer.samsung.com/SHealth/business-partner/m48wvqi1mt9w2w4c).

{% hint style="info" %}
While waiting for approval, you can start development immediately using the **Health Connect** route (see the other tab). Switching to Samsung direct later only changes the dependency you pull — no code changes needed.
{% endhint %}

**2. Install the Samsung-direct SDK**

After approval, replace `co.tryterra:terra-android` with the separate Samsung-direct artifact:

```gradle
implementation 'co.tryterra:android-sdk:{VERSION_NUMBER}'
```

Find the latest version on [Maven Central](https://central.sonatype.com/artifact/co.tryterra/android-sdk). Note that this is a separate artifact, not a tagged version of `terra-android` — your `initConnection(Connections.SAMSUNG, ...)` call and the rest of your code stay exactly the same.

**3. Add ProGuard rule (required for release builds)**

Add this to your `proguard-rules.pro`:

```proguard
-keep class com.samsung.android.** { *; }
```

Without this, R8 minification will strip Samsung SDK classes and cause runtime crashes in production.

**4. Requirements**

* **Samsung Health** app must be installed on the device
* **Android 28 (minSDK 28)** and above
* Enable **Developer Mode** in Samsung Health on test devices (Settings > About Samsung Health > tap version number repeatedly)
* No additional manifest or Gradle changes needed — the Terra SDK handles everything
  {% endtab %}

{% tab title="Apply to use Health Connect" %}
**1. In the Health Connect app**

Give all permissions between the apps you wish to read from (e.g. **Samsung Health, Google Fit, etc**) & Health Connect.

**2. Add Health Connect capability to your app**

Include the permission tags under the Activity you wish to link the user to when they click the **privacy policy** link in the Health Connect permission screen. Here are the steps:

1. Go to your Android App's `AndroidManifest.xml`
2. Go to your **Privacy Policy** <mark style="color:purple;">`<activity>`</mark> and include the following tags under this <mark style="color:purple;">`<activity>`</mark> tag.

"Your Android manifest needs to have an Activity that displays your app's **privacy policy**, which is your app's rationale of the requested permissions, describing how the user's data is used and handled." — Health Connect.

<pre class="language-xml" data-title="AndroidManifest.xml" data-line-numbers><code class="lang-xml"><strong>&#x3C;intent-filter>
</strong>  &#x3C;action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
 &#x3C;/intent-filter>
  
&#x3C;intent-filter>
   &#x3C;action android:name="android.intent.action.VIEW_PERMISSION_USAGE"/>
  &#x3C;category android:name="android.intent.category.HEALTH_PERMISSIONS"/>
&#x3C;/intent-filter>
</code></pre>

{% hint style="warning" %}

#### Apply for Health Connect access

Before going **live** (release), you will need to apply for permissions to access the Health Connect API with Google.

1. Use this [application form](https://developer.android.com/health-and-fitness/guides/health-connect/publish/declare-access#explain-use-data-types).
2. For each permission which you are ***not*** using, please add the following lines to your AndroidManifest.xml

```xml
<uses-permission android:name="android.permission.health.READ_HEART_RATE" tools:node="remove"/>
```

with `android.permission.health.XXX` for each permission you aren't using
{% endhint %}
{% endtab %}
{% endtabs %}

***

## 2. Initialize the SDK

The initialization only needs to be done **once** **on** **app** **start**, (e.g. in your `MainActivity` file or equivalent), **and** **every time** the app is brought into the foreground. This ensures that the SDK is properly set up and ready to be used.

{% hint style="warning" %}
The SDK should be initialized every time your app is started.

This is a necessary prerequisite for other SDK functions to run as expected
{% endhint %}

#### **Step 1: Import `Terra` and `TerraManager`**

In your Android Studio project, you should now be able to import classes from the Terra SDK.

{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
import co.tryterra.terra.Terra
import co.tryterra.terra.TerraManager
```

{% endcode %}

#### **Step 2: Get a `TerraManager` Instance**

To do this, run the [Terra manager initialization function](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin#initialization-of-terra-manager) as below:

{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
import co.tryterra.terra.Terra
import co.tryterra.terra.TerraManager

import java.lang.IllegalStateException

class MainActivity : AppCompatActivity() {
    private lateinit var terra: TerraManager

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        Terra.instance("YOUR DEV ID", "REFERENCE ID", this) { manager, error ->
            error?.let {
                throw IllegalStateException("🤯: Error initialising terra ${it.message}")
            }
            terra = manager
        }
    }
}
```

{% endcode %}

(**N.B** This call is asynchronous, please ensure this is complete before using the manager).

***

## 3. Connect to a User

Once Terra is initialized, you can create a connection (e.g. Samsung Health or Health Connect).

Call [`terra.initConnection`](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin#initconnection) from the `TerraManager` instance you've created above to have the corresponding permission screen pop up.

#### 1. Call [`initConnection()`](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin#initconnection)

This method triggers a permission popup and establishes the connection.

From the `TerraManager` instance, call [`initConnection()`](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin#initconnection) with the following arguments:

* `type`: Specify the connection type — `Connections.SAMSUNG` for Samsung Health or `Connections.HEALTH_CONNECT` for Health Connect
* `token`: A **one-time authentication token** generated from your **backend** **server** using Terra API. This ensures secure communication between your app and the Terra servers.
* `customPermissions`: A **set of permissions** that define what data you want to request (e.g., heart rate, steps). If empty, it defaults to all available permissions. See [Permissions mapping](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/permissions-mapping) for every value and the Health Connect (or Samsung) data type it requests.
* `schedulerOn`: Enables background delivery, so Terra keeps syncing while your app is in the background. Defaults to `true`. On Health Connect connections, Terra also requests Health Connect's background-read permission as part of this flow. See [Background Delivery setup](#id-5.-background-delivery-setup) below.

To learn more about these parameters and their different options, check the [Android SDK Reference.](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin)

{% tabs %}
{% tab title="Samsung Health" %}
{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

import co.tryterra.terra.Terra
import co.tryterra.terra.TerraManager
import co.tryterra.terra.enums.Connections

import java.lang.IllegalStateException

class MainActivity : AppCompatActivity() {
    private lateinit var terra: TerraManager

    override fun onCreate(savedInstanceState: Bundle?) {
        // Creation of Terra Manager as in Step 2!
    }

    fun initialiseUserConnection() {
        if (!this::terra.isInitialized){
            throw IllegalStateException("🤯: Terra Manager not initialised yet")
        }
        val token = "your_generated_token_from_backend"

        terra.initConnection(
            connection = Connections.SAMSUNG,
            token = token,
            context = this,
            customPermissions = setOf(),
            schedulerOn = true,
            startIntent = null
        ) { success, error ->
            error?.let {
                throw IllegalStateException("🤯: ${it.message}")
            }
            Log.i("MainActivity", "Auth Success status: $success")
        }
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Health Connect" %}
{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

import co.tryterra.terra.Terra
import co.tryterra.terra.TerraManager
import co.tryterra.terra.enums.Connections

import java.lang.IllegalStateException

class MainActivity : AppCompatActivity() {
    private lateinit var terra: TerraManager

    override fun onCreate(savedInstanceState: Bundle?) {
        // Creation of Terra Manager as in Step 2!
    }

    fun initialiseUserConnection() {
        if (!this::terra.isInitialized){
            throw IllegalStateException("🤯: Terra Manager not initialised yet")
        }
        val token = "your_generated_token_from_backend"

        terra.initConnection(
            connection = Connections.HEALTH_CONNECT,
            token = token,
            context = this,
            customPermissions = setOf(),
            schedulerOn = true,
            startIntent = null
        ) { success, error ->
            error?.let {
                throw IllegalStateException("🤯: ${it.message}")
            }
            Log.i("MainActivity", "Auth Success status: $success")
        }
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
**`initConnection` only needs to be called a single time.**

**Health Connect** prohibits the permission popup to appear more than once for any given permission, so calling `initConnection` more than once will result in no action at all

The only case where it would re-appear is if:

a. you call initConnection with **an expanded set of `customPermissions`**

b. the **app is deleted & reinstalled.**

c. **a permission that was not granted to use on release by Google has been requested by the app**
{% endhint %}

To be able to call the `initConnection()` method, you need to pass a `token` as an argument.

This `token` is a **single-use token** created to ensure the authentication endpoint for creating a connection (and connecting the SDK to Terra's servers) does not get abused.

Generate the token with this endpoint <mark style="color:orange;">`POST`</mark>`https://api.tryterra.co/v2/auth/generateAuthToken` . Make sure to call it with your Terra `x-api-key` and `dev-id` in the headers from **your backend server.** After you generate the token, provide the response to your client side using your own logic.

{% code title="CLI" %}

```bash
curl --request POST \
  --url https://api.tryterra.co/v2/auth/generateAuthToken \
  --header 'dev-id: <YOUR-DEV-ID>' \
  --header 'x-api-key: <YOUR-API-KEY>'
```

{% endcode %}

Go to the **SDK Reference** to find more details on the [Generate the Mobile SDK Auth Token **API Endpoint**](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references)**.**

{% hint style="warning" %}

* During the development phase, it it acceptable to place this call on the **client** side, exposing your API key in your mobile app.
* For a production implementation, **DO NOT** expose your API key this way, and make sure to **only** make this call from your **backend.**
  {% endhint %}

***

## 4. Validate the Connection

To ensure a connection is still valid on the client side, use the [`terra.getUserId()`](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin#getuserid) method. This function is synchronous and returns the `user_id` right away or null if none exists.

{% hint style="info" %}

## Always validate the connection before using the SDK

Check if a `user_id` exists right after **initializing the Terra SDK** to see if the connection still exists.

1. **Check if the User is Connected**
   1. If the function returns a user ID, the user is still connected, 🎉 keep vibing along!
   2. If the function returns <mark style="color:red;">`nil`</mark>, the user needs to reconnect.
2. **Re-connect if Needed**\
   If the connection is lost, you can call `terra.initConnection()` again to re-establish the connection.

Calling `terra.initConnection()` when the user is already connected or just needs a reconnection will re-establish the connection if necessary, and the user flow will remain uninterrupted.

On Apple Health, note that a `user_id` only tells you the connection exists on our side, not that your app still holds HealthKit access. Deleting an app clears its HealthKit authorization while the Terra connection survives, so the permission screen can still appear on the next `initConnection()` if iOS has not yet determined permissions for the current install.
{% endhint %}

***

## 5. Background Delivery setup

Background delivery keeps Health Connect data syncing while your app is in the background, rather than only while it is open. It requires `terra-android` **1.7.0 or later**, and we recommend **1.7.3 or later** — before that, the periodic sync could be scheduled even when the user had not granted the background-read permission, so it ran without being able to read.

Passing `schedulerOn = true` to `initConnection` (the default) is all that is needed. Terra requests Health Connect's background-read permission during the connection flow and schedules the periodic sync as soon as the user grants it.

{% hint style="info" %}
If you use the Samsung-direct `co.tryterra:android-sdk` artifact described in Step 1, it is on a separate version line and does not go through Health Connect — check with support before relying on background delivery there.
{% endhint %}

If the user declines that permission, background sync is not scheduled. Call `enableBackgroundDelivery` to prompt again:

{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
terra.enableBackgroundDelivery { success, error ->
    if (success) {
        Log.i("MainActivity", "Background delivery enabled")
    } else {
        Log.w("MainActivity", "Background delivery unavailable: ${error?.message}")
    }
}
```

{% endcode %}

To read the current state without prompting the user, call `isBackgroundDeliveryAvailable` (available from `terra-android` 1.7.3):

{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
terra.isBackgroundDeliveryAvailable { granted ->
    Log.i("MainActivity", "Background read granted: $granted")
}
```

{% endcode %}

It returns `false` if called before `initConnection`, which is indistinguishable from the user having declined — call it only once a connection exists.

{% hint style="info" %}
`enableBackgroundDelivery` reports failure through its `TerraError`:

* `BackgroundDeliveryPermissionDenied` — the user declined the prompt.
* `BackgroundDeliveryNotSupported` — the Health Connect SDK is not available on the device.
* `NotAuthenticated` — called before `initConnection`.
* `UnexpectedError` — the context you passed to Terra does not extend `AppCompatActivity`, so the permission request cannot be launched.
  {% endhint %}

***

Now you'll start receiving health data events **automatically** to your Data Destination (e.g. webhook)!

You can also request historical data to backfill, to verify that data exists, or as a fallback.

Check out the [Android SDK reference](/unified-api/mobile-only-sources/android-kotlin.md) for details about all the functions in the SDK.

***

## 📱 App Example

{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

import co.tryterra.terra.Terra
import co.tryterra.terra.TerraManager
import co.tryterra.terra.enums.Connections

import java.lang.IllegalStateException

class MainActivity : AppCompatActivity() {
    private lateinit var terra: TerraManager

    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        Terra.instance("YOUR DEV ID", "REFERENCE ID", this) { manager, error ->
            error?.let{it -> 
                throw IllegalStateException("🤯: Error initialising terra ${it.message}")
            }
            terra = manager
            this.initialiseUserConnection()
        }
    }

    fun initialiseUserConnection() {
        if (!this::terra.isInitialized){
            throw IllegalStateException("🤯: Terra Manager not initialised yet")
        }
        
        if (terra.getUserId(Connections.SAMSUNG) != null){
            // User already connected
            return
        }
        
        // Initialize the connection to Samsung Health
        terra.initConnection(
            connection = Connections.SAMSUNG,   // Connecting to Samsung Health
            token = "AUTH TOKEN",                      // Token passed from backend
            context = this,                  // The activity context
            customPermissions = setOf(),  // Custom permissions set (empty set defaults to all permissions)
            schedulerOn = true,                 // Background scheduler on
            startIntent = null                  // Deprecated - always set to null
        ) { success, error ->
            error?.let {
                throw IllegalStateException("🤯: ${it.message}")
            }
            // Connection successful!
            Log.i("MainActivity", "Auth Success status: $success")
        }
    }
}
```

{% endcode %}

***

## Disconnecting a user

In order to disconnect an SDK user, you may use [the same endpoint as for Web API-based integrations](https://docs.tryterra.co/reference/health-and-fitness-api/readme#auth-deauthenticateuser), called from your backend.

***

## Historical Data retrieval

You can request for historical data using one of the [data retrieval functions](https://docs.tryterra.co/reference/health-and-fitness-api/sdk-references/android-kotlin#data-retrieval).

You may set `toWebhook` to false if you wish for the callback function to return the data payload on the client side.

{% code title="MainActivity.kt" lineNumbers="true" %}

```kotlin
import co.tryterra.terra.enums.Connections
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.*

fun requestData() {
    val startDate = Date.from(Instant.now().minus(1, ChronoUnit.DAYS))
    val endDate = Date()

    // Request for yesterday and today's data
    terra.getDaily(
        type = Connections.SAMSUNG,
        startDate = startDate,
        endDate = endDate,
        toWebhook = false 
    ) { success, payload, error ->
        error?.let{
            throw IllegalStateException("🤯: Error requesting data ${it.message}")    
        }
        // Process payload here
    }
}
```

{% endcode %}

{% hint style="warning" %}

## Data Sources requiring the Terra **Mobile-SDK**

The **Mobile SDK** is <mark style="color:red;">**ONLY**</mark> used to connect to the following integrations:

1. <img src="/files/HWBv7PWMxBEc0WMNdWrW" alt="" data-size="line">**Apple Health -** iOS
2. <img src="/files/qO7xuPhRY1YEKm096Aq2" alt="" data-size="line"> **Samsung Health -** Android
3. <img src="/files/zhkGsxfmF1M2jUWRUXRA" alt="" data-size="line"> **Google Fit & Health Connect**- Android.

Note: the [Unified API](broken://pages/u4aCTkJiebhGE1aqPS8y) is the preferred way to connect to Google Fit due to better reliability.

For **ALL** other integrations, please refer to Integration Setup the [Unified API](broken://pages/u4aCTkJiebhGE1aqPS8y).
{% endhint %}
