Health Rewards

Health Rewards

Overview

The Terra Health Rewards product lets you turn the raw activity, sleep and physiological data you already receive through the Terra API into actionable, gamified points that drive user engagement.

  • Science‑based & adaptive – Points are calculated from evidence‑backed metrics (sleep duration, HRV, steps, floors climbed and more) and automatically adjust to each individual’s baseline.

  • Real‑time – Rewards are generated on demand

  • Configurable – Limits, category weights and streak bonuses can be tweaked in Dashboard → Health Rewards (see screenshots below).

  • Monetisable – Combine earned points with in‑app currency, coupons or badge systems to incentivise healthier habits.


How It Works

  1. Enable Rewards Open the dashboard, navigate to Health Rewards and toggle the product to “Enabled”. Here you can:

    • assign daily point caps (overall or per category),

    • choose weights for each goal (e.g. “Hours Slept = 3 pts”, “Steps = 1 pt”),

    • add streak bonuses (e.g. “≥10 pts 2 days in a row → +1 pt”). All changes apply instantly to new calculations.

  2. Request points Call the Rewards endpoints (see below) providing the user_id and a start_date (and optionally end_date). Terra fetches the necessary daily, sleep, activity, data, runs the algorithm in real time, then returns a total plus a human‑readable breakdown for every day.


Endpoints

Base URL https://api.tryterra.co/v2 All calls require the standard dev-id & x-api-key headers.

Method
Path
Purpose

GET

/rewards/points

Calculate points for a period

POST

/rewards/points/spent

Deduct points (e.g. when a user redeems a voucher)

GET

/rewards/points/spent/history

Retrieve a chronological list of redeemed points

1 Get points

GET /rewards/points?user_id=USER123&start_date=2024‑08‑01&end_date=2024‑08‑07&store=true
dev-id: YOUR_DEV_ID

Query parameters

Name
Type
Required
Description

user_id

string

Terra user identifier

start_date

YYYY‑MM‑DD

First date (inclusive)

end_date

YYYY‑MM‑DD

Last date (exclusive). Defaults to start_date + 1.

Successful response (200)
{
  "status": "success",
  "total": 147,
  "daily": [
    {
      "date": "2024-08-06",
      "raw_total": 12,
      "breakdown": {
        "sleep": {
          "streaks": [
            {
              "achieved": true,
              "goal": "Achieve 3 sleep points or more for at least 2 consecutive days",
              "points": 1,
              "max": 1
            }
          ],
          "raw_total": 4,
          "hours": {
            "achieved": 8,
            "goal": "Achieve 7.0-9.0 hours in bed",
            "points": 3,
            "max": 3
          },
          "bedtime": {
            "achieved": "11:00 PM",
            "goal": "Go to bed between 10:10 PM and 11:10 PM",
            "points": 2,
            "max": 2
          },
          "efficiency": {
            "achieved": 95,
            "goal": "Achieve a sleep efficiency of 88% or higher",
            "points": 2,
            "max": 2
          },
          "quality": {
            "achieved": 69,
            "goal": "Achieve a sleep quality of 77% or higher",
            "points": 0,
            "max": 2
          },
          "max": 4
        },
        "movement": {
          "floors": {
            "achieved": 0,
            "goal": "Achieve 3 floors climbed",
            "points": 0,
            "max": 1
          },
          "steps_proportional": {
            "achieved": 7266,
            "goal": "Achieve up to 10000 steps",
            "points": 1,
            "max": 2
          },
          "streaks": [
            {
              "achieved": false,
              "goal": "Achieve 2 movement points or more for at least 2 consecutive days",
              "points": 0,
              "max": 1
            }
          ],
          "raw_total": 1,
          "steps": {
            "achieved": 7266,
            "goal": "Achieve 10000 steps",
            "points": 0,
            "max": 1
          },
          "floors_proportional": {
            "achieved": 0,
            "goal": "Achieve up to 3 floors climbed",
            "points": 0,
            "max": 1
          },
          "max": 2
        },
        "streaks": [
          {
            "achieved": true,
            "goal": "Achieve 10 points or more for at least 2 consecutive days",
            "points": 1,
            "max": 1
          }
        ],
        "health_data": {
          "resting_heart_rate": {
            "consistency": {
              "achieved": 74,
              "goal": "Achieve a resting heart rate of 81 or lower",
              "points": 1,
              "max": 1
            },
            "improvement": {
              "achieved": 74,
              "goal": "Achieve a resting heart rate of 66 or lower",
              "points": 0,
              "max": 1
            }
          },
          "streaks": [
            {
              "achieved": true,
              "goal": "Achieve 1 health data points or more for at least 2 consecutive days",
              "points": 1,
              "max": 1
            }
          ],
          "raw_total": 1,
          "max": 1,
          "hrv": {
            "consistency": {
              "achieved": 22,
              "goal": "Achieve an HRV of 23 or higher",
              "points": 0,
              "max": 1
            },
            "improvement": {
              "achieved": 22,
              "goal": "Achieve an HRV of 28 or higher",
              "points": 0,
              "max": 1
            }
          }
        },
        "activity": {
          "streaks": [
            {
              "achieved": true,
              "goal": "Achieve 3 activity points or more for at least 2 consecutive days",
              "points": 1,
              "max": 1
            }
          ],
          "raw_total": 8,
          "target": {
            "achieved": {
              "moderate": 22,
              "vigorous": 44
            },
            "goal": "Achieve 30 minutes of moderate or 15 minutes of vigorous activity",
            "points": 5,
            "max": 5
          },
          "max": 8,
          "extra": {
            "achieved": {
              "moderate": 22,
              "vigorous": 44
            },
            "goal": "Achieve up to 15 extra minutes of moderate or 8 extra minutes of vigorous activity",
            "points": 2,
            "max": 2
          },
          "proportional": {
            "achieved": {
              "moderate": 22,
              "vigorous": 44
            },
            "goal": "Achieve up to 30 minutes of moderate or 15 minutes of vigorous activity",
            "points": 5,
            "max": 5
          }
        }
      },
      "streak_total": 4,
      "max": 12
    }
  ]
}

Key fields:

Field
Description

total

All‑time points earned (including streak bonuses, minus redemptions).

daily[*].raw_total

Points before streak bonuses that day.

daily[*].breakdown

Per‑category goals (activity, sleep, movement, health_data) with goal, achieved, points and max.

daily[*].streak_total

Points awarded from streak goals for that day.

2 Spend points

POST /rewards/points/spent?user_id=USER123
dev-id: YOUR_DEV_ID
Content-Type: application/json

{ "points": 50 }

Deducts 50 points from the user’s balance. A 400 error is returned if the user has insufficient points.

3 Redemption history

GET /rewards/points/spent/history?user_id=USER123&start_date=2024‑01‑01&end_date=2024‑12‑31
dev-id: YOUR_DEV_ID

Returns an ordered array [ [points, timestamp] ].


Response Object Anatomy

Below is a trimmed version of the example response highlighting the structure you will typically work with:

{
  "daily": [
    {
      "date": "2024‑08‑06",
      "breakdown": {
        "activity": {
          "proportional": { "goal": "Up to 30 mins", "achieved": {...}, "points": 5, "max": 5 },
          "target":       { "goal": "30 mins",        "points": 5 },
          "extra":        { "goal": "Extra 15 mins",  "points": 2 },
          "streaks":      [ { "goal": "≥3 activity pts for 2 days", "points": 1 } ],
          "raw_total": 8,
          "max": 8
        },
        "sleep":   { … },
        "movement":{ … },
        "health_data": { … }
      },
      "raw_total": 12,
      "streak_total": 4,
      "max": 12
    }
  ],
  "total": 147
}

Tip – Parsing points quickly If you only need the numeric score, read daily[*].raw_total + daily[*].streak_total. If you want to visualise goals, iterate over breakdown and use the goal / achieved strings to populate the UI.


Dashboard Configuration

The Health Rewards UI (see screenshots) is laid out by category:

Category
Example Goals

Activity

Minutes of moderate / vigorous activity

Movement

Steps & floors climbed

Sleep

Hours in bed, bedtime consistency, efficiency, quality

Health Data

HRV, resting heart‑rate consistency & improvement

Use Limits & Streaks to:

  • Set an overall cap (e.g., 30 pts / day)

  • Add global streaks (e.g., ≥20 pts for 5 days → +3 pts)

  • Define per‑category streaks (e.g., Movement ≥5 pts for 3 days → +1 pt)

All configuration is stored in Terra and read by the API at calculation time.


Example Use Case – “Weekly Wellness Challenge”

  1. Configure Goals

    • Dashboard → Health Rewards

    • Overall daily limit = 30 pts

    • Activity proportional = max 5, target = 5, extra = 2

    • Movement steps = 1, proportional = 2

    • Sleep hours/efficiency/quality = 2 each

    • Add streak: ≥20 pts for 7 days → +5 bonus

  2. Frontend Implementation

    • Show a progress bar that calls GET /rewards/points every morning for yesterday’s date.

    • Display breakdown tool‑tips using the goal and achieved strings.

  3. Redemption Flow

    • When a user taps “Redeem 100 pts for free shipping”, call POST /rewards/points/spent.

    • Show the new balance (total field) returned in the 200 response.

  4. Leaderboard

    • Aggregate raw_total + streak_total per user over the week to show top performers.


Best Practices

  • Sync ahead of time – Make sure wearable data is pulled before you calculate rewards for a day.

  • Store on server‑side – Use store=true (default) so that the canonical balance lives with Terra and cannot be tampered with client‑side.

  • Handle nulls gracefully – If a metric is missing (e.g., HRV), its goal will remain but points might be 0.

  • Rate limits – Reward endpoints share the same rate limits as other v2 calls (60 req/min default). Aggregate user calls where possible.


FAQ

Question
Answer

Can I backfill rewards?

Yes. Provide a historical start_date/end_date; Terra will fetch past data and compute points retro‑actively.

What time zone are dates evaluated in?

All dates are interpreted in local time.

Can I override an individual user’s goal?

Not yet. Goals are global per developer account.

Why are my users getting 0 points?

Check that the necessary data (sleep, daily summary) exists and that rewards are enabled for your dev ID.


Need help? Reach out via a dashboard support ticket.

Last updated

Was this helpful?