Important
Recommended authentication flow for browser clients:
- Get one-time ticket via REST (
GET /api/ws/ticket) with Bearer token - Connect via WebSocket using
?ticket=...
GET https://neuro.appstun.net/api/ws/ticket
WSS wss://neuro.appstun.net/api/ws
The WebSocket API provides real-time events for stream, schedule, and subathon updates. Authentication is required. Browser clients should use ticket-based auth. Server-to-server clients can use direct Authorization: Bearer auth during handshake.
GET https://neuro.appstun.net/api/ws/ticket
Generates a one-time WebSocket connection ticket. The ticket is valid for 30 seconds and can only be used once.
Required - Valid API token must be provided in Authorization header.
None
GET https://neuro.appstun.net/api/ws/ticket
Authorization: Bearer YOUR_API_TOKEN{
"data": {
"ticket": "f8c8e16a...",
"expiresIn": 30,
"usage": "Connect with wss://neuro.appstun.net/api/ws?ticket=<ticket>"
}
}WSS wss://neuro.appstun.net/api/ws
Establishes an authenticated WebSocket session. After connecting, clients can subscribe/unsubscribe to event types and receive real-time updates.
Required - Choose one method:
- Ticket (recommended):
wss://neuro.appstun.net/api/ws?ticket=YOUR_ONE_TIME_TICKET - Authorization header (server-to-server):
Authorization: Bearer YOUR_API_TOKEN
| Parameter | Type | Required | Description |
|---|---|---|---|
ticket |
string | No | One-time ticket (required for browser clients). |
GET wss://neuro.appstun.net/api/ws?ticket=YOUR_ONE_TIME_TICKETGET wss://neuro.appstun.net/api/ws
Authorization: Bearer YOUR_API_TOKEN{
"type": "addEvent",
"data": {
"eventType": "streamOnline"
}
}{
"type": "removeEvent",
"data": {
"eventType": "streamOnline"
}
}{
"type": "listEvents",
"data": {}
}{
"type": "welcome",
"data": {
"sessionId": "a1b2c3d4"
}
}{
"type": "event",
"data": {
"eventType": "scheduleUpdate",
"eventData": {
"year": 2026,
"week": 8,
"schedule": [],
"isFinal": true
},
"timestamp": 1766924114000
}
}{
"type": "addSuccess",
"data": {
"eventType": "streamOnline",
"subscribed": true
}
}{
"type": "removeSuccess",
"data": {
"eventType": "streamOnline",
"unsubscribed": true
}
}{
"type": "listEvents",
"data": {
"subscribedEvents": ["streamOnline"],
"availableEvents": [
"scheduleUpdate",
"subathonUpdate",
"subathonGoalUpdate",
"streamOnline",
"streamUpdate",
"streamOffline",
"secretneuroaccountOnline",
"streamRaidIncoming",
"streamRaidOutgoing"
]
}
}| Event Type | Description |
|---|---|
scheduleUpdate |
Weekly schedule was updated |
subathonUpdate |
Subathon state changed |
subathonGoalUpdate |
Subathon goal status changed |
streamOnline |
Stream started |
streamUpdate |
Stream metadata changed (throttled) |
streamOffline |
Stream ended |
secretneuroaccountOnline |
secretneuroaccount went live (same payload as streamOnline) |
streamRaidIncoming |
Raid incoming event |
streamRaidOutgoing |
Raid outgoing event |
{
"error": {
"code": "AU1",
"message": "Missing or invalid authorization header"
}
}{
"error": {
"code": "AU2",
"message": "Invalid or expired API token"
}
}{
"error": {
"code": "RL4",
"message": "Rate limit exceeded: Maximum 100 requests per minute"
}
}Handshake errors return plain text responses (not JSON):
404 Not Found(invalid WebSocket path)401 Missing authentication (ticket or token required)401 Invalid or expired ticket401 Invalid or expired token429 Connection limit reached (max 5)500 Authentication error500 Upgrade failed
{
"type": "invalid",
"data": {
"reason": "malformed",
"message": "Could not parse message."
}
}Possible reason values:
malformedunauthenticatedmissingEventtypeinvalidEventtypemissingTokeninvalidTokenauthError
- Ticket validity is 30 seconds and each ticket is one-time use
- Maximum 5 active WebSocket connections per user (unlimited tokens excluded)
streamUpdateevents are throttled to at most one broadcast every 2 seconds- Keepalive pings are enabled; idle timeout is 60 seconds
- WebSocket uses the next port after the HTTP API server (typically API port + 1)