Appearance
Ws
Generated from
DialerDigital/corecommit0fc18008b3c8— specopenapi/dialer-v1.yamlv0.134.0. Do not edit by hand; see How this reference is built. Download the OpenAPI spec.
Endpoints
| Method | Path | Summary |
|---|---|---|
POST | /v1/ws/ticket | Mint an ephemeral single-use WebSocket ticket |
Mint an ephemeral single-use WebSocket ticket
http
POST /v1/ws/ticketMints the ONLY credential GET /v1/ws accepts (MT-SEC-05 / MT-SEC-10).
Browsers cannot set headers on a WebSocket handshake, so the credential must ride the query string — where it lands in access logs, history and Referer. This endpoint makes what rides there worthless: the ticket expires in seconds and is BURNED on first redemption. The long-lived bearer stays where it can go: this request's Authorization header.
The ticket inherits the EXACT principal of the bearer that minted it — a ddw_ agent bearer mints an agent-lane ticket, never a tenant one. Lane is encoded in the prefix (ddta_ agent, ddtt_ tenant) so the public edge can admit only the agent lane without a database lookup.
Redemption is single-use: a second GET /v1/ws with the same ticket is rejected, as is an expired one. Both answer the same 401 as an unknown ticket — the client learns nothing from the difference.
Socket cap (core#1285): a handshake refused with 429 reaches a browser as a bare close 1006, indistinguishable from a network drop. So this endpoint reads the SAME caps as the upgrade door — the widget token's first on the ddw_ lane, then the tenant's — and, when one is full, answers 429 too_many_connections with a machine-readable scope and mints nothing. It is an early warning, not the control: a ticket minted with room to spare is still refused at the upgrade if the cap fills before it is redeemed.
Responses
201
A freshly minted ticket. It is valid for expires_in seconds and for ONE handshake.
| Field | Type | Required | Description |
|---|---|---|---|
ticket | string | yes | Present it as GET /v1/ws?ticket=…. Never reusable. |
expires_at | string (date-time) | yes | Absolute expiry (RFC-3339). |
expires_in | integer | yes | Seconds of life. Deliberately small. |
401 — Unauthorized
Missing, unknown or revoked API key (or, on /v1/admin/*, a bad/unset admin token; on webhooks, a bad/missing signature). A dashboard session (ddu_) that a newer login of the same person ended answers the code session_superseded instead (tenant flag single_session_enforced), so the screen can say why it was signed out; a logout, an expired or an unknown token stay unauthorized.
json
{
"error": {
"code": "unauthorized",
"message": "invalid or revoked API key"
}
}403 — Forbidden
The tenant is suspended. A dashboard session (ddu_) of a tenant whose login policy (ADR 227) is in mode enforce answers 403 with the code login_policy_denied on a request from an address or at an hour the policy does not allow — the same body for both; the session is not revoked and a later request inside the policy goes through. A widget token (ddw_) of a seat is held to the same policy on every request (core#2406, flag widget_login_honors_policy). A synthetic (demo) tenant answers 403 with the code synthetic_tenant on every route that would reach a third party — manual dial, external transfer or conference leg, SMS send (core#2251, ADR #280 addendum). No payment changes it.
json
{
"error": {
"code": "forbidden",
"message": "tenant is suspended"
}
}429
The live-floor socket cap is full (too_many_connections); no ticket is minted. scope says which cap: widget_token (this ddw_ already holds its sockets) or tenant (the tenant's cap). Retry once a socket closes, not in a loop.
tenant
json
{
"error": {
"code": "too_many_connections",
"message": "live-floor connection limit reached for this tenant",
"scope": "tenant"
}
}widget_token
json
{
"error": {
"code": "too_many_connections",
"message": "live-floor connection limit reached for this widget token",
"scope": "widget_token"
}
}503
The ticket could not be minted (storage unavailable). Fail-closed — no socket is opened.
json
{
"error": {
"code": "unavailable",
"message": "could not mint a websocket ticket"
}
}