Public API

/signal.ashx · public API

The WebRTC signaling backbone. The conference page upgrades the HTTP connection here to a WebSocket and stays there for the duration of the call. A handful of plain HTTP query flags expose anonymous-safe metadata used by the status page, the public virtual numbers directory, and the camera registry.

Anonymous-safe. The endpoints listed below intentionally never reveal a caller number, an internal IP, or a tenant secret. Anything that does — ?stats, ?diallog, ?cdrlog, ?registrar, ?aliases, ?whitelist, the ?virtualnumbers CRUD — lives behind admin HMAC or an OIDC Bearer and is excluded here.

WS /signal.ashx #

Bidirectional WebRTC signaling channel. The browser conference client connects here on page load and remains for the lifetime of the call. Every signaling message after the upgrade is JSON-encoded.

Request

Standard WebSocket upgrade. No query parameters required for join. The server reads room + display-name + optional dial=<vnum> from the URL the page was loaded with and conveys them to the server via the first join frame.

Response

Server → client frame types include welcome (peer-id + minted ICE servers including TURN credentials when configured), peer-joined, peer-left, signal (carries SDP + ICE candidates from another peer), ring, ring-cleared, kicked, error. Client → server frame types include join, signal (to relay SDP/ICE to a specific peer), dial, leave, ping.

Errors

The endpoint also responds to a plain GET (no Upgrade header) with a one-line build-stamp text/plain response, useful for liveness probes.

Designed for the bundled browser client. Keepalive pings every ~25 s. Anonymous join is allowed; OIDC sign-in upgrades the connection's verifiedIdentity field which is broadcast to other peers as a green badge.

GET /signal.ashx?healthsummary=1 #

Public health snapshot consumed by status.html. Lists live rooms, current peer count, trunk status (with hostnames redacted), and CDR aggregates. Safe to expose — no caller numbers, no IPs.

Request

No request body. No auth. No cookies.

Response

Returns application/json shaped roughly:

{
  "build": "2026-06-01",
  "tenant": "phone.codeb.io",
  "rooms": [ { "id": "vnum-2000-...", "peers": 2 }, ... ],
  "trunks": [ { "id": "...", "registered": true, "lastSeenSec": 12 } ],
  "vnumCallsLast24h": 47
}

Errors

500 only on internal error.

Example

curl https://phone.codeb.io/signal.ashx?healthsummary=1
Intentionally low-detail. Anyone polling this endpoint can monitor uptime without revealing telephony state.

GET /signal.ashx?vnum-overview=1 #

List of public virtual numbers a visitor can dial right now. Each row carries the dial-in URL, the human name, and the mode (AI receptionist vs. TTS).

Request

No body. Optionally accepts an OIDC Bearer token; signed-in users see additional internal rows. Anonymous callers see only rows marked visibility=anonymous.

Response

Returns application/json:

{
  "virtualNumbers": [
    { "number": "1234", "name": "CodeBDemo",
      "mode": "gemini-live", "dialUrl": "https://phone.codeb.io/room.html?room=random&dial=1234&cam=off",
      "visibility": "anonymous" }
  ]
}

Example

curl https://phone.codeb.io/signal.ashx?vnum-overview=1
Used by status.html and the public virtual-numbers-overview.html directory page. Only rows marked visibility=anonymous appear publicly; operators who set a vnum name to personal data should mark it non-anonymous to keep it off this endpoint.

GET /signal.ashx?cameras=1 #

List of named cameras the tenant has wired up through go2rtc. Returned as {name, streamId} pairs for use as room=cam_<name>.

Request

No body. No auth.

Response

{
  "cameras": [ { "name": "lobby", "streamId": "lobby" } ]
}

Example

curl https://phone.codeb.io/signal.ashx?cameras=1
Returns an empty array if no ?cameras=… tenant configuration is set.

POST /signal.ashx?office-ring #

Internal SIP-bridge webhook the bridge POSTs to when an inbound PSTN call lands. Not intended for third-party integration — documented here for transparency and security review. HMAC-validated against the per-tenant bridge secret; only the local bridge can reach it.

Request

POST JSON body with the inbound call descriptor plus an X-Sig: HMAC-SHA256 header signing office-ring|<host>|<body> with the bridge shared secret.

Response

Returns { "delivered": N } where N is the number of CodeB Webphone instances that accepted the ring.

Errors

401 on signature mismatch.

Only the SIP bridge running on the same machine knows the secret. Documented here because it appears on the wire and any reviewer should know what it is.

POST /signal.ashx?office-ring-clear #

Internal SIP-bridge webhook the bridge POSTs when an inbound call is cancelled, hung up, or picked up elsewhere. Not intended for third-party integration — documented here for transparency and security review. Same HMAC envelope as ?office-ring.

Request

POST JSON with the same HMAC-SHA256 envelope as ?office-ring.

Response

{ "ok": true }

Errors

401 on signature mismatch.

Same bridge-HMAC posture as ?office-ring.
Need an admin endpoint? Admin-only and OIDC Bearer-gated routes are documented inside the admin UI itself (visible only to signed-in admins on this host). The public API set on this page is the surface you can integrate against without provisioning a CodeB user.

Questions? Ask us · Index: All public APIs