> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/introduction/examples/terra-pulse.md).

# Terra Pulse

Terra Pulse is a React web app that shows how to consume real-time wearable data from Terra, built directly on the [Streaming API](/streaming-api/getting-started.md) with no SDK. It opens a WebSocket to Terra's broker, authenticates as a consumer, and renders every reading (heart rate, steps, acceleration, and more) on a dashboard that updates as the data arrives.

It's the *consumer* half of a [Streaming API](/streaming-api/getting-started.md) integration (the Terra → your app path), and the code you copy to build it into your own product. A tiny token endpoint stands in for your backend so your API key never reaches the browser; the browser holds the WebSocket connection itself.

Terra Pulse is the `streaming-consumer-web-app` example in the [`terra-examples`](https://github.com/tryterra/terra-examples) repository. Scaffold your own copy with one command:

```bash
npm create tryterra-app -- --template streaming-consumer-web-app
cd my-app && npm install && npm run dev
```

## What it demonstrates

* [Opening a consumer connection](/streaming-api/terra-greater-than-your-backend.md) to Terra's WebSocket broker and receiving live `DISPATCH` readings
* Minting a single-use [developer token](/streaming-api/terra-greater-than-your-backend.md) from your backend, so your API key is never exposed to the browser
* The full connection lifecycle (`HELLO` → `IDENTIFY` → `READY` → `DISPATCH`) with jittered heartbeats and an explicit close-code policy
* Resilient reconnects with fresh tokens and exponential backoff, telling retryable drops apart from client-side protocol bugs
* Rendering readings live: per-user, per-type rolling buffers feeding a scrolling chart and sparkline stat cards, with honest connecting, waiting, and disconnected states

<figure><img src="/files/7axFtLLQsW0ZLBWOWu9z" alt="The Terra Pulse dashboard showing a live heart-rate chart over the last 60 seconds and stat cards for acceleration, distance, floors climbed, gyroscope, heart rate, speed, and steps, each with a sparkline"><figcaption><p>The dashboard renders every reading live, demultiplexed per user and per data type</p></figcaption></figure>

## How streaming works here

Real-time streaming has four parts: the **wearable**, a producer app (the dashboard's test-user generator or your own), Terra's **WebSocket broker**, and your backend, the **consumer**. Terra Pulse is the consumer: it reads the data back out of Terra. For the producer side, see [Your app → Terra](/streaming-api/your-app-greater-than-terra.md).

A consumer authenticates with a single-use **developer token**. Because minting that token needs your secret API key, it happens on your backend, never in the browser. The browser receives only the short-lived token and opens the WebSocket with it. Terra Pulse ships a \~50-line Express server as the smallest possible version of that backend; in your own product, any authenticated route that returns a token works.

## Tech stack

| Layer     | Technology                                                       |
| --------- | ---------------------------------------------------------------- |
| Frontend  | React 19 + TypeScript on Vite 7                                  |
| Styling   | Tailwind CSS v4, Terra design tokens, Poppins                    |
| Charts    | Recharts for the hero time-series, hand-rolled SVG sparklines    |
| Backend   | A \~50-line Express token endpoint (a stand-in for your backend) |
| Streaming | A framework-free `StreamingConsumer` WebSocket client, no SDK    |

## Run it yourself

{% hint style="info" %}
**Prerequisites:** [Node.js](https://nodejs.org/) 18+, a Terra **Dev ID** and **API key** from the [Terra dashboard](https://dashboard.tryterra.co) → API keys, and access to the [streaming page](https://dashboard.tryterra.co/dashboard/streaming?create=1) to create a test user. No wearable or hardware is required.
{% endhint %}

{% stepper %}
{% step %}

#### Scaffold and configure

```bash
npm create tryterra-app -- --template streaming-consumer-web-app
cd my-app
npm install
cp .env.example .env
```

Open `.env` and paste your Dev ID and API key. They stay server-side: the token server reads them to mint consumer tokens, and they are never sent to the browser.

```
TERRA_DEV_ID=your-dev-id
TERRA_API_KEY=your-api-key
```

{% endstep %}

{% step %}

#### Start the app

```bash
npm run dev
```

This runs the token server on port 4000 and the Vite dev server on port 5173 together. Open <http://localhost:5173>. While the consumer mints a token and opens the WebSocket, the header pill reads **Connecting…**; once Terra sends `READY`, it turns to **Live**.

<figure><img src="/files/AbW8tleLODGUL9ydZRRc" alt="The Terra Pulse app on load, showing a Connecting pill and a centered card with a spinner reading Connecting to Terra"><figcaption><p>On load the consumer mints a token and opens the WebSocket, then flips to Live</p></figcaption></figure>
{% endstep %}

{% step %}

#### Create a test user

The consumer only receives; something has to produce. On the [streaming page](https://dashboard.tryterra.co/dashboard/streaming?create=1) of the Terra dashboard, click **+ Test User** and choose **Generate test data**. Terra streams synthetic heart rate, steps, and more through the live API, with no hardware needed.

<figure><img src="/files/JybyWBKWchCy8fE1PTkr" alt="The Terra Pulse waiting state: a Live pill and a centered card reading Connected and listening, with a Create a test user button"><figcaption><p>Until a producer streams, the app waits and points you to create a test user</p></figcaption></figure>
{% endstep %}

{% step %}

#### Watch it stream

A user section appears the moment data starts flowing. The hero chart plots the selected metric over a scrolling 60-second window; a stat card per data type shows the latest value, its unit, and a sparkline. Click a card or a metric pill to change the hero chart, and use the search box to filter when several users stream at once.
{% endstep %}
{% endstepper %}

## How the Terra integration works

One file holds the integration, kept free of UI so it reads as reference code: `src/lib/consumer.ts`. On `HELLO`, it sends `IDENTIFY` with the token and connection type `1` (developer/consumer) before anything else (the server closes the socket if `IDENTIFY` doesn't arrive within 15 seconds), then starts a jittered heartbeat:

```typescript
case Op.HELLO: {
  // IDENTIFY goes out first (type 1 = consumer); the server closes 4000
  // if it doesn't arrive within 15s. Exactly once per connection.
  this.send({ op: Op.IDENTIFY, d: { token, type: IDENTIFY_TYPE_DEVELOPER } });
  // First beat after interval * random() so reconnecting clients don't sync.
  this.scheduleHeartbeat(interval * Math.random(), interval);
  break;
}
```

The token is single-use (Terra consumes it on a successful `IDENTIFY`), so the consumer mints a fresh one before *every* connection attempt, including reconnects. That token comes from your backend through an injected `mintToken` closure, which is the only place the client knows where tokens come from:

```typescript
// src/lib/stream.ts — swap this closure for your own backend to reuse the
// consumer unchanged.
mintToken: async () => {
  const res = await fetch("/api/token", { method: "POST" });
  const { token } = await res.json();
  return token;
},
```

To use the consumer in your own product, copy `src/lib/consumer.ts` unchanged and point `mintToken` at your endpoint.

## Explore the code

The [full source is on GitHub](https://github.com/tryterra/terra-examples/tree/main/packages/cli/templates/streaming-consumer-web-app), including the token server and the live dashboard components.

| Path                  | What it holds                                                                 |
| --------------------- | ----------------------------------------------------------------------------- |
| `src/lib/consumer.ts` | The `StreamingConsumer`: protocol lifecycle, heartbeat, reconnect; start here |
| `src/lib/protocol.ts` | Opcodes, close codes, frame types, and the parse guard                        |
| `src/lib/store.ts`    | Per-user/per-type rolling buffers and coalesced re-renders                    |
| `src/lib/stream.ts`   | The singleton wiring and the `mintToken` backend seam                         |
| `server/index.ts`     | The token endpoint, a stand-in for your own backend                           |
| `src/components/`     | The dashboard: status pill, stat cards, sparklines, and the live chart        |
