Skip to main content

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
}
FieldTypeNotes
peerIdstringEchoes the requested peer ID.
closedintCount 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

HTTPerrorWhen
400invalid_requestPeer ID empty, too long, or contains invalid chars
401unauthorizedsk_ 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)

signout.js
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.)