REST API Overview
The REST control plane lives at:
https://rms.metered.ca/v1
Five endpoints, all authenticated with sk_live_… keys via Bearer header.
| Method | Path | Purpose |
|---|---|---|
POST | /v1/tokens | Mint a JWT for a peer (alternative to signing one yourself) |
GET | /v1/channels/:id/peers | List peers currently subscribed to a channel |
POST | /v1/channels/:id/publish | Publish to a channel from your backend, no WebSocket needed |
DELETE | /v1/peers/:id | Forcibly close every WebSocket owned by a peer (admin kick) |
GET | /v1/usage | Live concurrent count + period totals + plan limits |
Authentication
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- sk_ keys only.
pk_live_…keys are rejected — they're shipped to browsers and must never be able to mint tokens or kick peers. - The sk_ key's
channelPatternsandactionsapply to channel-scoped REST endpoints: a key that can't subscribe toapp_abc/*can'tGET /v1/channels/app_abc/room-1/peerseither.
Errors
All endpoints share an error shape:
{
"error": "channel_not_authorized",
"message": "channel not in key's allowed patterns: other-app/room-1"
}
error is the machine-readable code (stable); message is human-readable detail (informational).
Error codes by HTTP status
| HTTP | Error codes | When |
|---|---|---|
| 400 | invalid_request, channel_reserved | Malformed body, missing required fields, reserved channel name |
| 401 | unauthorized | Missing / malformed / unknown sk, pk used by mistake |
| 403 | channel_not_authorized, action_not_permitted | sk_ valid but lacking scope for this resource |
| 404 | not_found | Unknown endpoint |
| 413 | invalid_request | Body exceeds 32 KB cap (REST endpoint cap; WS layer caps at 64 KB) |
| 429 | (no error code; HTTP only) | Per-IP / per-key rate limit hit on REST |
| 503 | internal_error | Plan-cache failure, infra blip |
Same-status-different-code is timing-safe for auth: unauthorized covers unknown / revoked / wrong-type keys with the same response so attackers can't enumerate valid keyIds.
Where to find your sk_ key
- Dashboard → Realtime Messaging → Keys
- Click + Create key, choose type Secret
- Copy the signing secret when it's shown — it is never shown again
The sk_id_… part is the bearer-token value. The sk_secret_… part is the JWT signing material — only needed if you're minting JWTs yourself rather than calling POST /v1/tokens.
OpenAPI spec
Full machine-readable schema for the five endpoints:
Common patterns
| You want to | Use |
|---|---|
| Get a connect token to ship to a logged-in user | POST /v1/tokens — recommended; alternative is signing the JWT yourself |
| Send a server-side broadcast (chat moderation, AI-agent fan-out) | POST /v1/channels/:id/publish |
| Know who's currently in a room | GET /v1/channels/:id/peers |
| Implement a user-initiated logout that drops their WebSocket | DELETE /v1/peers/:id |
| Read live usage for a billing widget | GET /v1/usage |