Sockudo
Server

HTTP API

Pusher-compatible HTTP endpoints and request signing.

Endpoints

Realtime Publish

  • POST /apps/{appId}/events
  • POST /apps/{appId}/batch_events

Channel Queries

  • GET /apps/{appId}/channels
  • GET /apps/{appId}/channels/{channelName}
  • GET /apps/{appId}/channels/{channelName}/users (presence only)

User Management

  • POST /apps/{appId}/users/{userId}/terminate_connections

Health and Ops

  • GET /up
  • GET /up/{appId}
  • GET /usage
  • GET /metrics (metrics server port, default 9601)

Signed Query Parameters

HTTP API routes under /apps/* require Pusher-style query auth:

  • auth_key
  • auth_timestamp
  • auth_version
  • auth_signature
  • body_md5 for non-empty POST body

Timestamp window is ±10 minutes.

Signature Format

String to sign:

{METHOD}\n{REQUEST_PATH}\n{SORTED_QUERY_EXCLUDING_AUTH_SIGNATURE}

Where query params are lowercase-key sorted pairs joined with &.

Node Signing Example

sign.ts
import crypto from "node:crypto";

export function signPusherRequest({
  method,
  path,
  appKey,
  appSecret,
  body,
}: {
  method: "GET" | "POST";
  path: string;
  appKey: string;
  appSecret: string;
  body?: string;
}) {
  const auth_timestamp = Math.floor(Date.now() / 1000).toString();
  const params: Record<string, string> = {
    auth_key: appKey,
    auth_timestamp,
    auth_version: "1.0",
  };

  if (method === "POST" && body && body.length > 0) {
    params.body_md5 = crypto.createHash("md5").update(body).digest("hex");
  }

  const queryForSig = Object.keys(params)
    .sort()
    .map((k) => `${k}=${params[k]}`)
    .join("&");

  const stringToSign = `${method}\n${path}\n${queryForSig}`;
  const auth_signature = crypto
    .createHmac("sha256", appSecret)
    .update(stringToSign)
    .digest("hex");

  return { ...params, auth_signature };
}

Publish Example

publish.ts
import { signPusherRequest } from "./sign";

const path = "/apps/app-id/events";
const body = JSON.stringify({
  name: "my-event",
  channel: "my-channel",
  data: JSON.stringify({ hello: "world" }),
});

const auth = signPusherRequest({
  method: "POST",
  path,
  appKey: "app-key",
  appSecret: "app-secret",
  body,
});

const qs = new URLSearchParams(auth).toString();
await fetch(`http://127.0.0.1:6001${path}?${qs}`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body,
});

Useful Query Flags

  • GET /channels?filter_by_prefix=presence-&info=user_count
  • GET /channels/{name}?info=subscription_count,user_count,cache
Copyright © 2026