7.6 KiB
Aether API V3 WebSocket Integration Guide
This guide explains how to implement real-time communication using the Aether API V3 WebSocket protocol. V3 introduces granular routing, strict message schemas, and improved multi-tenant isolation compared to previous versions.
1. Key Improvements (V2 vs V3)
| Feature | WebSocket V2 (Legacy) | WebSocket V3 (Modern) |
|---|---|---|
| URL Prefix | /ws/ or /ws_redis/ |
/v3/ws/ |
| Routing | Global: Every client receives every message. | Granular: Redis filters messages before sending. |
| Performance | Low efficiency at scale (Python filtering). | High efficiency (Redis native pub/sub). |
| Schema | Loose JSON objects. | Strict Pydantic-validated models. |
| Presence | None / Manual. | Automatic Redis-backed presence sets. |
2. Connection Strategy
A. Endpoint URL
The V3 WebSocket path requires both a group_id and a client_id. Both are treated as opaque unique strings by the backend — no specific format is enforced.
wss://[api_domain]/v3/ws/group/{group_id}/client/{client_id}
group_id — identifies the shared channel (e.g., an event ID, a room name, or a Vision ID random string). All clients using the same group_id receive group-targeted messages together.
client_id — uniquely identifies this specific connection. Common choices:
Date.now()timestamp (e.g.1773266158823) — simple and collision-resistant for short-lived sessions- A UUID (
crypto.randomUUID()) - A Vision ID random string if you need the client to be addressable by a known database identity
Use
ws://for local development andwss://in production (any HTTPS site). The Nginx config must include the Upgrade block — see Section 6.
B. Authentication
Browsers cannot set custom HTTP headers on WebSocket connections. Pass the API Key and account context as query parameters instead:
| Parameter | Purpose | Example |
|---|---|---|
api_key |
Entry Ticket (machine auth) | ?api_key=<your_app_key> |
jwt |
Visa (user / account context) | &jwt=<token> |
x_account_id |
Alt account context | &x_account_id=<account_id> |
Full example URL:
wss://dev-api.oneskyit.com/v3/ws/group/{group_id}/client/{client_id}?api_key=<key>&jwt=<token>
C. Connection Example (TypeScript)
const group_id = "event_abc123"; // Any unique string identifying the shared channel
const client_id = String(Date.now()); // Any unique string identifying this client connection
const api_key = import.meta.env.VITE_API_KEY;
const jwt = getSessionToken(); // your JWT helper
const ws_url = `wss://dev-api.oneskyit.com/v3/ws/group/${group_id}/client/${client_id}?api_key=${api_key}&jwt=${jwt}`;
const socket = new WebSocket(ws_url);
socket.onopen = () => {
console.log("Connected to Aether WS V3");
};
3. The V3 Message Schema
All messages sent and received over V3 must follow the standardized WS_Message_V3 structure.
Message Fields
| Field | Type | Required | Description |
|---|---|---|---|
version |
string | Auto | Always "3". Set by server. |
msg_type |
string | Yes | 'msg', 'cmd', 'heartbeat', 'presence' |
target |
string | Yes | 'direct', 'group', 'broadcast', 'echo' |
from_id |
string | Auto | Server fills this from the URL path — do not send. |
to_id |
string | Conditional | Target Client ID. Required when target is 'direct'. |
group_id |
string | Auto | Server fills this from the URL path — do not send. |
cmd |
string | No | Specific action keyword (e.g., 'RELOAD'). |
msg |
string | No | Human-readable text content. |
payload |
object | No | Flexible key-value data. |
sent_at |
string | Auto | ISO 8601 Timestamp. Set by server. |
Frontend tip: Only send
msg_type,target, and whatever content fields you need (msg,cmd,payload,to_id). The server enforcesfrom_id,group_id, andsent_atfrom the connection context, preventing spoofing.
4. Message Targeting Logic
V3 uses the target field to determine which Redis channel to use, ensuring only the intended recipients receive the data.
A. Group Broadcast
Sends the message to every client connected to the same group_id.
{
"msg_type": "msg",
"target": "group",
"msg": "Hello team!"
}
B. Direct Message (DM)
Sends the message to one specific client ID, regardless of their group.
{
"msg_type": "msg",
"target": "direct",
"to_id": "target_client_random_id",
"msg": "Private message just for you."
}
C. System Broadcast
Sends the message to every connected client on the platform (use sparingly).
{
"msg_type": "cmd",
"target": "broadcast",
"cmd": "MAINTENANCE_WARNING"
}
D. Echo
Sends the message back only to the sender (useful for testing round-trip latency).
{
"msg_type": "msg",
"target": "echo",
"msg": "Ping!"
}
5. Specialized Message Types
Commands (cmd)
Used for remote control or orchestration.
{
"msg_type": "cmd",
"target": "group",
"cmd": "RELOAD_UI",
"payload": { "force": true }
}
Heartbeats (heartbeat)
Keep the connection alive and refresh presence in the backend. Should be sent every 30-60 seconds.
- The server intercepts
heartbeatmessages and refreshes the Redis presence TTL (1 hour window) before echoing back. - Without periodic heartbeats, a client idle for >1 hour may disappear from the presence set even while still connected.
- Use
target: 'echo'so the server sends the heartbeat straight back — useful for measuring round-trip latency.
{
"msg_type": "heartbeat",
"target": "echo"
}
6. Infrastructure Requirements (Nginx)
Unlike standard REST endpoints, WebSockets require explicit "Upgrade" handling in the Nginx gateway. If you are deploying to a new server, ensure the following block is present in your Nginx configuration:
location /v3/ws {
proxy_pass http://fastapi_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_read_timeout 2100s; # Match your app's max heartbeat/session time
}
7. Common Pitfalls & Troubleshooting
- HTTP 404 Errors: This almost always means Nginx is missing the
location /v3/wsblock and is trying to serve the request as a static file from the disk. - HTTP 400 Errors: Check your
Hostheader. Nginx routes requests based on theserver_namedirective. If you connect to an IP or a non-standard hostname (likelocalhost), ensure it is explicitly listed in your Nginx config. - Connection Drops: If the connection drops exactly after 60 seconds, check your Nginx
proxy_read_timeout. It should be set high (e.g.,2100s) to allow for long-lived WebSocket sessions.
8. Migration Guide (V2 to V3)
If you are upgrading from the legacy V2 WebSocket (/ws/group/...):
- Change the URL: Prepend
/v3/to your WebSocket path. - Wrap your JSON: In V2, you might have sent
{"msg": "hi"}. In V3, this must be{"msg_type": "msg", "target": "group", "msg": "hi"}. - Use unique string IDs: Both
group_idandclient_idin the path are opaque strings — any unique value works (timestamp, UUID, Vision ID random string). Just don't use raw database integer IDs. Forto_idin direct messages, use whateverclient_idthat target client registered with. - Listen for
msg_type: Update your frontend handlers to switch logic based on themsg_typefield instead of proprietary keys.