# Terra -> Your backend

This guide will walk you through the essential steps, from connecting and authenticating to managing heartbeats and receiving data.

## Setting up a Consumer Connection

Each developer ID is linked to a single data stream. To set up your consumer connection to the Terra WebSocket service, follow the steps below.

**WebSocket endpoint:** `wss://ws.tryterra.co/connect`

***

## Connecting to the WebSocket

Once you open a WebSocket connection to the server, you will immediately receive an `Op 2 HELLO` payload. This payload contains the `heartbeat_interval` value (in milliseconds), which the client will use to maintain the connection.

Example payload:

```json
{
  "op": 2,
  "d": {
    "heartbeat_interval": 40000
  }
}
```

After receiving this payload, the client must start sending heartbeat messages, as described below.

***

## Heartbeating

{% hint style="info" %}
To keep the WebSocket connection alive, the client **needs** to send regular heartbeats.

This lets the server know the connection is still active and waiting for data.
{% endhint %}

To send a heartbeat, send the following payload:

```json
{
  "op": 0
}
```

You'll always receive the following response (acknowledging your heartbeat):

```json
{
  "op": 1
}
```

{% hint style="danger" %}

* The first heartbeat should be sent after `heartbeat_interval * jitter` milliseconds, where jitter is a random value between 0 and 1.
* After the first heartbeat, continue sending heartbeats **at most** at the interval specified in the HELLO payload.
* If no `HEARTBEAT_ACK` is received, the client should close the connection and establish a new one.
* If the server does not receive a heartbeat within the `heartbeat_interval` window, it will close the connection with code **4005**.
  {% endhint %}

***

## Authenticating the Connection

{% hint style="warning" %}
You must send an IDENTIFY payload **within 15 seconds** of connecting. If the server does not receive IDENTIFY in time, it will close the connection with code **4000**.
{% endhint %}

Once heartbeating is set up, the client must authenticate the connection by sending an `IDENTIFY` payload containing a token.

#### IDENTIFY payload:

```json
{
  "op": 3,
  "d": {
    "token": "your_developer_token_here",
    "type": 1
  }
}
```

The `type` field specifies the connection type:

| Type | Name      | Description                                                                                  |
| ---- | --------- | -------------------------------------------------------------------------------------------- |
| `0`  | USER      | Producer connection (mobile SDKs sending data). Uses a token from `POST /auth/user`.         |
| `1`  | DEVELOPER | Consumer connection (your backend receiving data). Uses a token from `POST /auth/developer`. |

For consumer connections (this guide), use `type: 1` with a developer token.

{% hint style="info" %}
**Tokens are single-use.** Each token is deleted from the server after a successful IDENTIFY. If your connection drops, you must generate a new token before reconnecting.
{% endhint %}

{% hint style="warning" %}
**One connection per developer.** Only one active consumer connection is allowed per developer ID. If you attempt to connect while a previous session is still active, the new connection will be closed with code **4002**. Close the previous connection first.
{% endhint %}

When authentication is successful, the server will respond with an `Op 4 READY` message:

```json
{
  "op": 4
}
```

***

## Listening for Data Updates

Once the connection is established and authenticated, data from your connected users will be streamed to you in real time.

When new data is available, an `Op 5 DISPATCH` payload is sent:

```json
{
  "op": 5,
  "d": {
    "ts": "2022-05-04T10:26:11.268507+01:00",
    "val": 95
  },
  "uid": "user_id_here",
  "seq": 73,
  "t": "HEART_RATE"
}
```

Each dispatch contains:

* `op`: Opcode `5` (DISPATCH).
* `uid`: The Terra user ID of the user whose device produced this data.
* `t`: The data type (e.g., `"HEART_RATE"`, `"STEPS"`, `"ACCELERATION"`, `"ECG"`, `"HRV"`, `"CALORIES"`, `"LOCATION"`, `"GYROSCOPE"`, etc.).
* `seq`: Monotonically increasing sequence number per developer. Used for ordering and [replay](#requesting-missed-data).
* `d`: The data payload:
  * `ts`: ISO 8601 timestamp of the reading.
  * `val`: Scalar value (e.g., heart rate BPM, step count). Present for single-value types.
  * `d`: Array of doubles (e.g., `[x, y, z]` for accelerometer/gyroscope, or `[lat, lng]` for location). Present for multi-axis types.

***

## Requesting missed Data

If your connection drops and you miss some data, you can request it using the REPLAY command once you reconnect.

**REPLAY Command (Op 7)**

```json
{
  "op": 7,
  "d": {
    "after": 28,
    "before": 43
  }
}
```

* `after` (required): Replay messages with sequence numbers **greater than** this value.
* `before` (optional): Replay messages with sequence numbers **less than** this value. Omit to replay everything after `after`.

{% hint style="info" %}
Bounds are **exclusive** — `after: 28, before: 43` replays sequence numbers 29 through 42.

Replayed messages arrive as standard DISPATCH (op 5) payloads.
{% endhint %}

{% hint style="warning" %}
**2-day replay window.** Data payloads older than 2 days are purged from the server. REPLAY only works for data within this window.
{% endhint %}

***

## Error Close Codes

The server may close your WebSocket connection with one of the following custom close codes:

| Code     | Reason                                  | What to do                                                               |
| -------- | --------------------------------------- | ------------------------------------------------------------------------ |
| **4000** | Identify expected but was not received  | Send IDENTIFY within 15 seconds of connecting                            |
| **4001** | Improper token has been passed          | Token is invalid or expired — generate a new one                         |
| **4002** | Duplicate connection                    | Another session is already active for your developer ID — close it first |
| **4003** | Multiple IDENTIFY payloads received     | Don't send IDENTIFY more than once per connection                        |
| **4004** | Invalid opcode was received             | You sent an opcode the server doesn't recognize for your connection type |
| **4005** | Heartbeat expected but was not received | Send heartbeats within the `heartbeat_interval` window                   |

***

## Opcode Reference

| Opcode | Name           | Direction       | Description                                       |
| ------ | -------------- | --------------- | ------------------------------------------------- |
| 0      | HEARTBEAT      | Client → Server | Keep-alive ping                                   |
| 1      | HEARTBEAT\_ACK | Server → Client | Keep-alive acknowledgement                        |
| 2      | HELLO          | Server → Client | Sent on connection, contains `heartbeat_interval` |
| 3      | IDENTIFY       | Client → Server | Authentication with token and connection type     |
| 4      | READY          | Server → Client | Authentication successful                         |
| 5      | DISPATCH       | Server → Client | Real-time data payload                            |
| 6      | SUBMIT         | Client → Server | Data submission (producer connections only)       |
| 7      | REPLAY         | Client → Server | Request missed data by sequence range             |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tryterra.co/streaming-api/terra-greater-than-your-backend.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
