About terra-models
terra-models is a skill for running Terra API wellness models on connected users’ data and interpreting their results. It covers sleep schedules, cycle indicators, and reconstructed metrics, with guidance on unsupported data and errors.
Key features
- 01Runs three documented wellness models
- 02Explains model inputs and outputs
- 03Distinguishes unsupported, no-data, and errors
- 04Tracks model runs and credit use
- 05Provides CLI and MCP administration guidance
Use cases
- 01Generating a sleep schedule insight
- 02Reviewing cycle indicators across a date range
- 03Filling gaps in recorded health metrics
MCP support
The skill describes using Terra’s admin MCP server for account commands, including model catalogs and run logs. Model execution itself uses the Terra API endpoint.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ tryterra/ agent-skills --skill terra-modelsAfter installing, type/terra-modelsin Claude Code to confirm it loaded.
Bundled filesmetadata.jsonREADME.md
Details
From the project READMETerra API Models
Run one of Terra API's health models over a connected user's data for a date range and get a ready-to-use insight back: one request in, one insight out. No model to host. Every model is a wellness indicator, not a medical or diagnostic tool; never present its output as clinical.
Account tools: CLI or MCP
Models has account commands of its own, including the run log that says why a run did not return what you expected:
terra models catalog list # the models available, account-wide
terra models runs list --env <dev-id> --status error # failed runs
terra models runs list --env <dev-id> --user-id <uuid> --model <model>
terra models runs list --env <dev-id> --run-id <run_id> # one run, by id
Two things about that log. It holds 30 days by default, so an older run is not there and an empty page is not evidence a run never happened. But --run-id widens the lookup to the full retained history, so a run you have an id for is never cut off by the window.
Because a run costs a credit whether or not it returns an insight, runs list --status error is also the fastest read on credits being spent on runs that never produced anything.
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 to use this
Building anything that calls GET /v2/models or GET /v2/models/run, prices model_run usage, or turns a user's Terra API data into a sleep-schedule, menstrual-cycle, or gap-filled-metrics insight.
Connect the user first
Models run for a user you have connected through the Unified API (see the terra-unified-api skill), over a date range you choose. A model cannot always return an insight for a given user and range; when it cannot you get either an unsupported result or an error, and the run still costs a credit either way (see Reading the result and Pricing).
The three models (generally available)
model id | Returns | Needs |
|---|---|---|
sleep_window | A recommended bedtime, wake time and in-bed duration, a 0–100 consistency score with a band, and the single change that would most improve it | Overnight sleep sessions from across the user's history |
cycle_phase_tracker | A per-day menstrual cycle phase, plus the period onsets and fertile windows detected in overnight physiology, each with a confidence band | At least 30 days of overnight sleep carrying heart rate and HRV |
graph_impute | Smart Fill: one day's metrics for a chosen table, marking which the device recorded and which the model reconstructed | Recorded history for that user in the table you ask for |
sleep_window, cycle_phase_tracker and graph_impute are GA and documented. Treat any other model id as pre-GA and unstable: it may be runnable, but its response shape can change without notice.
cycle_phase_tracker is a wellness indicator, not a medical or contraceptive tool, and it detects onsets from physiology alone – it neither needs nor reads a user's logged period dates.
Making a run
GET https://api.tryterra.co/v2/models/run
Query params: model, user_id, start_time, end_time (all ISO 8601). Headers: x-api-key and dev-id (your own keys; this is not a privileged endpoint).
graph_impute needs one more param: table, one of sleeps, dailies or activities. The endpoint refuses the run without it rather than choosing a table for you, so a request built from the generic param list alone returns 400. The three tables carry different metrics.
curl --request GET \
'https://api.tryterra.co/v2/models/run?model=sleep_window&user_id=USER_ID&start_time=2026-04-01T00:00:00Z&end_time=2026-07-01T00:00:00Z' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'dev-id: YOUR_DEV_ID'
Fetch https://docs.tryterra.co/models/models.md for the exact params and the full per-model response schema before you build request or response types; the response shape differs per model and evolves. 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.
Reading the result (the part agents get wrong)
- Unsupported device is a 200, not an error. When the user's device or provider cannot produce the model, the run returns HTTP 200 with
unsupported: true(plusunsupported_reasonandsupported/supported_devices) in place of an insight. Handle that as a normal outcome and surface it to the user, not as a failure. - A genuine error is a real error. Bad or missing inputs return
4xx, and a run that cannot be computed (for example too little data in the range) can return5xx. These come back as RFC 7807 problem+json (title,detail, anderrors[]for validation), for example400 unknown model,404 invalid user id. This is not the gracefulunsupportedcase above; do not swallow it. - Every run bills, including failures. A run is metered before it computes, so an unsupported or errored run still costs a credit. Validate the model id, the user, and that the device is supported up front rather than probing blindly.
- Empty is not always
unsupported.sleep_windowusesunsupported/unsupported_reasonfor a device it cannot serve andno_data/no_data_reasonfor a user it has too little history for.cycle_phase_trackeronly ever usesno_data/no_data_reason. Handle both keys; do not branch onunsupportedalone. - The shape is per-model.
sleep_windowreturns awindowobject (recommended_bed/recommended_wakeare local clock times;approximate: truemeans the schedule was estimated from limited history), asleep_regularityobject (score0–100 and abandof Low/Fair/Good/Excellent), and, unless the user is already consistent, arecommendationobject (change,reason,moves_to,gain) naming the single biggest change that would raise the score.cycle_phase_trackerreturnsresultsplusperiodsandfertile_windows.graph_imputereturnstable,featuresandresults. sleep_windowignores the range you pass. It reads the user's whole sleep history, sostart_timeandend_timedo not change its answer. Do not build a UI that implies picking a window changes the schedule.cycle_phase_tracker'sresultsis not a list of answers. It carries one row per calendar day of the range you asked for, including days the model had no data for – soresults.lengthis never a coverage signal. Branch on each row'savailable. Read the*_confidencebands (low/medium/high), never a raw probability, and note a period day is never also a fertile day.- For
graph_impute,computedandreportedare the whole point. Each entry in a day'sfieldscarriesreported: trueif the device actually recorded the value andcomputed: trueif the model reconstructed it, with aconfidenceon the reconstructed ones. Never present a computed value as recorded. - The response's
data_typeis not the catalog'sdata_type. The catalog reports the data a model reads (sleep_window→sleep); a run response reports its own value (sleep_window→sleep_window). Do not assert one from the other. truncated: truemeans the run hit the per-request data cap; the insight is still valid but computed over a bounded slice of the range.- Model outputs are modest-confidence indicators. Do not present a single result as authoritative.
Pricing
Usage-based: $0.01 per model run, drawn from your plan's monthly credit allowance. One run is one model over one user for one date range, regardless of how much data the range covers; running two models, or the same model on two users, is two runs. A run started from the Terra API dashboard and one made through this API bill through the identical path. Details: https://docs.tryterra.co/unified-api/pricing
Boundaries
- Not the Unified API. Connecting users, webhooks, and historical data are the Unified API (
terra-unified-api); a model run analyzes a connected user's data and returns an insight. - Not Health Scores. Health Scores are continuous physiological scores; Models return a discrete, on-demand insight from a single call.