| name | terra-troubleshooting |
| description | Terra API troubleshooting and debugging. Use when experiencing connection issues, data sync problems, webhook failures, SDK errors, or provider-specific issues. |
Terra Troubleshooting
Diagnose and resolve common Terra API issues.
Quick Diagnostics
from terra import Terra
def check_terra_health(client: Terra) -> dict:
"""Run basic health checks."""
results = {}
try:
integrations = client.integrations.fetch()
results["api_connection"] = f"OK - {len(integrations.integrations)} providers"
except Exception as e:
results["api_connection"] = f"FAILED - {e}"
try:
users = client.user.getsubscriptions()
results["connected_users"] = f"OK - {len(users.users)} users"
except Exception as e:
results["connected_users"] = f"FAILED - {e}"
return results
client = Terra(dev_id="...", api_key="...")
print(check_terra_health(client))
Common Issues
Authentication Issues
"Invalid API key or dev-id"
Cause: Incorrect credentials or wrong environment.
Solution:
ENVIRONMENTS = {
"testing": {
"dev_id": os.environ.get("TERRA_DEV_ID_TESTING"),
"api_key": os.environ.get("TERRA_API_KEY_TESTING")
},
"staging": {
"dev_id": os.environ.get("TERRA_DEV_ID_STAGING"),
"api_key": os.environ.get("TERRA_API_KEY_STAGING")
},
"production": {
"dev_id": os.environ.get("TERRA_DEV_ID_PRODUCTION"),
"api_key": os.environ.get("TERRA_API_KEY_PRODUCTION")
}
}
from terra import Terra
client = Terra(**ENVIRONMENTS["testing"])
print(client.integrations.fetch())
Widget session expired
Cause: Session URLs expire after 15 minutes.
Solution: Generate a new widget session:
response = client.authentication.generatewidgetsession(
reference_id="user_123",
auth_success_redirect_url="https://app.example.com/success",
auth_failure_redirect_url="https://app.example.com/failure"
)
Mobile SDK token invalid
Cause: Token expired (3 min) or already used.
Solution: Generate fresh token for each connection attempt:
token = client.authentication.generateauthtoken(reference_id="user_123")
Connection Issues
User stuck on "connecting"
Cause: OAuth flow interrupted or WebView used.
Solution:
- Never use WebView/iFrame for OAuth
- Open auth URL in real browser
- Handle redirects properly:
<WebView source={{ uri: authUrl }} />
import { Linking } from 'react-native';
Linking.openURL(authUrl);
import { InAppBrowser } from 'react-native-inappbrowser-reborn';
InAppBrowser.open(authUrl);
Provider returns "Access Denied"
Cause: Provider-side issue or insufficient permissions.
Solution:
user = client.user.getuser(user_id="terra_abc123")
print(f"Scopes: {user.user.scopes}")
WHOOP / Dexcom connection fails
Cause: These require special activation.
Solution: Contact Terra support at [email protected] for:
- WHOOP access activation
- Dexcom (CGM) access activation
- Freestyle Libre EU dedicated API keys
- Strava dedicated API keys
Data Sync Issues
No data received
Causes:
- User just connected (data takes time to sync)
- User has no data in that date range
- Provider is "polled" type (5-min delay)
Solution:
from datetime import datetime, timedelta
def check_user_data(client: Terra, user_id: str) -> dict:
"""Check if user has any data."""
end = datetime.now()
start = end - timedelta(days=7)
results = {}
for data_type in ["daily", "activity", "sleep", "body"]:
try:
method = getattr(client, data_type)
response = method.get(user_id=user_id, start_date=start, end_date=end)
results[data_type] = len(response.data)
except Exception as e:
results[data_type] = f"Error: {e}"
return results
print(check_user_data(client, "terra_abc123"))
Duplicate data received
Cause: Daily/body data updates multiple times per day.
Solution: Use UPSERT pattern, not INSERT:
db.insert(data)
db.upsert(
{"user_id": user_id, "date": date},
{"$set": data}
)
Historical data missing
Cause: Provider limits historical access.
Reference - Maximum historical data per provider:
| Provider | Limit |
|---|
| Garmin | 5 years |
| Fitbit | 10 years |
| Oura | 3 years |
| WHOOP | 2 years |
| Polar | 30 days |
| COROS | 3 months |
Webhook Issues
Webhooks not received
Checklist:
-
Check webhook configured in dashboard:
- Terra Dashboard → Destinations → Webhooks
- Verify URL is correct and HTTPS
-
Check endpoint is accessible:
curl -X POST https://your-webhook-url.com/terra \
-H "Content-Type: application/json" \
-d '{"type": "test"}'
-
Check server logs for incoming requests
-
Verify IP not blocked - Terra IPs:
18.133.218.210, 18.169.82.189, 18.132.162.19,
18.130.218.186, 13.43.183.154, 3.11.208.36,
35.214.201.105, 35.214.230.71, 35.214.252.53, 35.214.229.114
Signature verification failing
Causes:
- Wrong signing secret
- Body modified before verification
- Incorrect signature algorithm
Solution:
import hmac
import hashlib
def debug_signature(header: str, body: bytes, secret: str):
"""Debug signature verification."""
parts = dict(p.split("=") for p in header.split(","))
print(f"Timestamp: {parts['t']}")
print(f"Received signature: {parts['v1']}")
message = f"{parts['t']}.{body.decode()}"
expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
print(f"Expected signature: {expected}")
match = hmac.compare_digest(expected, parts['v1'])
print(f"Match: {match}")
return match
@app.route("/webhook", methods=["POST"])
def webhook():
header = request.headers.get("terra-signature")
body = request.get_data()
if not debug_signature(header, body, SIGNING_SECRET):
return "Invalid signature", 401
payload = request.get_json()
Critical: Get raw body BEFORE parsing JSON.
Webhook timeouts
Cause: Processing takes too long (>5 seconds recommended).
Solution: Process asynchronously:
from celery import Celery
celery = Celery()
@app.route("/webhook", methods=["POST"])
def webhook():
process_webhook.delay(request.get_json())
return "OK", 200
@celery.task
def process_webhook(payload):
save_to_database(payload)
send_notifications(payload)
SDK Issues
iOS: Apple Health no data
Causes:
- Permissions not granted
- Background delivery not enabled
- App not authorized in Health app
Solution:
Terra.checkPermissions { granted in
if !granted {
Terra.requestPermissions()
}
}
func application(_ app: UIApplication, didFinishLaunchingWithOptions...) {
Terra.setUpBackgroundDelivery()
}
Android: Samsung Health fails
Causes:
- Samsung Health app not installed
- minSDK < 28
- Missing permissions
Solution:
if (!Terra.isSamsungHealthAvailable(context)) {
showInstallSamsungHealthDialog()
}
android {
defaultConfig {
minSdkVersion 28
}
}
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="android.permission.BODY_SENSORS"/>
Android: Health Connect issues
Cause: Health Connect not installed or configured.
Solution:
if (!Terra.isHealthConnectAvailable(context)) {
val intent = Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://play.google.com/store/apps/details?id=com.google.android.apps.healthdata")
}
startActivity(intent)
}
React Native: Build failures
Common fixes:
cd ios
pod deintegrate
pod install
cd ..
npx react-native run-ios
cd android
./gradlew clean
cd ..
npx react-native run-android
Provider-Specific Issues
MyFitnessPal: Gateway timeout
Cause: MyFitnessPal API is slow/unreliable.
Solution: Retry with exponential backoff:
import time
def fetch_with_retry(client, user_id, start, end, max_retries=3):
for attempt in range(max_retries):
try:
return client.nutrition.get(user_id, start, end)
except Exception as e:
if "timeout" in str(e).lower() and attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
raise
Garmin: Data delayed
Cause: Garmin is a "polled" provider (~5 min sync).
Solution: Wait for webhook rather than polling API:
Fitbit: Token revoked
Cause: User revoked access in Fitbit app.
Solution: Handle access_revoked webhook:
def handle_access_revoked(payload):
user_id = payload["user"]["user_id"]
db.terra_users.update_one(
{"terra_user_id": user_id},
{"$set": {"status": "access_revoked"}}
)
send_reconnect_notification(user_id)
Debug Mode
Enable detailed logging:
import logging
logging.getLogger("terra").setLevel(logging.DEBUG)
import http.client
http.client.HTTPConnection.debuglevel = 1
Support Escalation
If issues persist:
- Check Terra Status: https://status.tryterra.co
- Documentation: https://docs.tryterra.co
- Email Support: [email protected]
- Include in support request:
- Dev ID (not API key!)
- User ID (if applicable)
- Error message
- Timestamp of issue
- Provider affected
Health Check Script
"""Terra API health check script."""
from terra import Terra
from datetime import datetime, timedelta
def run_health_check():
print("=" * 50)
print("Terra API Health Check")
print("=" * 50)
envs = {
"testing": (os.environ.get("TERRA_DEV_ID_TESTING"), os.environ.get("TERRA_API_KEY_TESTING")),
"staging": (os.environ.get("TERRA_DEV_ID_STAGING"), os.environ.get("TERRA_API_KEY_STAGING")),
"production": (os.environ.get("TERRA_DEV_ID_PRODUCTION"), os.environ.get("TERRA_API_KEY_PRODUCTION")),
}
for env_name, (dev_id, api_key) in envs.items():
print(f"\n{env_name.upper()}:")
try:
client = Terra(dev_id=dev_id, api_key=api_key)
integrations = client.integrations.fetch()
print(f" ✅ API Connected - {len(integrations.integrations)} providers")
users = client.user.getsubscriptions()
print(f" ✅ Users: {len(users.users)} connected")
except Exception as e:
print(f" ❌ Error: {e}")
print("\n" + "=" * 50)
print("Health check complete")
if __name__ == "__main__":
run_health_check()
Related Skills
- terra-auth: Credential management
- terra-connections: Connection flows
- terra-webhooks: Webhook handling
- terra-data: Data retrieval
- terra-sdk: SDK integration