---
title: "Get Websocket Ticket"
method: GET
path: "/auth/ws-ticket"
tags: ["auth", "authentication"]
---

# Get Websocket Ticket

`GET /auth/ws-ticket`

A one-time, 60-second ticket to authenticate a WebSocket handshake.

A browser cannot put a header on a WebSocket handshake, so the credential
has to ride in the URL — and URLs are logged, by us and by every proxy in
between. Until now what rode there was the bearer JWT itself, good for 24
hours (see app/core/ws_ticket.py for what that cost us). This hands out an
opaque value instead: it names nobody, is destroyed when spent, and expires
in a minute whether it is spent or not.

Returning 503 rather than falling back to the JWT is deliberate. The
fallback belongs to the client, which still has a working bearer token and
will use it; quietly returning one from an endpoint called 'ws-ticket'
would put the thing we are trying to keep out of URLs straight back into
one, with nothing in the logs to say it had happened.

## Query parameters

- `scope` string

## Response `200`

Successful Response

- unknown

## Other responses

- `422` — Validation Error

## Changes

> 25 revisions in range; 1 not diffed.

- **2026-09-17** `3630ae19ea1e` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/fluentea/apis/aurqa-language-learning-platform-refactored/changes/auth/ws-ticket/get.md)

---

[API](https://skmtc.dev/fluentea/apis/aurqa-language-learning-platform-refactored.md) · [All operations](https://skmtc.dev/fluentea/apis/aurqa-language-learning-platform-refactored/llms.txt) · [OpenAPI document](https://skmtc.dev/fluentea/apis/aurqa-language-learning-platform-refactored/revisions/be75c0eba493?raw)
