SuperAPI AI Directory

terra-mobile-sdk

General & other

View repository

Updated 10-16 01:26

About this skill

Terra API Mobile SDK

Guidance for integrating the Terra API mobile SDK on iOS, Android, React Native, and Flutter. The workflow and every cross-platform gotcha live here in SKILL.md; per-platform setup detail (capabilities, manifest entries, install coordinates, code snippets, platform quirks) lives in references/.

Account tools: CLI or MCP

For a mobile integration, the account tools supply the credential your backend holds and confirm that a device actually registered.

Setting up, once. The backend that mints per-user tokens needs a credential of its own:

terra environments list --select dev_id,name                    # the dev-id the app is built against
terra data-tokens create --env <dev-id> --name mobile --scopes auth:write

Checking your work, as often as you like. These are read-only and print no credential material:

terra data-tokens list --env <dev-id> --select token_id,name,scopes,expires_at,last_used_at,revoked_at
terra users list --env <dev-id> --provider APPLE --select user_id,reference_id,active

data-tokens list is metadata only, which is what makes it the right check: it says whether a usable token exists and what it is scoped for, without re-showing any bearer. Re-showing one is the separate, keys:read-gated terra data-tokens retrieve-secret.

Exercising the mint path by hand. This one is not a read: it mints the short-lived token initConnection() takes, and prints it.

terra data-api /auth/tokens -X POST -q reference_id=<your id>

reference_id is a query parameter here, so it takes -q. Passing it with -d puts it in a JSON body the endpoint does not read, and the call still succeeds: you get back a token that is silently unbound. That matters, because binding is what stops a token attaching a device to the wrong user. Bound, the token is tied to that identifier and redemption ignores whatever the SDK sends. (/auth/generateWidgetSession does take a body, so -d is right there. Check with terra admin-api list --data-api <path> --format json rather than assuming.)

Useful for checking the flow end to end before the backend exists, but it is still a credential on stdout, so keep it out of CI logs.

The reference files and shipped SDK versions call POST /auth/generateAuthToken. That is the deprecated spelling of this endpoint and is identical to it, same parameters and same response, kept indefinitely because released SDKs call that path. Either works; new code should use /auth/tokens.

Reach for terra environments retrieve-api-key only when something genuinely needs the environment's API key. Nothing in the mobile flow does, and it prints the webhook signing secret alongside it.

Never ship the admin token or the environment API key in an app. The SDK takes a short-lived auth token minted per user, and terra data-tokens create --scopes auth:write is the explicitly scoped, revocable credential for the backend that mints them: several coexist per environment, so rotation is mint-new then revoke-old with no cutover. Over MCP, minting a data token needs the user's approval in the dashboard and an access level the default MCP sign-in does not grant, so this one step is simpler with the CLI.

After a device connects, terra users list --reference-id <ref> says whether the record landed and stayed active, which separates an SDK problem from a permissions one.

In a coding agent (Claude Code, Codex, Cursor and the like, on the user's machine or in the cloud), use the CLI, and offer to install it if it is missing: curl -fsSL https://cli.tryterra.co/install.sh | sh on macOS and Linux, irm "https://cli.tryterra.co/install.ps1" | iex in Windows PowerShell, or Homebrew or npm where the user already uses them. In a chat assistant (claude.ai, Claude Desktop, ChatGPT), use the Terra API admin MCP server, even when it can run code: its sandbox cannot install the CLI or sign in. Each admin command is a tool whose name and method spell it (terra users list is users_read with method: "list"). Only the CLI has terra data-api, terra admin-api and terra examples, and --select and --jq have no MCP equivalent. The terra-cli and terra-mcp skills carry the guardrails (confirming changes, keeping credentials out of transcripts), the errors, and a playbook per task. They administer the integration; they do not replace the API calls this skill describes.

When the mobile SDK is the right tool

The mobile SDK exists for one reason: to reach health sources that have no web API. Only three integrations require it:

  • Apple Health / HealthKit (iOS)
  • Samsung Health (Android)
  • Health Connect (Android)

Every other provider (Garmin, Fitbit, Oura, Whoop, Strava, Dexcom, and 500+ more) connects through the Unified API, not the SDK. Google Fit can be read through the SDK via Health Connect, but the web API is the preferred, more reliable route for it. If a provider you need has a web API, use the web API.

Data captured by the SDK flows to the same Data Destination (webhook) as web API data, and connections are managed with the same auth model. If you are building the receiving webhook or storing the health data, that is the terra-unified-api skill's territory; this skill covers the on-device connection. Live, per-second data from a BLE or ANT+ wearable is the separate Real-Time SDK, covered by terra-streaming.

The six-step workflow

The shape is identical on every platform. Platform differences are in the references/ files.

  1. Install the SDK and grant native capabilities. Add the package, declare HealthKit/background capabilities (iOS) or manifest permissions and minSDK 28 (Android). See the per-platform reference.
  2. Initialize the SDK on every app start and every foreground. Create the TerraManager (iOS/Android) or call initTerra (RN/Flutter). This call is asynchronous and must complete before any other SDK call. Initializing is a prerequisite for everything else, so do it every time the app opens, not just once per install.
  3. Mint a single-use auth token from YOUR BACKEND. POST https://api.tryterra.co/v2/auth/tokens (the deprecated /auth/generateAuthToken is the same endpoint), with your user's identifier as the reference_id query parameter so the token is bound to that user. Authenticate with the auth:write data token from "Account tools" above, sent as Authorization: Bearer terra_dt_...; it is bound to one environment, so no dev-id header is needed. The dev-id and x-api-key pair the reference files show still works, but it is the environment's master key. The token is single-use and expires in 3 minutes (the response includes expires_in), so mint it just-in-time when the user initiates the connection, not at app start. Hand it to the client through your own channel. Never call this endpoint from the app, in development or production: whatever credential can mint tokens must stay on your server.
  4. Open the connection with initConnection. Pass the connection type, the token, and your permission set. This triggers the OS permission popup. See "Permission popup behavior" below – it is the highest-friction part of the integration.
  5. Validate with getUserId on every re-initialization. getUserId is synchronous and returns the user_id if the connection is live or nil/null if not. Call it right after every init; on nil, call initConnection again to reconnect. Re-connecting an already-connected user triggers no popup and does not interrupt the user.
  6. iOS only: enable background delivery. Call Terra.setUpBackgroundDelivery() in your AppDelegate's didFinishLaunchingWithOptions. Without it, Apple Health data is not pushed automatically.

Deauthenticating a user uses the same endpoint as web integrations: DELETE /auth/deauthenticateUser, called from your backend. See the web API auth reference.

Permission popup behavior (read before shipping)

  • The popup fires once. On both Apple Health and Health Connect, the OS shows the permission screen only the first time. Calling initConnection again is a no-op for the popup. It re-triggers only if: (a) you call initConnection with an expanded set of customPermissions, (b) the app is deleted and reinstalled, or (c) – Health Connect only – you request a permission that Google has not approved for your app on release.
  • The HealthKit popup is a WebView. If your app is itself WebView-based, you must interrupt your WebView, call initConnection, and reopen your WebView on completion. Otherwise the popup cannot render.
  • getUserId returning nil means reconnect, not re-popup. Reconnecting a still-authorized user is silent.

Background delivery (iOS)

Terra.setUpBackgroundDelivery() enables automatic Apple Health pushes, but the delivery is constrained:

  • Fires at a much lower frequency when the app is killed.
  • Fires only when the phone is unlocked.
  • Fires only with a network connection.
  • Delivers only data types enabled on the Terra API dashboard. terra unified-api data scopes list --env <dev-id> reads that set. To change it, terra unified-api data scopes replace takes the complete set: every type you leave out is deselected, so read the list first and send it back with your additions.
  • If you use customPermissions, background delivery needs specific per-category permissions enabled: Daily needs STEPS; Sleep needs SLEEP_ANALYSIS; Body needs BMI and HEART_RATE; Activity needs WORKOUT_TYPE; Nutrition needs NUTRITION_CALORIES.

schedulerOn has no effect on iOS – background delivery is controlled entirely by setUpBackgroundDelivery(). On Android, schedulerOn = true enables Terra API to make scheduled requests while the app is in the foreground.

Cross-platform gotchas

  • Samsung Health needs an approved partnership. Terra API has a privileged Samsung partnership that bypasses Health Connect, but you must apply for access. While waiting, develop against Health Connect; switching to Samsung direct later is a version bump with no code changes. Release builds need the ProGuard rule -keep class com.samsung.android.** { *; } (R8 strips the Samsung classes without it and crashes at runtime), and test devices need Developer Mode enabled in Samsung Health.
  • Health Connect needs manifest work and Google approval. Your manifest needs a privacy-policy <activity> with the ACTION_SHOW_PERMISSIONS_RATIONALE and VIEW_PERMISSION_USAGE intent-filters. Before going live you must apply to Google for Health Connect data-type access and add tools:node="remove" <uses-permission> lines for every permission you do not use. See references/android.md.
  • setIgnoredSources is iOS-only and not persisted. It filters out data from specific source apps in HealthKit (useful when a user has both a cloud Terra API connection and that provider's companion app syncing into Apple Health, which produces duplicates). It resets on every app restart, so call it after every init. On Android it is a no-op.
  • A new device or reinstall replaces the connection – handle user_reauth. When an SDK auth arrives with the same (dev-id, reference_id, provider) as an existing connection but a new device identifier (second device, or a reinstall that reset the vendor ID), Terra API issues a new user_id, deletes every previous connection for that tuple, and sends a user_reauth webhook carrying old_user and new_user. Handle that event by migrating your stored old_user.user_id to new_user.user_id; there is only ever one active connection per (dev-id, reference_id, provider). This replacement only fires when a reference_id is supplied – with an empty reference_id stale connections accumulate instead, one more reason to always set reference_id.
  • postActivity requires device_data and iOS 14+. Writing a completed activity fails without device_data in the payload.

Decisions left to you

These are your calls; the docs do not prescribe them. Make them deliberately:

  • Which data types / permissions to request. Empty customPermissions defaults to all available; narrowing reduces the permission surface but you must keep background-delivery categories (above) enabled.
  • Immediate vs deferred HealthKit prompting. requestPermissions: false on Terra.instance recognizes returning users silently and lets you gate the popup on an explicit user action. See references/ios.md.
  • Device-switch handling. A second device or reinstall replaces the existing connection and fires user_reauth (see above), so decide whether to let any device connect (and migrate user_ids on user_reauth) or gate initConnection to a single primary device.
  • Samsung direct vs Health Connect as your Android route (and whether to start on Health Connect while the Samsung partnership is pending).
  • Backfill approach. The getDaily/getSleep/etc. getters accept toWebhook: true (push to your destination) or toWebhook: false (return the payload to the client callback).
  • How your backend hands the auth token to the client, and your reference_id scheme (this is the join key in every webhook).

Platform references

Read the file for the platform you are building on:

  • references/ios.md – read when integrating TerraiOS natively (Swift): capabilities, info.plist keys, Terra.instance, deferred prompting, setUpBackgroundDelivery, setIgnoredSources, writing data, planned-workout WorkoutKit sync.
  • references/android.md – read when integrating TerraAndroid natively (Kotlin): build.gradle coordinates, minSDK 28, Samsung vs Health Connect setup, ProGuard, manifest intent-filters, startIntent (deprecated, always null).
  • references/react-native.md – read when integrating terra-react: install, iOS + Android native setup, initTerra/initConnection, the AppDelegate background-delivery edit, postActivity (Apple Health only).
  • references/flutter.md – read when integrating terra_flutter_bridge: flutter pub, native setup, TerraFlutter API, AppDelegate background delivery, getters.

Full docs: docs.tryterra.co. Where a signature or enum is not shown here, see the SDK reference linked from mobile-only sources. Ask terra docs ask (or docs_ask over MCP) before fetching a page: it answers from the docs and cites its sources. Append .md to any docs URL for markdown.