DELETE /v1/peers/:id
Forcibly close every WebSocket owned by a peer. Used by your backend when:
- A user signs out and you want to drop their connection immediately
- A moderator kicks a participant from a room
- You want to invalidate a token without waiting for
exp
Endpoint
DELETE https://rms.metered.ca/v1/peers/u_alice_123
Authorization: Bearer sk_live_xxx...
The peer ID goes in the URL path. URL-encode it if it has special characters.
Authorization
Any sk key for the app may kick any peer of that app. There's no separate "admin" / "kick" action — sk keys are server-side credentials by definition; if your backend has the sk_'s secret, it can disconnect its own users.
Response — 200 OK
{
"peerId": "u_alice_123",
"closed": 1
}
| Field | Type | Notes |
|---|---|---|
peerId | string | Echoes the requested peer ID. |
closed | int | Count of WebSocket connections closed. Could be 0 (peer wasn't online), 1 (typical), or >1 (same peer ID across multiple tabs / devices — we close them all). |
0 closed is NOT a 404. The request was well-formed and processed; the result is just zero sockets. If you asked "ensure they're not connected," 0 satisfies that.
What the kicked peer sees
The WebSocket closes with code 4020 AdminDisconnect — distinct from:
1001 GoingAway(server shutdown — peer should reconnect after a delay)4012 AccountSuspended(account-level disconnect — peer should stop)
Client SDKs should branch on 4020 to typically retry-with-a-fresh-token if the kick was a user-initiated logout, or stop entirely if the kick was a moderation action.
Errors
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | Peer ID empty, too long, or contains invalid chars |
| 401 | unauthorized | sk_ missing or invalid |
Cross-app isolation
A DELETE /v1/peers/alice request with acme-corp's sk_ key will only close alice connections from acme-corp's app. The same peer ID under a different app's namespace is untouched.
Example — curl
curl -X DELETE "https://rms.metered.ca/v1/peers/u_alice_123" \
-H "Authorization: Bearer sk_live_xxx..."
Example — Node.js (sign-out flow)
async function signOutUser(userId) {
// 1. Invalidate the user's server-side session record.
await db.sessions.deleteMany({ userId });
// 2. Drop their realtime-messaging WebSocket so it can't keep
// receiving messages on the kid's behalf.
await fetch(
`https://rms.metered.ca/v1/peers/${encodeURIComponent(userId)}`,
{
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.METERED_REALTIME_SK}` },
},
);
// 3. Redirect to login.
}
Why not just expire the token?
Tokens are stateless — the server can't invalidate one mid-flight without dropping the connection itself. DELETE /v1/peers/:id is how you do server-side revocation right now.
(Mid-session token rotation via WS upgrade is on the roadmap; today, closing-and-reconnecting is the right pattern.)