Auction Room Frontend Integration Guide¶
Use one WebSocket endpoint for every auction realtime role:
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:
Browser clients usually cannot set custom WebSocket headers, so send the token through Sec-WebSocket-Protocol:
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:
{
"id": "request-id",
"type": "auction.subscribe",
"version": "1.0",
"source": "web-client",
"timestamp": "2026-08-05T10:00:00.000Z",
"data": {},
"meta": {
"traceId": "client-trace-id"
}
}
Rules:
typeis required and must match a registered eventidis optional but recommended for correlating acknowledgements and errorsdatais validated per eventversion,source,timestamp,metaare optional and informational- Use
data, not the removedpayloadfield
Every server frame adds three things the client must handle:
version— protocol version, always"1.0".sequence— a server-assigned monotonic counter per connection. Track the last value you processed; a gap means frames were dropped and you should recover via snapshot + replay.meta.traceId— server correlation id (echoes yourmeta.traceIdwhen sent).
Successful command acknowledgements look like:
{
"id": "request-id",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 5,
"data": {
"eventType": "auction.subscribe",
"auctionId": "auction-id",
"cycleId": "cycle-id",
"room": "auction:auction-id"
}
}
Errors look like:
{
"id": "request-id",
"type": "error",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 6,
"data": {
"code": "FORBIDDEN",
"message": "Safe client-facing message."
}
}
Known error codes:
Recommended Client Flow¶
- Open
/wswith a valid access token. - Send
auction.subscribefor the active cycle. - Wait for the initial server events.
- Render the latest
auction.state.snapshotandauction.audience.snapshot. - Send commands such as
auction.bid.place. - Apply pushed auction events such as
auction.bid.created. - Send
auction.unsubscribewhen leaving the screen. - Reconnect and resubscribe after network loss.
Subscribe As Subscriber¶
Subscribers include enrolledSubscriberId:
{
"id": "subscribe-001",
"type": "auction.subscribe",
"data": {
"auctionId": "auction-id",
"enrolledSubscriberId": "enrolled-subscriber-id"
}
}
The server sends these initial events:
It also returns an acknowledgement:
{
"id": "subscribe-001",
"type": "ack",
"data": {
"eventType": "auction.subscribe",
"auctionId": "auction-id",
"cycleId": "cycle-id",
"room": "auction:auction-id"
}
}
Subscribe As Company Or Admin¶
Company and superadmin users omit enrolledSubscriberId:
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:
{
"id": "subscribe-002",
"type": "auction.subscribe",
"data": {
"auctionId": "auction-id",
"enrolledSubscriberId": "enrolled-subscriber-id",
"lastSequence": 42
}
}
lastSequence is the auction-level cursor you last observed (data.serverSequence on auction.bid.created / auction.bid.updated events), not the connection envelope sequence. When replay is available, the server merges it into the auction.state.snapshot payload.
Place A Bid¶
Only subscriber connections can place live bids.
{
"id": "bid-001",
"type": "auction.bid.place",
"data": {
"auctionId": "auction-id",
"enrolledSubscriberId": "enrolled-subscriber-id",
"amountMinor": "2500000"
}
}
Use decimal strings for money values to avoid JavaScript precision surprises. amountMinor is expressed in minor currency units. The envelope id doubles as the idempotency key — reuse it for retries of the same bid action.
auction.bid.place is synchronous: the server validates and commits against the latest locked auction state, then returns a terminal ack (accepted or replayed). Rejections return a correlated error. Pushed auction events still drive the shared room feed.
Successful acknowledgement:
{
"id": "bid-001",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"data": {
"eventType": "auction.bid.place",
"status": "accepted",
"commandId": "bid-001",
"auctionId": "auction-id",
"cycleId": "cycle-id",
"bidId": "bid-id",
"amountMinor": "2500000",
"stateVersion": 12
}
}
The acknowledgement confirms that the bid is committed. Use the corresponding auction event to update the shared feed for every participant:
{
"id": "evt_01HQ5BY3Y7Z2V6Y0W5X0W5X0W5X",
"type": "auction.bid.created",
"version": "1.0",
"source": "auction-service",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 7,
"data": {
"auctionId": "auction-id",
"cycleId": "cycle-id",
"stateVersion": 7,
"serverSequence": 12,
"payload": {
"bidId": "bid-id",
"leadingBidAmount": 25000
}
}
}
A rejected bid (validation or authorization) returns command.rejected; a business-rule failure after acceptance (e.g. bid below current minimum) returns command.failed. Both carry correlationId: "bid-001" and an errorCode.
Unsubscribe¶
Successful acknowledgement:
{
"id": "unsubscribe-001",
"type": "ack",
"data": {
"eventType": "auction.unsubscribe",
"auctionId": "auction-id",
"unsubscribed": true
}
}
Application Ping¶
The server also has protocol-level heartbeat pings. This event is for client application-level health checks.
Acknowledgement data:
{
"id": "ping-001",
"type": "ack",
"data": {
"eventType": "system.ping",
"type": "system.pong",
"occurredAt": "2026-08-05T10:00:00.000Z"
}
}
Generic Rooms¶
The frontend can join controlled generic rooms when needed. See events/room.md for the full room.join / room.leave request and response contract.
Allowed room names:
admins is only available to SUPERADMIN. company:<id> must match the authenticated user's canonical Company.id resolved from their active CompanyMembership.
[!WARNING] Attempting to join auction channels (
auction:<auctionId>orauction:staff:<auctionId>) viaroom.joinis forbidden and returns403 Forbidden("Use auction.subscribe to join an authorized auction channel."). Clients must useauction.subscribeto connect to auctions. Staff connections are automatically dual-joined toauction:<id>andauction:staff:<id>upon subscribing.
Leave a room:
Reconnection Guidance¶
On socket close:
- Open a new
/wsconnection with a fresh access token if needed - Send
auction.subscribeagain - Include
lastSequencewhen the client has processed an auction-level sequence number (data.serverSequence) - 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:
auction.bid.place limit:
Limits are applied per authenticated user, per IP, and per event type. A rate-limited message receives:
{
"id": "bid-001",
"type": "error",
"data": {
"code": "RATE_LIMITED",
"message": "Too many WebSocket messages."
}
}
WebSocket endpoint¶
All realtime auction traffic flows through the single /ws endpoint.