Authentication
WebSocket Authentication
Authenticate the Rhombus event WebSocket: which credentials it accepts, where they go on the handshake, what a connection can see, partner connections, revocation, and how to read 401, 403 and 429 responses.
On this page
- Prerequisites
- Generating an API token
- Authentication parameters
- Connection URL format
- API key
- OAuth access token
- Federated token
- Partner API token
- Authentication flow
- Retrieving your organization UUID
- Example: Authenticated connection
- Connecting from a browser
- What a connection receives
- Partner API authentication
- Revocation
- Security best practices
- Troubleshooting
The event WebSocket (wss://ws.rhombussystems.com:8443/websocket) authenticates the HTTP upgrade request (the handshake). It accepts three credentials:
- An API key, standard or partner, in a request header.
- An OAuth access token with the
readscope, asAuthorization: Bearer <token>. - A federated token, as headers or query parameters. This is the one a browser can use.
Certificate (mTLS) keys aren't accepted on this connection.
Two different WebSockets. This page is about the event WebSocket: one STOMP connection per organization that delivers alerts and device changes. Live video and audio also stream over WebSockets, but those are per-camera URLs returned by getMediaUris and they authenticate differently. See Media and streams.
Prerequisites
- A credential:
- An API key created in the Rhombus Console with Auth Type: Api Token. See API keys and certificates.
- Or an OAuth access token from Sign in with Rhombus whose grant includes the
readscope. - Or a federated token minted by your backend.
- A client that can send that credential. API keys and OAuth tokens travel in request headers, and browsers can't set headers on a WebSocket handshake, so a web app either connects from its backend and relays events, or opens the socket in the browser with a federated token in the URL (see Connecting from a browser).
Certificate (mTLS) keys aren't accepted on the event WebSocket. If your REST integration uses a certificate key, create a standard API key (Auth Type Api Token) for the WebSocket, or mint a federated token.
Generating an API token
- Log in to the Rhombus console
- Go to Settings → Integrations & Developer Resources → API Tokens and click Add API Key
- Enter a Name, set Auth Type to Api Token, and select a Role. The role decides which events the connection receives (see What a connection receives).
- Copy and securely store the token
Authentication parameters
Credentials go on the handshake (the HTTP upgrade request). Values sent later in the STOMP CONNECT frame (login, passcode or custom headers) are ignored.
| Credential | x-auth-scheme (header or query parameter) | Credential goes in | Where |
|---|---|---|---|
| API key | api-token | x-auth-apikey | Header only |
| OAuth access token | Leave it out, or api-oauth-token | Authorization: | Header only |
| Federated token | federated-token | x-auth-ft | Header or query parameter |
Partner credentials use the same schemes (partner-api-token and partner-api-oauth-token remain as aliases) and add one header:
| Name | Where | Value | Required |
|---|---|---|---|
x-auth-org | Header only | The client organization's UUID | Yes, for every partner credential (see Partner API authentication) |
An API key or OAuth token sent as a query parameter isn't read, and the handshake fails with 401.
Connection URL format
| Region | URL |
|---|---|
| US | wss://ws.rhombussystems.com:8443/ |
| EU | wss://ws.eu.rhombussystems.com:8443/ |
Use port 8443 and TLS 1.2 or later. Credentials are region-bound like every Rhombus credential, so an EU key or token only works on the EU host. See US & EU Regions.
API key
wss://ws.rhombussystems.com:8443/websocket?x-auth-scheme=api-tokenSend the key in the x-auth-apikey header.
OAuth access token
wss://ws.rhombussystems.com:8443/websocketSend Authorization: Bearer <access token>. A token whose grant doesn't include the read scope is refused with 403 PERMISSION_REQUIRED and WWW-Authenticate: Bearer error="insufficient_scope", scope="read".
Federated token
wss://ws.rhombussystems.com:8443/websocket?x-auth-scheme=federated-token&x-auth-ft=TOKENThe token has the role of the credential that minted it. If it was minted with domain, the handshake's Origin must match that origin, or it's refused with 403 DOMAIN_NOT_ALLOWED.
Partner API token
wss://ws.rhombussystems.com:8443/websocket?x-auth-scheme=api-tokenSend the partner key in x-auth-apikey and the client organization's UUID in the x-auth-org header. See Partner API authentication.
Authentication flow
1. Build the URL (x-auth-scheme as a query parameter or header)
│
▼
2. Add the credential: x-auth-apikey, Authorization: Bearer, or x-auth-ft
(and x-auth-org for partner credentials)
│
▼
3. Open the WebSocket (wss, port 8443)
│
┌────┴─────────────────────────┐
▼ ▼
Credentials accepted Rejected before the upgrade:
│ 401 (credential missing, unknown,
▼ expired or revoked),
4. Send STOMP CONNECT 403 (valid credential, not allowed),
│ 429 (rate limited)
▼
5. Receive STOMP CONNECTED → subscribeNo prior HTTP session is needed. The credential is checked at the handshake and again about once a minute while the connection is open (see Revocation).
Retrieving your organization UUID
Topic names include your organization UUID. Retrieve it with the REST API:
curl -X POST https://api2.rhombussystems.com/api/org/getOrgV2 \
-H "x-auth-scheme: api-token" \
-H "x-auth-apikey: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'The org UUID is at org.uuid in the response.
Example: Authenticated connection
import websocket
API_TOKEN = "YOUR_API_KEY"
url = "wss://ws.rhombussystems.com:8443/websocket?x-auth-scheme=api-token"
headers = {
"x-auth-apikey": API_TOKEN
}
ws = websocket.create_connection(url, header=headers)
print("WebSocket connected")const WebSocket = require("ws");
const API_TOKEN = "YOUR_API_KEY";
const url = "wss://ws.rhombussystems.com:8443/websocket?x-auth-scheme=api-token";
const ws = new WebSocket(url, {
headers: {
"x-auth-apikey": API_TOKEN,
},
});
ws.on("open", () => {
console.log("WebSocket connected");
});import (
"fmt"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
apiToken := "YOUR_API_KEY"
url := "wss://ws.rhombussystems.com:8443/websocket?x-auth-scheme=api-token"
headers := http.Header{}
headers.Set("x-auth-apikey", apiToken)
dialer := websocket.Dialer{
HandshakeTimeout: 10 * time.Second,
}
conn, _, err := dialer.Dial(url, headers)
if err != nil {
log.Fatal("Connection failed:", err)
}
fmt.Println("WebSocket connected")import websocket
ACCESS_TOKEN = "YOUR_OAUTH_ACCESS_TOKEN" # its grant must include the read scope
url = "wss://ws.rhombussystems.com:8443/websocket"
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}"
}
ws = websocket.create_connection(url, header=headers)
print("WebSocket connected")// federatedToken comes from your backend; never put an API key in the page
const url = new URL("wss://ws.rhombussystems.com:8443/websocket");
url.searchParams.set("x-auth-scheme", "federated-token");
url.searchParams.set("x-auth-ft", federatedToken);
const ws = new WebSocket(url);
ws.onopen = () => console.log("WebSocket connected");Connecting from a browser
- Use a federated token in the URL. Your backend mints it with
generateFederatedSessionTokenand hands it to the page, which opens the socket withx-auth-scheme=federated-token&x-auth-ft=<token>(see the browser example above). Mint it for the viewer's session (durationSecdefaults to one hour and can't exceed 24 hours), readexpiresAtMsfrom the response, and mint a new one before it expires. When the session ends, revoke it withPOST /api/org/revokeFederatedSessionToken, from your backend or from the page, authenticated with the token itself. See Federated tokens. - Bind the token to your page with
domain. A token minted withdomain(an absolute origin such ashttps://app.example.com) is accepted only when the handshake'sOriginmatches it. - A signed-in Rhombus console session only works from the Rhombus console. A handshake that relies on the console's session cookies is accepted only from a Rhombus console origin, so a page on your own site can't use a viewer's console session. Handshakes with an API key, an OAuth token, a federated token or the Rhombus mobile app aren't origin-checked; a federated token's
domainis its own origin check.
What a connection receives
- Events are filtered by the credential's role. An API key uses its role. An OAuth token acts with the signed-in user's role, limited to the token's scopes. A federated token has the role of the credential that minted it. A full-access role receives every event in the organization. A role limited to some locations or devices receives only events for those; the rest are dropped without an error. If a connection is quiet, check the role before anything else.
- Policy alerts follow the same rule. A policy alert is delivered when the role can access the alert's device or its location. An alert with neither goes only to full-access roles.
- Entity types without an access check go only to full-access roles. A limited role receives the entity types the server can check against its devices and locations, and no others.
- The connection is receive-only. Clients subscribe; they don't send commands over it. Use the REST API to act.
- Subscribing to another organization's topic is refused. The server sends a STOMP
ERRORframe and closes the socket with code1002.
Partner API authentication
If you're a Rhombus partner receiving events for a client organization:
- Use your partner key with the
api-tokenscheme (partner-api-tokenworks as an alias) - Send the client's
orgUuidin thex-auth-orgheader. It's required: a partner credential without it is refused with 403PERMISSION_REQUIRED. A blank value counts as absent, a query parameter isn't read, and a value that isn't a valid UUID gets 400INVALID_AUTH_ORG. - Open one connection per client organization;
x-auth-orgis fixed for the life of the connection
url = (
"wss://ws.rhombussystems.com:8443/websocket"
"?x-auth-scheme=api-token"
)
headers = {
"x-auth-apikey": partner_api_token,
"x-auth-org": client_org_uuid,
}
ws = websocket.create_connection(url, header=headers)If your partner account isn't authorized for that client, the handshake fails with 403 PARTNER_CLIENT_ACCESS_DENIED.
Revocation
Deleting a key, changing its role, revoking an OAuth grant, or revoking a federated token (or the credential that minted it) takes effect within 60 seconds. An open connection that used the credential receives a STOMP ERROR frame and is closed with code 1008. After a role change, reconnecting picks up the new role. After a delete or revoke, every handshake with that credential fails with 401, so don't reconnect in a loop after a 1008: check the credential first.
Security best practices
- Keep keys on your server. Never ship an API key to a browser or mobile app; relay events from your backend, or give the browser a short-lived federated token.
- Use a dedicated credential with the narrowest role that still sees the devices you need. See Permissions and key rotation.
- Ask only for the OAuth scopes you need. The event WebSocket needs
read. - Reconnect with backoff. Handshakes count toward your organization's rate limit; a tight reconnect loop gets
429responses.
Troubleshooting
A handshake refused before the upgrade returns an HTTP status, an x-rhombus-txid header and the same JSON body as every Rhombus host (see Errors):
{
"msg": "x-auth-org is required for partner credentials",
"error": true,
"errorCode": "PERMISSION_REQUIRED",
"errorMsg": "Your role does not have the required permission on the requested resource.",
"status": 403,
"timestamp": 1791367200000,
"txid": "6f1c2a9e4b7d"
}Read errorCode for the reason. Quote txid when you contact support.
| Error | Cause | Solution |
|---|---|---|
HTTP 401 UNAUTHENTICATED | No credential reached the server: x-auth-scheme is missing, the key or OAuth token was sent as a query parameter, or the scheme is a certificate scheme (api, partner-api) | Send the scheme and the credential as the table above shows |
HTTP 401 INVALID_CREDENTIAL | The key or token is malformed, unknown (a deleted API key, for example), or from the other region | Use an existing credential from the same region as the host |
HTTP 401 CREDENTIAL_EXPIRED or CREDENTIAL_REVOKED | The key or token expired, or it was revoked (an OAuth token whose grant was revoked, for example) | Create a new key, refresh or re-authorize the OAuth token, or mint a new federated token |
HTTP 401 CERTIFICATE_MISMATCH | A certificate key was sent with api-token | Use a standard API key (Auth Type Api Token) |
HTTP 403 PERMISSION_REQUIRED | A partner credential without x-auth-org, or an OAuth token without the read scope (the response names the scope in WWW-Authenticate) | Send x-auth-org; ask the user to authorize the read scope |
HTTP 403 PARTNER_CLIENT_ACCESS_DENIED | The partner isn't authorized for the x-auth-org organization | Check the partner's access to the client |
HTTP 403 DOMAIN_NOT_ALLOWED | A federated token minted with domain, opened from another origin | Mint the token with your page's exact origin |
HTTP 400 INVALID_AUTH_ORG | x-auth-org isn't a valid organization UUID | Send the client organization's UUID |
HTTP 429 RATE_LIMITED | Too many handshakes or API calls for the organization | Wait for Retry-After seconds, then reconnect with backoff |
| Connected, but no events arrive | The credential's role doesn't cover the devices or locations | Use a role that covers the devices (see What a connection receives) |
STOMP ERROR and close code 1008 | The credential was deleted, revoked or had its role changed while connected | Check the credential before reconnecting (see Revocation) |
STOMP ERROR and close code 1002 | Subscribed to a topic outside the credential's organization, or sent a frame the server doesn't accept | Subscribe with the organization UUID the credential belongs to (or the x-auth-org client) |
| Connection drops after about 30 seconds | No heartbeats | Send STOMP heartbeats (see Connection Lifecycle) |
| Connection timeout | Network or firewall | Allow outbound TLS to port 8443 |