Skip to content

Auction Room Frontend Integration Guide

Use one WebSocket endpoint for every auction realtime role:

wss://api.example.com/ws

Subscribers, companies, and admins all connect to the same URL. The server authorizes each message from the authenticated JWT context.

Authentication

Native and mobile clients can send the token as an HTTP header:

Authorization: Bearer <access-token>

Browser clients usually cannot set custom WebSocket headers, so send the token through Sec-WebSocket-Protocol:

Sec-WebSocket-Protocol: websocket.v1, bearer.<access-token>

auction-room.v1 is also accepted for protocol compatibility.

Invalid or missing tokens close the socket with policy violation code 1008.

Message Contract

Every client message must be JSON:

{
  "type": "auction.subscribe",
  "requestId": "request-id",
  "data": {}
}

Rules:

  • type is required and must match a registered event
  • requestId is optional but recommended for correlating acknowledgements
  • data is validated per event
  • Use data, not the removed payload field

Successful command acknowledgements look like:

{
  "type": "ack",
  "requestId": "request-id",
  "eventType": "auction.subscribe",
  "data": {}
}

Errors look like:

{
  "type": "error",
  "requestId": "request-id",
  "code": "FORBIDDEN",
  "message": "Safe client-facing message."
}

Known error codes:

BAD_REQUEST
UNAUTHORIZED
FORBIDDEN
NOT_FOUND
CONFLICT
RATE_LIMITED
INTERNAL
  1. Open /ws with a valid access token.
  2. Send auction.subscribe for the active cycle.
  3. Wait for the initial server events.
  4. Render the latest auction.snapshot and presence.snapshot.
  5. Send commands such as auction.bid.place.
  6. Apply pushed auction events such as bid.created.
  7. Send auction.unsubscribe when leaving the screen.
  8. Reconnect and resubscribe after network loss.

Subscribe As Subscriber

Subscribers include enrolledSubscriberId:

{
  "type": "auction.subscribe",
  "requestId": "subscribe-001",
  "data": {
    "cycleId": "cycle-id",
    "enrolledSubscriberId": "enrolled-subscriber-id"
  }
}

The server sends these initial events:

auction.connected
auction.snapshot
presence.snapshot

It also returns an acknowledgement:

{
  "type": "ack",
  "requestId": "subscribe-001",
  "eventType": "auction.subscribe",
  "data": {
    "auctionId": "auction-id",
    "cycleId": "cycle-id",
    "room": "auction:cycle-id"
  }
}

Subscribe As Company Or Admin

Company and superadmin users omit enrolledSubscriberId:

{
  "type": "auction.subscribe",
  "requestId": "subscribe-001",
  "data": {
    "cycleId": "cycle-id"
  }
}

Staff snapshots can include staff-only auction state such as the full subscriber room view when authorized by the application use case.

Resume With Replay

Clients that persisted the last processed auction sequence can request replay data:

{
  "type": "auction.subscribe",
  "requestId": "subscribe-002",
  "data": {
    "cycleId": "cycle-id",
    "enrolledSubscriberId": "enrolled-subscriber-id",
    "lastSequence": 42
  }
}

When replay is available, the server merges it into the auction.snapshot payload.

Place A Bid

Only subscriber connections can place live bids.

{
  "type": "auction.bid.place",
  "requestId": "bid-001",
  "data": {
    "cycleId": "cycle-id",
    "enrolledSubscriberId": "enrolled-subscriber-id",
    "amount": "25000",
    "idempotencyKey": "bid-001"
  }
}

Use decimal strings for money values to avoid JavaScript precision surprises. The idempotencyKey should be stable for retries of the same bid action.

Successful acknowledgement:

{
  "type": "ack",
  "requestId": "bid-001",
  "eventType": "auction.bid.place",
  "data": {
    "bidId": "bid-id",
    "stateVersion": 7
  }
}

The client may also receive auction realtime events before or after the acknowledgement, for example:

{
  "type": "bid.created",
  "requestId": "bid-001",
  "data": {
    "auctionId": "auction-id",
    "cycleId": "cycle-id",
    "stateVersion": 7,
    "occurredAt": "2026-08-03T00:00:00.000Z",
    "payload": {
      "bidId": "bid-id",
      "leadingBidAmount": 25000
    }
  }
}

Unsubscribe

{
  "type": "auction.unsubscribe",
  "requestId": "unsubscribe-001",
  "data": {
    "cycleId": "cycle-id"
  }
}

Successful acknowledgement:

{
  "type": "ack",
  "requestId": "unsubscribe-001",
  "eventType": "auction.unsubscribe",
  "data": {
    "cycleId": "cycle-id",
    "unsubscribed": true
  }
}

Application Ping

The server also has protocol-level heartbeat pings. This event is for client application-level health checks.

{
  "type": "system.ping",
  "requestId": "ping-001",
  "data": {}
}

Acknowledgement data:

{
  "type": "ack",
  "requestId": "ping-001",
  "eventType": "system.ping",
  "data": {
    "type": "system.pong",
    "occurredAt": "2026-08-03T00:00:00.000Z"
  }
}

Generic Rooms

The frontend can join controlled generic rooms when needed:

{
  "type": "room.join",
  "requestId": "room-join-001",
  "data": {
    "room": "user:user-id"
  }
}

Allowed room names:

notifications
admins
user:<current-user-id>
company:<current-company-id>
auction:<cycleId>

admins is only available to SUPERADMIN. company:<id> must match the authenticated company context.

Leave a room:

{
  "type": "room.leave",
  "requestId": "room-leave-001",
  "data": {
    "room": "user:user-id"
  }
}

Reconnection Guidance

On socket close:

  • Open a new /ws connection with a fresh access token if needed
  • Send auction.subscribe again
  • Include lastSequence when the client has processed a sequence number
  • Rebuild UI from the latest snapshot, then apply pushed events

If the access token expires, refresh it through the normal HTTP auth flow before opening a new socket.

Rate Limits

Default limit:

120 messages per 60 seconds

auction.bid.place limit:

100 messages per 60 seconds

Limits are applied per authenticated user, per IP, and per event type. A rate-limited message receives:

{
  "type": "error",
  "requestId": "bid-001",
  "code": "RATE_LIMITED",
  "message": "Too many WebSocket messages."
}

Removed WebSocket Routes

Do not use legacy role-specific auction WebSocket URLs. They have been removed. All realtime auction traffic now flows through the single /ws endpoint.