Terra -> Your backend
Set up and manage a consumer connection to the Terra WebSocket service to receive real-time health data.
This guide will walk you through the essential steps, from connecting and authenticating to managing heartbeats and receiving data.
Setting up a Consumer Connection
A consumer connection delivers your developer ID's real-time 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:
{
"op": 2,
"d": {
"heartbeat_interval": 40000
}
}After receiving this payload, the client must start sending heartbeat messages, as described below.
Heartbeating
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.
To send a heartbeat, send the following payload:
You'll always receive the following response (acknowledging your heartbeat):
The first heartbeat should be sent after
heartbeat_interval * jittermilliseconds, 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_ACKis received, the client should close the connection and establish a new one.If the server does not receive a heartbeat for more than one
heartbeat_interval, it will close the connection with code 4005.
Authenticating the Connection
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.
Once heartbeating is set up, the client must authenticate the connection by sending an IDENTIFY payload containing a token.
IDENTIFY payload:
The type field specifies the connection type:
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.
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.
Consumer session limit. A per-developer cap limits concurrent consumer connections. During the current rollout the cap is 1, so opening a second consumer connection closes that new connection with code 4002 — close the previous session first. The cap is a server-side setting and may be raised, so don't hard-code an assumption of exactly one consumer.
When authentication is successful, the server will respond with an Op 4 READY message:
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:
Each dispatch contains:
op: Opcode5(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: A sparse, time-ordered cursor — values generally increase over time, but gaps between consecutive values are normal and do not indicate lost data (treat it as a cursor, not a counter). Two readings produced in the same instant may arrive in either order. Use it to order DISPATCHes and as theafterbound for replay.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.
To backfill the gap on reconnect:
Re-IDENTIFY, then wait for the first live DISPATCH and note its
seq(call itfirstLive).Send a REPLAY with
after= the lastseqyou processed before the drop, andbefore=firstLive.Process the replayed DISPATCHes (they arrive as op 5 payloads), then resume handling live data.
If no live DISPATCH has arrived yet, there's nothing to backfill — REPLAY once one does.
REPLAY Command (Op 7)
after(required): Replay messages with sequence numbers greater than this value — typically the lastseqyou successfully processed before disconnecting.before(required): Replay messages with sequence numbers less than this value. Set it to theseqof the first live DISPATCH you receive after reconnecting, so you backfill exactly the gap. Both bounds are required — a REPLAY withoutbeforereturns no messages.
Bounds are exclusive — after: 28, before: 43 replays sequence numbers 29 through 42.
Replayed messages arrive as standard DISPATCH (op 5) payloads.
Replay reads from Terra's data warehouse, so a payload becomes replayable a few seconds after it is delivered live. If a REPLAY returns fewer messages than you expect, wait a moment and request it again. There is currently no retention limit on replayable data, though this may change in future.
Error Close Codes
The server may close your WebSocket connection with one of the following custom close codes:
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
Consumer session limit reached
Another consumer session is already active for your developer ID (current cap) — 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, or one not valid for your session type (e.g. SUBMIT on a consumer)
4005
Heartbeat expected but was not received
Send heartbeats within the heartbeat_interval window
The server may also close with the standard WebSocket codes 1000 (normal closure), 1003 (a malformed frame was received), or 1011 (internal server error).
For reconnect logic: 4000, 4003, 4004, and 1003 signal a client-side bug — fix your client rather than reconnecting, since a blind retry just loops on the same error. The rest are safe to reconnect after the remedy above (for 4001, always mint a fresh token; for 4002, close the other session first).
Opcode Reference
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
Last updated
Was this helpful?