# nLight.fit developer resources

Everything nLight.fit publishes for people and programs building against it, with the URL of each and a request you can run for every one. If you are an agent deciding whether this API is integrable, this page and the documents it links are the whole answer.

This page is also served at [`/api`](/api) and [`/api-docs`](/api-docs), and as raw Markdown at [`/docs/developers.md`](/docs/developers.md).

## Start here

**The MCP server is the intended integration point.** nLight.fit exposes a member's health record as read-only [Model Context Protocol](https://modelcontextprotocol.io) tools over Streamable HTTP, so an MCP client calls them natively rather than through a REST wrapper you have to write. The REST endpoints exist because the web app uses them.

**Nothing below needs an account to try.** The handshake and the tool catalog on the real endpoint need no credential, and the sandbox needs none for anything at all.

### Call the sandbox

[`https://nlight.fit/api/sandbox`](/docs/sandbox) speaks the same protocol with the same tool schemas against a synthetic member record. No account, no token, no approval:

```bash
curl -sS https://nlight.fit/api/sandbox \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

Every tool works. Every value is generated, and every response says so.

### Handshake with the real server

`initialize` and `tools/list` are answered on the production endpoint without a credential too, so you can confirm the server is real and see the tool surface before anyone signs up:

```bash
curl -sS https://nlight.fit/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2026-07-28","capabilities":{},
                 "clientInfo":{"name":"my-client","version":"1.0.0"}}}'
```

```bash
curl -sS https://nlight.fit/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
```

`tools/call` is where a credential becomes necessary, because that is where a member's record is read.

### Or use the CLI

```bash
npm install -g https://nlight.fit/cli/nlight-fit-latest.tgz
nlight discover      # fetch every developer document listed below
nlight init          # MCP handshake against the sandbox
nlight tools         # list the tools
nlight call get_streak metric=vitamins
```

Installing from that URL needs no registry account — `npm` supports tarball URLs natively, and it is the same artifact `npm publish` would upload. Registry publication is pending; `x-cli.registryPublished` in [the OpenAPI document](/openapi.json) says whether `npm install -g nlight-fit` works yet. See [the CLI reference](/docs/cli) for the full command set and how to point it at production.

## Machine-readable resources

| Resource | URL | Format |
| --- | --- | --- |
| nLight.fit OpenAPI specification | `https://nlight.fit/openapi.json` | `application/openapi+json` |
| — alternate location | `https://nlight.fit/.well-known/openapi.json` | `application/openapi+json` |
| nLight.fit API catalog (RFC 9727) | `https://nlight.fit/.well-known/api-catalog` | `application/linkset+json` |
| nLight.fit MCP server manifest | `https://nlight.fit/.well-known/mcp.json` | `application/json` |
| — short alias | `https://nlight.fit/mcp.json` | `application/json` |
| OAuth protected resource metadata (RFC 9728) | `https://nlight.fit/.well-known/oauth-protected-resource` | `application/json` |
| OAuth authorization server metadata (RFC 8414) | `https://nlight.fit/.well-known/oauth-authorization-server` | `application/json` |
| Documentation index for language models | `https://nlight.fit/llms.txt` | `text/plain` |
| Full documentation corpus | `https://nlight.fit/llms-full.txt` | `text/plain` |

Every documentation page is also served as raw Markdown at `/docs/<slug>.md` — for example [`/docs/mcp.md`](/docs/mcp.md) — with no navigation chrome to strip. Every HTML page on the site serves Markdown under `Accept: text/markdown`.

## Endpoints

| Endpoint | What it is | Credential |
| --- | --- | --- |
| `POST /api/mcp` | The nLight.fit MCP server. Eighteen read-only tools over a member's record. | Bearer token with `read`, for `tools/call`. The handshake and `tools/list` need none. |
| `POST /api/sandbox` | The same protocol and tools against synthetic data. | None, ever. |
| `GET /openapi.json` | This API as OpenAPI 3.1. Fifteen operations, all with typed schemas. | None. |
| `GET /.well-known/api-catalog` | RFC 9727 linkset naming every published API. | None. |
| `GET /.well-known/mcp.json` | The MCP manifest: transport, protocol revisions, tool list, access terms. | None. |
| `POST /api/proxy` | The web app's data endpoint, dispatching on an `action` field. | Bearer token, except `healthCheck`. |
| `POST /api/tokens` | Mint, list and revoke your own API tokens. | Signed-in session. |

A version alias exists at `/api/v1/*` for tooling that expects a version segment in the path. It routes to exactly the same handlers; see [versioning and deprecation](/docs/versioning).

The full operation list, with request and response schemas for each, is in [the OpenAPI document](/openapi.json). It is built from the same constants the handlers use, so a documented operation that no longer exists fails the build rather than misleading a generated client.

### Service status

`healthCheck` is the one REST action that needs no credential, and it is what the API catalog names as this API's status link:

```bash
curl -sS https://nlight.fit/api/proxy \
  -H 'Content-Type: application/json' \
  -d '{"action":"healthCheck"}'
```

```json
{
  "version": "3.1.0",
  "status": "healthy",
  "timestamp": "2026-08-23T00:00:00.000Z",
  "mongodb": "connected"
}
```

A signed-in caller gets the same fields plus operational detail. Anyone can poll the four above.

## Authentication

There are two routes, and which one you want depends on whose record you are reading.

### Your own account: three calls, no human

Signup is open, so a program can go from nothing to calling tools without anyone approving anything:

```bash
# 1. Create an account. The response carries a session token.
curl -sS https://nlight.fit/api/auth \
  -H 'Content-Type: application/json' \
  -d '{"action":"register","username":"my-agent","password":"..."}'

# 2. Exchange that session for a read-scoped API token.
curl -sS https://nlight.fit/api/tokens \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <session token from step 1>' \
  -d '{"action":"create","name":"my-agent","scopes":["read"]}'

# 3. Use it.
curl -sS https://nlight.fit/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer rt_ro_...' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_columns","arguments":{}}}'
```

This is the path `credentialSteps` in `x-access-terms` describes, with `humanInvolved: false` on every step. A member can do step 2 from Settings → MCP / CLI Access instead.

### Somebody else's account: OAuth 2.1

A client reading a *member's* record — Claude connecting to someone's health history — goes through OAuth, because that is where a person decides to share it.

1. An unauthenticated `tools/call` returns `401` with `WWW-Authenticate: Bearer realm="routine-tracker-mcp", resource_metadata="https://nlight.fit/.well-known/oauth-protected-resource"`.
2. That document names the authorization server. Its metadata is at [`/.well-known/oauth-authorization-server`](/.well-known/oauth-authorization-server).
3. Register dynamically ([RFC 7591](https://www.rfc-editor.org/rfc/rfc7591)) at `POST /api/oauth/register`. Public clients only — there is no client secret.
4. Send the member through `GET /api/oauth/authorize` with a PKCE `code_challenge` (`S256`; `plain` is not accepted). They approve a consent screen.
5. Exchange the code at `POST /api/oauth/token` with the `code_verifier`.

**Step 3 is open, and you can do it right now.** Registration needs no credential and no human:

```bash
curl -sS https://nlight.fit/api/oauth/register \
  -H 'Content-Type: application/json' \
  -d '{"client_name":"my-client",
       "redirect_uris":["https://example.com/callback"],
       "grant_types":["authorization_code","refresh_token"],
       "response_types":["code"],
       "token_endpoint_auth_method":"none"}'
```

```json
{
  "client_id": "…",
  "client_id_issued_at": 1787450670,
  "token_endpoint_auth_method": "none",
  "scope": "read"
}
```

Step 4 is the one step in *this* route that a program cannot complete, and that is deliberate: it is where a person decides to share their own medical record. It is published as data under `delegatedAccess` in `x-access-terms`, alongside the fully automatable `credentialSteps` above, so a client can pick the route that matches what it is doing rather than discover the boundary by hitting it.

In Claude, all of that is *Add custom connector* and pasting `https://nlight.fit/api/mcp`.

Tokens are `read`-only today, carry their scopes in the database rather than in the token string, and are revoked individually and immediately. Full detail in [authentication](/docs/authentication) and [token management](/docs/token-scripts).

## Conventions every response follows

**Rate limits are reported, not just enforced.** Every response carries `RateLimit-Policy` describing the quota, and every response that spends or reads one carries `RateLimit` describing what is left, so a client paces itself instead of discovering the limit by hitting it. A `429` adds `Retry-After`.

```http
RateLimit-Policy: "mcp";q=120;w=60
RateLimit: "mcp";r=118;t=47
RateLimit-Limit: 120
RateLimit-Remaining: 118
RateLimit-Reset: 47
```

`RateLimit` and `RateLimit-Policy` follow the IETF structured-field draft; the three-header form is the earlier draft, sent alongside because that is what most existing SDKs read. The complete quota table is published as data under `x-ratelimit-policies` in the OpenAPI document. Details in [errors and limits](/docs/errors).

**The version is on every response.** `Api-Version` names the API version that answered. Deprecation, when it happens, arrives as a `Deprecation` header ([RFC 9745](https://www.rfc-editor.org/rfc/rfc9745)) and then a `Sunset` header ([RFC 8594](https://www.rfc-editor.org/rfc/rfc8594)), with notice periods stated in the [versioning policy](/docs/versioning).

**Every response links back to this documentation.** The `Link` header carries `service-desc` (the OpenAPI document), `service-doc` (this page) and `version-history` (the versioning policy), so a client holding any response can find the rest without a discovery request.

**Errors are structured.** REST failures return `error`, a stable `code` where one applies, and a `requestId` to quote in a support request. MCP failures are JSON-RPC error objects with the standard codes. Both tables are in [errors and limits](/docs/errors).

## Human-readable documentation

- [MCP server](/docs/mcp) — transport, methods, which need no credential, scope, and what is deliberately not exposed. Also at [`/mcp`](/mcp).
- [MCP tool reference](/docs/mcp-tools) — all eighteen tools with full input schemas.
- [Client setup](/docs/clients) — connecting Claude Desktop, Claude Code or Cursor.
- [Sandbox](/docs/sandbox) — the credential-free environment and what its data is and is not. Also at [`/sandbox`](/sandbox).
- [Authentication](/docs/authentication) — token format, scopes, storage and revocation.
- [nLight.fit CLI](/docs/cli) — `npm install -g https://nlight.fit/cli/nlight-fit-latest.tgz`. Also at [`/cli`](/cli).
- [Token management](/docs/token-scripts) — minting and revoking tokens from Settings.
- [Errors and limits](/docs/errors) — status codes, JSON-RPC error codes, rate limits and protocol versions.
- [Versioning and deprecation](/docs/versioning) — how change is signalled before it lands. Also at [`/deprecation`](/deprecation).
- [Security and privacy](/docs/security) — encryption, third-party handling, and an honest account of what is not protected.

## What it costs

Tracking is free, and API and MCP access are part of the free tier. [Member](/docs/membership) is $50/year and changes how hard the coach thinks over a member's own history — it does not change what this API exposes.

The machine-readable version is `x-access-terms` in [the OpenAPI document](/openapi.json) and `_meta."fit.nlight".pricing` in [the MCP manifest](/.well-known/mcp.json), with `credentialSteps` giving the path above and `delegatedAccess` giving the OAuth one.

**Signup is open, so nothing here needs a human.** Building against [the sandbox](/docs/sandbox) first is still the better order — you never create an account you do not need, never handle someone's real bloodwork while you are still getting the shapes right, and you develop against a populated record rather than an empty one. Then change one URL: the tool names, schemas, validation and response envelopes are the same objects in both, not two implementations kept in agreement by hand.

## Contact

`Austin@nLight.fit` — one mailbox, for developer questions and security reports alike. Please report security issues privately and allow a reasonable window for a fix before disclosure.

nLight.fit is based in Boston, MA.
