Skip to main content

Quickstart

Connect, subscribe to a channel, and publish a message. Under 30 lines of client code, using raw WebSockets and a JWT.

Using JavaScript / TypeScript?

Skip the raw WebSocket path and use the @metered-ca/peer SDK instead. It does framing, ack correlation, reconnect, perfect-negotiation WebRTC, and TURN credential injection for you. Five lines to connect + subscribe + publish.

This quickstart is for any other stack (Go, Python, Java, Swift, Kotlin, Rust, Unity, etc.) — or if you want to see the underlying wire protocol.

This guide gets you talking to the server end-to-end. For richer integration patterns (TURN credentials in the token, per-message peer metadata, server-side publish), jump to the Use Case Guides after this.

Prerequisites

  1. A Metered account — sign up if you don't have one.
  2. An App with Signalling enabled. From the dashboard, click Signalling in the sidebar and complete the one-question onboarding survey.
  3. An sklive key created from Dashboard → Realtime Messaging → Keys → Create key, type secret. Copy the signing secret when it's shown — it is never shown again.

You'll also need Node.js (or any JWT-capable backend) for token minting, and the ws package on the client side for this quickstart.

Step 1 — Mint a JWT (server-side)

Your backend signs an HS256 JWT with the sk_'s signing secret. The signalling server verifies it on connect.

mint-token.js
const jwt = require("jsonwebtoken");

const KEY_ID = "sk_id_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // from dashboard
const SECRET = "sk_secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // shown once at create

function mintToken(peerId, channels = []) {
return jwt.sign(
{
iss: "your-backend",
sub: peerId,
channels, // channels this peer may interact with
permissions: ["publish", "subscribe", "presence", "send"],
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
},
SECRET,
{ algorithm: "HS256", header: { alg: "HS256", kid: KEY_ID } },
);
}

console.log(mintToken("alice", ["app_abc/room-1"]));
Use the REST API instead

If you'd rather not embed JWT-signing into your backend, call POST /v1/tokens with the sk_ as Bearer auth — Metered mints the JWT for you with the same claims. See REST API → Tokens.

Step 2 — Connect from the client

client.js
const WebSocket = require("ws"); // browser code uses the native WebSocket constructor

const token = "<jwt from step 1>";
const ws = new WebSocket(`wss://rms.metered.ca/v1?token=${encodeURIComponent(token)}`);

ws.on("open", () => {
console.log("connected, waiting for welcome");
});

ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
console.log("←", msg);

if (msg.type === "welcome") {
// server confirmed our auth. Subscribe to the room.
ws.send(JSON.stringify({
type: "subscribe",
channel: "app_abc/room-1",
requestId: "sub-1",
}));
}

if (msg.type === "ack" && msg.requestId === "sub-1") {
// subscribe succeeded. Publish a hello.
ws.send(JSON.stringify({
type: "publish",
channel: "app_abc/room-1",
data: { hello: "world" },
requestId: "pub-1",
}));
}
});

ws.on("close", (code, reason) => {
console.log("closed", code, reason.toString());
});

Run two copies of client.js with different peerIds and you'll see them broadcast to each other.

What you saw

  1. Connect URL carries the JWT as ?token=. The server verifies the JWT before completing the WebSocket handshake.
  2. welcome is the first message the server sends. It confirms auth and carries server metadata (your peerId, serverTime, maxMessageSize, plus an optional opaque metadata pass-through from the JWT — WebRTC integrations conventionally put iceServers inside metadata).
  3. subscribe joins a channel. The server replies with ack (success) or error (e.g. channel not in your key's channelPatterns).
  4. publish broadcasts to every other subscriber on that channel. The server delivers it as a message event to each.
  5. Closing the WebSocket triggers peer-left presence events for every channel this peer was subscribed to.

Next