Nodds

Developer docs

The build guide and the full API reference, on one page.

Build a health app on Nodds

This guide takes you from nothing to a deployed AI health app — users connect a wearable or upload labs, then chat with their data. You write UI and product; Nodds handles connection, data retrieval, and grounded answers.

Two ways to start:

  • Clone the starter (examples/starter) — a minimal Next.js app already wired to Nodds. Fastest path.
  • Generate it — hand an agent the build-nodds-health-app skill and this guide; it scaffolds the app for you.

1. Get a key

You need a Nodds API key (NODDS_API_KEY) and your API base URL from the Nodds dashboard. The key is server-side only — it lives in your backend env, never in the browser.

2. The one rule: reference_id

Pick an id per user (a UUID works) and reuse it forever. It's how Nodds groups that user's devices + labs and scopes every call. Store it with your user record; for a signed-out demo, generate one and keep it in localStorage.

3. Proxy Nodds through your backend

Because the key is server-side, your frontend calls your routes, which forward to Nodds with the key attached. A tiny helper:

// lib/plane.js (your backend)
const BASE = process.env.NODDS_API_URL;      // https://.../api/nodds/v1
const KEY = process.env.NODDS_API_KEY;
export const plane = (path, init = {}) =>
  fetch(`${BASE}${path}`, { ...init, headers: { Authorization: `Bearer ${KEY}`, ...init.headers } });

Then thin proxy routes for the data plumbing — POST /api/connectplane("/connect", …); GET /api/integrationsplane("/integrations") — and your own /api/chat route running the agent loop from step 5.

4. Connect a device

  1. GET /integrations → render provider buttons.
  2. On click, POST /connect with { resource, reference_id, success_url } and redirect the browser to the returned connect_url.
  3. Handle the return at success_url (?status=success&resource=…). The connection is already saved against the reference_id; just show it as connected.

5. Chat with the data

Run a small agent with your own model key: per user message, POST /context with { reference_id, query } and drop the returned summary into your prompt; attach the Nodds MCP (<base>/mcp, Bearer auth) so the model can pull exact records and aggregations when the question needs them. The starter's lib/agent.js is a complete streaming implementation — status events drive a "thinking" indicator, deltas are answer tokens. That's the whole grounded experience — sleep, recovery, HRV, labs.

6. Labs (optional)

POST /lab-reports (multipart) to upload a PDF/image; GET /lab-reports?reference_id= to poll status. Extracted biomarkers become available to /context and the MCP automatically.

7. Deploy

It's a normal web app — deploy anywhere (Vercel, etc.). Set NODDS_API_URL and NODDS_API_KEY in the host's environment. Done: your customers have a branded AI health app.


See plane-api.md for the full endpoint reference. The build-nodds-health-app skill turns this guide into an automated scaffold.


Nodds API

The Nodds API gives your app device connection and grounded AI querying over a user's health data — wearables (sleep, recovery, HRV, heart rate, steps, activity) and lab/blood results — with one key. Nodds does the data retrieval and grounding; you get clean, structured answers and never invent numbers.

  • Base URL: https://nodds.ai/api/nodds/v1
  • Auth: Authorization: Bearer <NODDS_API_KEY>
  • Content type: application/json (except uploads, which are multipart/form-data)

Keep your key server-side. The Nodds API key is a server-to-server credential. Call Nodds from your backend, never from a browser.

Identity: reference_id

Every user is identified by a reference_id — an id you choose and own (a UUID per user works well). All of a user's device connections and lab reports group under their reference_id, and every call is scoped by it. Generate one per user, store it, and reuse it.


Connect a device

Start a provider OAuth flow. Redirect the user to the returned connect_url; the provider sends them back to your success_url / failure_url when done.

POST /connect
Authorization: Bearer <NODDS_API_KEY>
Content-Type: application/json

{
  "resource": "GARMIN",
  "reference_id": "user-123",
  "success_url": "https://yourapp.com/connected",
  "failure_url": "https://yourapp.com/connect?error=1"
}
{ "connect_url": "https://.../widget/session/...", "user_id": "…" }

On return, success_url is called with ?status=success&resource=GARMIN&user_id=… (store nothing extra — the connection is already keyed to the reference_id).

List the providers you can offer with GET /integrations{ providers: [{ name, provider, icon, types }] }. Icons are served from Nodds' domain.

Check a user's connections

GET /users/user-123
Authorization: Bearer <NODDS_API_KEY>
{ "reference_id": "user-123", "connections": [{ "provider": "GARMIN", "user_id": "…" }] }

Grounded chat = your model + Nodds' context layer

There is no managed chat endpoint: Nodds sells the context layer. Your app runs its own model (any provider) and grounds every answer with /context below, drilling into exact records via the MCP when needed — the starter template's lib/agent.js is a complete working implementation of the loop.

Sample-data fallback: if a reference_id has no data at all (no connected devices, no lab reports), context comes from a realistic shared sample profile instead — clearly flagged — so your app works before a user connects anything. The moment they connect a device or upload labs, answers switch to their own data automatically.

Grounded context (the primary endpoint)

If you'd rather run your own model, POST /context returns the structured health context Nodds would ground on — feed it straight into your prompt.

POST /context
Authorization: Bearer <NODDS_API_KEY>
Content-Type: application/json

{ "reference_id": "user-123", "query": "how is my recovery trending?" }
{ "reference_id": "user-123", "sample": false, "context": "…structured signals, evidence, insights…" }

context is null when there's nothing to ground on — never fabricate around it. sample: true means the reference had no data of its own, so the context comes from the shared sample profile — tell your model (and your user) accordingly.

The Nodds MCP — raw-data drill-down (and coding agents)

<base>/mcp is a Model Context Protocol server (Streamable HTTP, stateless). Attach it to your agent alongside /context for questions that need exact records or custom aggregations, or plug it straight into a coding agent:

Claude Code

claude mcp add --transport http plane https://nodds.ai/api/nodds/v1/mcp \
  --header "Authorization: Bearer $NODDS_API_KEY"

Codex (~/.codex/config.toml)

[mcp_servers.plane]
url = "https://nodds.ai/api/nodds/v1/mcp"
http_headers = { "Authorization" = "Bearer <NODDS_API_KEY>" }

Tools (every one takes reference_id):

ToolWhat it does
get_health_contextThe primary tool — grounded structured context for a natural-language question (same engine as /context)
query_health_dataRaw records or aggregations for one data type (fields/select/group_by/where)
describe_health_fieldsThe valid queryable fields for a data type — call before query_health_data
list_sourcesThe user's connected sources (+ whether results are sample data)
get_lab_resultsUploaded lab reports with extracted biomarkers
get_environmentWeather history + forecast (temperature, humidity, precipitation, UV, wind) — defaults to the account's saved Weather location; pass a place name to override
get_air_qualityCurrent AQI, pollutants, and health recommendations (+ up to 30 days of daily history) — defaults to the saved Weather location
get_pollenGrass/tree/weed pollen forecast up to 5 days — defaults to the saved Weather location
get_calendar_eventsEvents from the Google Calendar connected to your Nodds account (Connect page) — relate schedule load to health data

Same sample-data fallback as /context: references with no data ground on the shared sample profile, clearly flagged. The starter's lib/agent.js shows the full pattern (context + MCP + your own model).

Account-level connectors (current limitation): the environment tools' saved-location default and the Google Calendar grant belong to the Nodds account that owns the API key — not to your end users. For multi-user apps, pass each user's location explicitly to get_environment / get_air_quality / get_pollen; get_calendar_events currently reads only the key owner's connected calendar (per-user calendar connect is planned).

Lab / blood reports

POST /lab-reports            # multipart: file=<PDF/image>, reference_id=user-123
GET  /lab-reports?reference_id=user-123
Authorization: Bearer <NODDS_API_KEY>

GET returns { sessions: [{ session_id, current_status, results_count, uploaded_at }] }. Reports process asynchronously; poll status. Uploaded biomarkers are automatically available to /context and the MCP's get_lab_results.


Free tier & credits

Every account includes a free tier — 1 device connection, 25 grounded context calls, and 50 data queries per month — enough to build and demo an app. Past it, requests draw on a prepaid balance: each billable request debits its price, and requests halt with 402 when the balance hits zero until you reload. Per-request pricing: grounded context $0.01, data query $0.005 — so a $20 reload is roughly 2,000 context calls. Reload and check your balance on the Nodds dashboard (/billing). Integrations listing and lab uploads are never charged. (The hosted Nodds chat at the consumer site has its own free allowance and per-question pricing, shown in-app.)

Errors

StatusMeaning
400Missing/invalid parameters
401Missing or invalid Nodds API key
402Free tier exhausted and no credits remain — add credits on the Nodds dashboard (/billing)
403Reference belongs to another account's key, or asset host not allowed
502Upstream data platform error
503Nodds API not configured on the server

Notes

  • Data figures always come from real signals; when data is missing, Nodds says so rather than inventing values.
  • The only step that currently leaves Nodds' domain is the provider-consent page during /connect (hosted by the data platform). Everything else — data, chat, context, assets — is served through Nodds.