Auction Events¶
Auction events are the core of the real-time API. They let clients subscribe to a live auction, place and moderate bids, declare winners, and receive a continuously updated stream of auction state.
Reference: events/presence.md · events/prebid.md (subscriber prebid contract) · events/system.md · errors.md · rate-limits.md · Starting an auction (the full start flow, incl. the
auction.status.updatecommand)
Event list¶
Client → Server (commands)¶
| Event | Permission | Status |
|---|---|---|
auction.subscribe | auction:subscribe | Implemented |
auction.unsubscribe | auction:subscribe | Implemented |
auction.participation.join | auction:subscribe | Implemented |
auction.bid.place | auction:bid:place | Implemented |
auction.bid.cancel | auction:bid:place | Planned |
auction.status.update | auction:staff | Implemented |
auction.pause | auction:staff | Implemented |
auction.resume | auction:staff | Implemented |
auction.end | auction:staff | Implemented |
auction.bid.mark | auction:staff | Implemented |
auction.winner.declare | auction:staff | Implemented |
auction.winner.record_lot | auction:staff | Implemented |
auction.announcement.create | auction:staff | Implemented |
auction.presence.floor.update | auction:staff | Implemented |
Server → Client¶
| Event | Direction | Status |
|---|---|---|
auction.connected | Server → Client | Implemented |
auction.state.snapshot | Server → Client | Implemented |
auction.audience.snapshot | Server → Client | Implemented (see presence.md) |
auction.audience.changed | Server → Client | Implemented |
auction.join_window.opened · auction.join_window.closed | Server → Client | Implemented, durable, replayable |
auction.participation.joined | Server → Client | Implemented, staff only |
auction.state.changed | Server → Client | Implemented (all live transitions; data.payload.transition distinguishes them) |
auction.winner.changed | Server → Client | Implemented, durable, replayable, staff only |
auction.settings.updated | Server → Client | Implemented |
auction.bid.created · auction.bid.updated · auction.bid.deleted | Server → Client | Implemented |
auction.prebid.created · auction.prebid.deleted | Server → Client | Implemented |
auction.bidder.disqualified | Server → Client | Implemented |
auction.announcement.created · auction.announcement.updated · auction.announcement.deleted | Server → Client | Implemented |
The event names above are canonical. Earlier names such as auction.started, auction.paused, auction.resumed, auction.extended, auction.call_started, auction.closed, auction.winner_selected, auction.status_changed, auction.state_refreshed, bid.created, prebid.created, auction.announcement.created and auction.snapshot were collapsed/renamed during the canonical event migration. Clients should key off the canonical names above.
auction.subscribe¶
Description¶
Subscribes to an auction as a viewer and starts receiving the realtime stream. An eligible subscriber may watch without payment and without joining. Subscriber viewing is available in SCHEDULED, READY, LIVE, and PAUSED. On success the server:
- authorizes the caller against the specific (
auctionId) participation rule; - joins the
auction:<auctionId>room and the Redis fan-out channel; - subscribes to the domain realtime channel;
- for
COMPANY/SUPERADMIN, also joins the staff-onlyauction:staff:<auctionId>room and subscribes to the staff-scoped realtime channel (staff-only events, published withscope: "staff", are delivered only on this channel and never reach subscribers); - creates a per-connection viewer or participant audit session (for
SUBSCRIBER); - returns an acknowledgement with the auction identity;
- sends
auction.connected,auction.state.snapshot, andauction.audience.snapshot(in that order) after the acknowledgement.
Direction¶
Client → Server
Roles Allowed¶
SUBSCRIBER, COMPANY, SUPERADMIN (any role with an eligible participation or admin access to the cycle).
Permissions Required¶
auction:subscribe.
Request Schema¶
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | auction.subscribe |
data.auctionId | string | Yes | The auction id. |
data.enrolledSubscriberId | string | No | For SUBSCRIBER, the specific enrollment to join. |
data.lastSequence | number | No | Last serverSequence seen; server replay will merge missed events into the snapshot payload. |
Response Schema¶
The handler returns an acknowledgement first, and then pushes auction.connected, auction.state.snapshot and auction.audience.snapshot immediately after it.
| Field | Type | Description |
|---|---|---|
auctionId | string | Auction id (echoed from the request). |
cycleId | string | Cycle id for the auction. |
room | string | The room name joined, auction:<auctionId>. |
staffRoom | string | For staff only, the joined staff room auction:staff:<auctionId>. Absent for SUBSCRIBER. |
Success Response¶
{
"id": "req_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 1,
"data": {
"eventType": "auction.subscribe",
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"room": "auction:auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"staffRoom": "auction:staff:auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X"
}
}
Initial server events pushed after subscribe:
{
"type": "auction.connected",
"id": "req_01HQ5BWNJ5Y1P3V0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X0",
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"connectionId": "conn_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"heartbeatIntervalMs": 20000
}
}
}
{
"type": "auction.state.snapshot",
"id": "req_01HQ5BWNJ5Y1P3V0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"auction": {},
"currentUser": {},
"viewerCount": 2,
"joinedParticipantCount": 3,
"onlineParticipantCount": 0,
"recentBids": [],
"lastSequence": 12
}
}
}
{
"type": "auction.audience.snapshot",
"id": "req_01HQ5BWNJ5Y1P3V0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W4X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"viewerCount": 4,
"joinedParticipantCount": 3,
"onlineParticipantCount": 2,
"bidderCount": 1
}
}
}
Error Responses¶
| Code | Description |
|---|---|
FORBIDDEN | Subscriber is not eligible to view the auction, or viewing is unavailable in the current state. |
NOT_FOUND | Auction not found. |
BAD_REQUEST | auctionId missing/invalid, or the cycle invoice is not paid. |
CONFLICT | Auction state conflicts (e.g. already ended). |
Validation Rules¶
| Rule | Behaviour |
|---|---|
auctionId non-empty string | Required. |
lastSequence non-negative integer | Optional; if provided, replay is merged into the snapshot payload as replay. |
A subscribe without lastSequence into an auction that is past live start returns a snapshot whose payload.replay.events contains the persisted started transition (see Server events).
Fetching
auctionId: the auction row is now guaranteed beforeREADY/LIVE(ensureAuctionsForEligibleCycles), soauctionIdalways exists. Resolve it via REST before subscribing — e.g.GET /v2/.../auctions/by-cycle/:cycleIdor the cycle overview — then subscribe withauctionId.
Example Request¶
{
"type": "auction.subscribe",
"id": "req_01HQ5BWNJ5Y1P3V0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"lastSequence": 12
}
}
Broadcast Behaviour¶
Local (unicast to the requester) — but it triggers room membership and the server-pushed initial events.
Related Events¶
auction.unsubscribeauction.connected,auction.state.snapshot,auction.audience.snapshot- events/presence.md
Notes¶
- Implementation status: Implemented.
enrolledSubscriberIdis required by the subscriber authorization flow (or you retry without it and the server resolves), and omitted entirely by staff.- Use
lastSequenceafter reconnection to minimize the lost-event window.
Best Practices¶
- Subscribe once per auction; re-subscribe after reconnect using the last
serverSequence. - Apply the
auction.state.snapshotas the base, then deltas.
auction.participation.join¶
Explicitly admits a paid subscriber to bid. The command requires a message id; reuse the same id after a timeout. The auction row is locked and the server uses database time for the T−10 boundary. Concurrent joins produce one immutable participant row per auction and enrollment.
{
"id": "join-command-id",
"type": "auction.participation.join",
"data": {
"auctionId": "auction-id",
"enrolledSubscriberId": "enrollment-id",
"confirmFloorToOnline": false
}
}
The acknowledgement payload is:
{
"status": "accepted",
"commandId": "join-command-id",
"participantId": "participant-id",
"auctionId": "auction-id",
"cycleId": "cycle-id",
"joinedAt": "2026-09-06T09:50:00.000Z",
"joinWindowVersion": 2,
"source": "ONLINE"
}
status is accepted for the first commit and replayed when durable admission already exists. Joining opens 600 seconds before auctionStartAt. It closes at actual start for BEFORE_START, at startedAt + subscriberJoinGraceSeconds for GRACE_PERIOD, and at auction end/cancellation for ANYTIME.
Leaving or losing the socket never removes admission. A participant may reconnect after the window closes and may bid until the auction ends. Payment reversal also does not revoke admission; live disqualification still blocks bidding.
An existing FLOOR participant receives status: "confirmation_required" with currentSource: "FLOOR" and requestedSource: "ONLINE" unless the client sends confirmFloorToOnline: true. Until confirmed, the socket remains a viewer. Confirmation rechecks the online join window and all current eligibility rules. Staff floor-presence updates can switch ONLINE back to FLOOR; clearing floor presence never switches it online automatically.
Possible rejections include payment required, prior program winner, disqualification, inactive enrollment/cycle, window not open, window closed, or auction not viewable. Clients cannot send an access mode.
Join-window events¶
auction.join_window.opened and auction.join_window.closed are committed to the durable activity outbox before publication. Delivery is at least once. Deduplicate by event id, order by serverSequence, and reconnect with lastSequence to replay missed events.
Both events use { stateVersion, joinWindow, reason? }. joinWindow always contains status, version, joiningRule, opensAt, openedAt, closesAt, and closedAt. Opened events set status: "OPEN" and closedAt: null; closed events set status: "CLOSED" and add one of STARTED, GRACE_EXPIRED, AUCTION_ENDED, CANCELLED, or RESCHEDULED as reason. Rescheduling increments the window version. Ignore an older version after observing a newer one.
Sequences are global to the auction but replay is access-filtered. Visible events can therefore have numeric gaps; use replay.lastSequence as the high-water cursor instead of requiring contiguity.
auction.participation.joined is durable and staff-scoped. Subscriber clients learn their own admission from the command acknowledgement and snapshots. Its participant payload includes the durable joinedAt timestamp and nullable lastSeenAt, which is null at initial admission.
auction.participation.source_changed is also durable and staff-scoped. Its payload contains participant/subscriber/enrollment IDs, fromSource, toSource, and changedAt.
auction.unsubscribe¶
Description¶
Ends a subscription to an auction: leaves auction:<auctionId>, removes presence, and unsubscribes from the realtime channel.
Direction¶
Client → Server
Roles Allowed¶
All subscription roles.
Permissions Required¶
auction:subscribe.
Request Schema¶
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | auction.unsubscribe |
data.auctionId | string | Yes | Auction to leave. |
Response Schema¶
| Field | Type | Description |
|---|---|---|
auctionId | string | Auction id. |
unsubscribed | boolean | true if an active subscription was torn down. |
Success Response¶
{
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 1,
"data": {
"eventType": "auction.unsubscribe",
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"unsubscribed": true
}
}
Error Responses¶
| Code | Description |
|---|---|
BAD_REQUEST | auctionId missing/invalid. |
Validation Rules¶
auctionId required non-empty.
Example Request¶
{
"type": "auction.unsubscribe",
"id": "req_01HQ5BXWN1Y2RX0W5X0W5X0W5X",
"data": { "auctionId": "auc_01HQ5BWNJ5Y1P1RX0V0X0W5X0W5X" }
}
Example Response¶
{
"id": "req_01HQ5BXWN1Y2RX0W5X0W5X0W5X",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 1,
"data": {
"eventType": "auction.unsubscribe",
"auctionId": "auc_01HQ5BWNJ5Y1P1RX0W5X0W5X0W5X",
"unsubscribed": true
}
}
Broadcast Behaviour¶
None, except that other connections may receive auction.audience.changed if this was the subscriber's last chunk.
Related Events¶
auction.subscribeauction.audience.changed
Notes¶
- Implementation status: Implemented.
- Idempotent: unsubscribing when not subscribed returns
unsubscribed: false.
Best Practices¶
- Unsubscribe when the user leaves an auction view to free resources.
auction.bid.place¶
Description¶
Places a live bid on an active auction. For SUBSCRIBER users it is their own bid. For COMPANY/SUPERADMIN (staff) it can place a bid on behalf of an offline subscriber with enrolledSubscriberId (only when settings allow). amountMinor is the bid amount expressed in minor currency units (e.g. "150000" = 1500.00) as a decimal string.
Direction¶
Client → Server
Roles Allowed¶
SUBSCRIBER— own live bid.COMPANY,SUPERADMIN— offline staff bid (needsenrolledSubscriberId).
Permissions Required¶
auction:bid:place for the route. Staff offline placement additionally requires auction:staff-level authority at the application layer.
Request Schema¶
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | auction.bid.place |
id | string | Yes | Client-generated request id (1–128 chars); becomes the idempotency command id. |
data.auctionId | string | Yes | Auction id. |
data.amountMinor | string | Yes | Amount in minor units as a decimal string, regex /^[1-9]\d*$/. |
data.enrolledSubscriberId | string | Conditional | Required for staff offline bids; optional for subscriber. |
Response Schema¶
The route returns one terminal response after transactional validation.
| Field | Type | Description |
|---|---|---|
type | string | Always "ack" for a committed bid. |
id | string | Echoes the request id. |
data.eventType | string | auction.bid.place. |
data.status | string | accepted or replayed. |
data.commandId | string | Scoped idempotency key. |
data.bidId | string | Persisted bid id. |
data.amountMinor | string | Committed amount in minor units. |
data.stateVersion | number | Auction state version after the committed operation. |
Success Response¶
{
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"data": {
"eventType": "auction.bid.place",
"status": "accepted",
"commandId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"bidId": "bid_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"amountMinor": "2500000",
"stateVersion": 12
}
}
Error Responses¶
Business, validation, and authorization rejections use the existing correlated WebSocket error envelope with a safe code and public message:
| Code | Description |
|---|---|
AUCTION_NOT_LIVE | Auction is not live (not started, window closed, or cycle inactive). |
AUCTION_ENDED | Auction already ended. |
INVALID_AMOUNT | Amount failed validation (non-positive or malformed). |
BID_NOT_IMPROVING | Bid below the minimum, above the maximum, or not an improvement over the current lead. |
BID_INCREMENT_TOO_SMALL / BID_DECREMENT_TOO_SMALL | Bid improves but misses the minimum step. |
ONLINE_PARTICIPATION_REQUIRED | Online bid while durably floor-backed; confirm the floor→online switch and retry with the same command id. data.details carries currentSource/requiredSource. |
FLOOR_PARTICIPATION_REQUIRED | Staff floor bid while durably online. |
DUPLICATE_COMMAND | Same command id reused with a different amount. |
SUBSCRIBER_NOT_ELIGIBLE | Payment, eligibility, or winning constraints are not met. |
The payload schema is strict, so removed fields are rejected rather than ignored.
Validation Rules¶
| Rule | Behaviour |
|---|---|
amountMinor regex /^[1-9]\d*$/ | Required. |
id 1–128 chars | Required; doubles as the idempotency key. |
auctionId non-empty | Required. |
staff requires enrolledSubscriberId | Missing → BAD_REQUEST, then FORBIDDEN if the target is online. |
Example Request¶
{
"type": "auction.bid.place",
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"amountMinor": "2500000"
}
}
Example Response¶
- Terminal
ackafter commit, or correlatederroron rejection. - Domain events update the shared feed independently of the requester response.
Broadcast Behaviour¶
A successful bid is fanned out to every subscriber of auction:<auctionId> as auction.bid.created (leading bid) or auction.bid.updated (outbid) — see auction.bid.created server event below. The sender sees the same events.
Related Events¶
auction.bid.created,auction.bid.updatedauction.state.changed(start, pause, resume, extended transitions)
Notes¶
- Implementation status: Implemented (synchronous transactional command).
- Rate limit: 100 messages / window (overridden per-route).
- Idempotency prevents double-bids on replays: reusing the same
idfor the same bid returns the existing result asreplayed; changing the amount under the sameidis rejected (DUPLICATE_COMMAND).
Best Practices¶
- Reuse the request
idon reconnect for the same bid intent. - Treat the terminal ack as authoritative for the request and domain events as authoritative for the shared feed.
auction.bid.cancel¶
Description¶
Planned. Cancels/revokes a bid the client previously placed. Because a live floor bid cannot simply be undone, cancel behaviour maps to the canonical auction.bid.deleted workflow (staff auction.bid.mark with action: DELETE_BID) in the current build; a client-driven cancel is planned.
Direction¶
Client → Server (planned)
Roles Allowed¶
SUBSCRIBER (cancel own bid); staff may delete any bid via auction.bid.mark.
Permissions Required¶
auction:bid:place.
Request Schema¶
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | auction.bid.cancel |
data.auctionId | string | Yes | Auction id. |
data.bidId | string | Yes | Bid to cancel. |
Response Schema¶
Planned. Will follow async CQRS: an immediate command.acknowledged with no business data, followed by an auction.bid.deleted domain event carrying the cancelled bid id on success.
Success Response¶
{
"id": "req_01HQ5BXWYP1Y3RX0W5X0W5X0W5X",
"type": "ack",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"sequence": 1,
"data": {
"eventType": "auction.bid.cancel",
"bidId": "bid_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"cancelled": true
}
}
Error Responses¶
FORBIDDEN, NOT_FOUND, BAD_REQUEST, CONFLICT.
Validation Rules¶
| Rule | Behaviour |
|---|---|
auctionId, bidId non-empty | Required. |
| Bid ownership | Must belong to the caller. |
Example Request¶
{
"type": "auction.bid.cancel",
"id": "req_01HQ5BXWYP1Y5RX0W1X0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P0X0W5X0W5X0W5X",
"bidId": "bid_01HQ5BY3K1Z2V1Y0W5X0W5X0W5X"
}
}
Broadcast Behaviour¶
Broadcast to the room as auction.bid.deleted.
Related Events¶
auction.bid.deletedauction.bid.mark(staff) — the current safe way to delete a bid
Notes¶
- Implementation status: Planned. Use
auction.bid.markDELETE_BID(staff) today. - Policy constraint: deleting a bid that is currently leading may not be allowed; clients should be ready for
CONFLICT.
Best Practices¶
- Only cancel the client's own in-flight bids; for moderation use
auction.bid.mark.
Staff command events¶
All staff events require the auction:staff permission and an auctionId. They run the corresponding auction command and return an immediate command.acknowledged. No business data (e.g. stateVersion) is returned in the acknowledgement; the result arrives via domain events (auction.state.changed with the relevant transition, auction.announcement.created, etc.). Subscribers and insufficiently-permissioned users receive command.rejected/error (FORBIDDEN).
| Event | Purpose | Example data |
|---|---|---|
auction.status.update | Start live auction | { "auctionId": "...", "status": "START" } |
auction.pause | Pause auction | { "auctionId": "...", "reason": "network issue" } |
auction.resume | Resume auction | { "auctionId": "...", "reason": "resolved" } |
auction.end | End auction | { "auctionId": "...", "reason": "auction complete" } |
auction.bid.mark | Moderate a bid | action variants below |
auction.winner.declare | Declare winner | mode variants below |
auction.winner.record_lot | Record lot + runners-up | list of ids |
auction.announcement.create | Create an announcement | { "auctionId": "...", "message": "...", "tone": "INFO" } |
auction.presence.floor.update | Toggle floor presence | { "auctionId": "...", "enrolledSubscriberId": "...", "present": true } |
auction.bid.mark¶
One of two actions:
| action | payload |
|---|---|
DELETE_BID | { action, bidId, reason, reasonCode? } — reasonCode optional |
DISQUALIFY_BIDDER | { action, enrolledSubscriberId, reason, reasonCode } |
reasonCode ∈ KYC_ISSUE | PAYMENT_ISSUE | ELIGIBILITY_ISSUE | RULE_VIOLATION | OTHER (required except for DELETE_BID).
Example:
{
"type": "auction.bid.mark",
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"action": "DELETE_BID",
"bidId": "bid_01HQ5BY3K1Y1V1Y0W5X0W5X0W5X",
"reason": "errored entry"
}
}
auction.winner.declare¶
| mode | payload |
|---|---|
CANDIDATE | { mode, bidId? , prebidId?, reason? } — derive from candidate |
MANUAL | { mode, enrolledSubscriberId, amount, reason } — manual override |
auction.winner.record_lot¶
{
"type": "auction.winner.record_lot",
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"winnerEnrolledSubscriberId": "enr_01HQ5BY3K7Z1V1Y0W5X0W5X0W5X",
"candidateEnrolledSubscriberIds": [
"enr_01HQ5BY3K7Z1V2Y0W5X0W5X0W5X",
"enr_01HQ5BY3K7Z1V3Y0W5X0W5X0W5X"
]
}
}
auction.announcement.create¶
Description¶
Creates an auction-scoped announcement visible to all room members. The server stores the announcement and publishes auction.announcement.created to the auction:<auctionId> room. This is a staff-only command.
Direction¶
Client → Server
Roles Allowed¶
COMPANY, SUPERADMIN (staff).
Permissions Required¶
auction:staff.
Request Schema¶
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | auction.announcement.create |
data.auctionId | string | Yes | The auction id. |
data.message | string | Yes | Announcement text, trimmed, 1–1000 chars. |
data.tone | string | No | INFO (default) or WARNING. |
Response Schema¶
This is an async command — the immediate response is a command.acknowledged with no business data. The created announcement is delivered to the room as an auction.announcement.created event.
| Field | Type | Description |
|---|---|---|
type | string | Always "command.acknowledged". |
correlationId | string | Echoes the request's id for correlation. |
data.command | string | The acknowledged command type (auction.announcement.create). |
data.status | string | Always "accepted". |
Success Response¶
{
"id": "ack_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"type": "command.acknowledged",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"correlationId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"causationId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"data": {
"command": "auction.announcement.create",
"status": "accepted"
}
}
Error Responses¶
| Code | Description |
|---|---|
FORBIDDEN | Non-staff caller. |
NOT_FOUND | Cycle/auction not found. |
BAD_REQUEST | message empty/too long, tone invalid. |
Validation Rules¶
| Rule | Behaviour |
|---|---|
auctionId non-empty string | Required. |
message trimmed, 1–1000 chars | Required. |
tone ∈ INFO, WARNING | Optional; default INFO. |
Example Request¶
{
"type": "auction.announcement.create",
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"message": "Heads up: the reserve is being lowered.",
"tone": "WARNING"
}
}
Broadcast Behaviour¶
A successful command publishes auction.announcement.created to the auction:<auctionId> room.
Related Events¶
auction.announcement.created,auction.announcement.updated,auction.announcement.deleted
Notes¶
- Implementation status: Implemented.
- Announcements are persisted and also surfaced in the auction snapshot payload (
payload.announcements).
auction.presence.floor.update¶
Description¶
Allows a staff member to mark a subscriber as physically present on the auction floor (or remove the floor-presence flag). This sets the participant's source to "FLOOR" (AuctionParticipationSource) in the staff audience view. Marking a subscriber present also creates immutable FLOOR participation after the server verifies eligibility, payment, and the current join window. The resulting auction.audience.changed event is pushed to the room. Floor presence is independent of WebSocket connections, so a floor participant has connectionCount: 0.
Direction¶
Client → Server
Roles Allowed¶
COMPANY, SUPERADMIN (staff only; subscribers cannot set floor presence).
Permissions Required¶
auction:staff.
Request Schema¶
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | auction.presence.floor.update |
data.auctionId | string | Yes | The auction id. |
data.enrolledSubscriberId | string | Yes | The subscriber whose floor presence is being set. |
data.present | boolean | Yes | true to mark as floor-present, false to remove. |
Response Schema¶
This is an async command — the immediate response is a command.acknowledged with no business data. The resulting presence transition is delivered to the room as auction.audience.changed.
| Field | Type | Description |
|---|---|---|
type | string | Always "command.acknowledged". |
correlationId | string | Echoes the request's id for correlation. |
data.command | string | The acknowledged command type (auction.presence.floor.update). |
data.status | string | Always "accepted". |
Success Response¶
{
"id": "ack_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"type": "command.acknowledged",
"version": "1.0",
"source": "dichit-backend",
"timestamp": "2026-08-05T10:00:00.000Z",
"correlationId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"causationId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
"data": {
"command": "auction.presence.floor.update",
"status": "accepted"
}
}
Error Responses¶
| Code | Description |
|---|---|
FORBIDDEN | Non-staff caller; subscriber is not eligible for this auction. |
NOT_FOUND | Cycle/auction not found; subscriber not eligible. |
BAD_REQUEST | Required data is malformed or the paid invoice cannot be resolved. |
Validation Rules¶
| Rule | Behaviour |
|---|---|
auctionId non-empty string | Required. |
enrolledSubscriberId non-empty | Required. |
present boolean | Required. |
present: true | Requires eligibility, payment, and an open join window. |
present: false | Clears floor presence without revoking participation. |
Example Request¶
{
"type": "auction.presence.floor.update",
"id": "req_01HQ5BXWYP1Y5RX0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"enrolledSubscriberId": "enr_01HQ5BWYP1Y5RX0W5X0W5X0W5X",
"present": true
}
}
Broadcast Behaviour¶
The server publishes auction.audience.changed to the auction room:
- Adding floor presence durably admits the paid subscriber, if needed, then adds the floor presence overlay. Both public and staff-scoped copies include a participant with
source: "FLOOR"andconnectionCount: 0. - Removing floor presence removes only the floor overlay. Durable admission remains.
Related Events¶
auction.audience.changed— public and staff-scoped copies both include aggregate counts plus the affected participant projection.auction.audience.snapshot— aggregate counts for subscribers; staff may also receive identities.
Notes¶
- Implementation status: Implemented.
- Floor participants are distinct from online (
"ONLINE") subscribers. A single subscriber can be both online (viaauction.subscribe) and floor-present; in that case the online presence dominates in the snapshot andsourcereflects the active state. - Use this to represent paddle/bidder-room presence in a live sales room where the bidder is not connected via a browser.
Staff event Error Responses¶
| Code | Description |
|---|---|
FORBIDDEN | Non-staff (e.g. SUBSCRIBER). |
BAD_REQUEST | Missing/ill-typed fields. |
NOT_FOUND/CONFLICT | Business rejections. |
Related Events¶
auction.state.changed, auction.bid.created, auction.bid.deleted, auction.bidder.disqualified, auction.announcement.created, auction.audience.changed (floor).
Server events¶
Below are the canonical server-pushed auctions. All use the standard envelope with data.auctionId, data.cycleId, optional data.stateVersion, data.occurredAt, and data.payload.
auction.connected¶
Description — Sent once by auction.subscribe. Confirms live subscription and gives the connection id + heartbeat interval.
Direction Server → Client · Roles all subscribed · Permissions – · Request N/A.
| Field | Type | Description |
|---|---|---|
payload.connectionId | string | Connection id. |
payload.heartbeatIntervalMs | number | Heartbeat interval. |
Example:
{
"type": "auction.connected",
"id": "req_01HQ5BWNJ5Y1P3V0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"connectionId": "conn_01HQ5BWNJ5Y4X5RX0W5X0W5X0W5X",
"heartbeatIntervalMs": 20000
}
}
}
Related: auction.state.snapshot, auction.audience.snapshot.
auction.state.snapshot¶
Description — the current auction state delivered right after subscribe (or merged with a replay parcel when lastSequence was provided). When the auction is past live start, the snapshot also carries the authoritative live-start marker and, for late joiners, a storage-backed started transition in replay.events (see below).
Direction Server → Client · Roles all subscribed · Permissions -
| Field | Description |
|---|---|
payload.auction | Auction summary (role-scoped). |
payload.auction.startedAt | ISO-8601 live-start marker or null; set once the started transition ran and never cleared. |
payload.currentUser | Current bidder/lead, role-scoped. |
payload.viewerCount | Distinct online viewers. |
payload.joinedParticipantCount | Durable admitted participants. |
payload.onlineParticipantCount | Distinct admitted participants currently online or floor-present. |
payload.bidderCount | Durable participants with an accepted bid. |
payload.recentBids | Recent bid activity items. |
payload.lastSequence | Last event sequence for replay resume. |
Example (subscriber view):
{
"type": "auction.state.snapshot",
"id": "req_01HQ5BWNJ5Y1P3V0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"auction": {
"id": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"status": "LIVE",
"startedAt": "2026-08-03T10:00:00.000Z"
},
"currentUser": {
"bidderCode": "*********3210",
"bidAmount": "24500.00"
},
"viewerCount": 0,
"joinedParticipantCount": 0,
"onlineParticipantCount": 0,
"bidderCount": 0,
"recentBids": [],
"lastSequence": 12
}
}
}
Late-join replay — a fresh subscribe (no lastSequence) into an auction that is past live start includes a replay parcel whose events contain the persisted started transition. The started transition is written to the auction's activity feed with a real serverSequence when the auction goes live (manual start or scheduled auto-start), so the replay event is storage-backed — never synthesized by the server. The same event is prepended to the gap-fill replay.events on reconnect when the auction is past live start and the gap does not already carry it; clients should treat it as idempotent (re-applying the started transition is harmless).
{
"type": "auction.state.snapshot",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:00:00.100Z",
"payload": {
"auction": {
"id": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"status": "LIVE",
"startedAt": "2026-08-03T10:00:00.000Z"
},
"currentUser": {},
"viewerCount": 0,
"joinedParticipantCount": 0,
"onlineParticipantCount": 0,
"bidderCount": 0,
"recentBids": [],
"lastSequence": 12,
"replay": {
"events": [
{
"type": "auction.state.changed",
"id": "evt_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"serverSequence": 1,
"payload": {
"transition": "started",
"state": {
"status": "LIVE",
"stateVersion": 2,
"currentPhase": "LIVE_BIDDING",
"prebidPhase": "CLOSED",
"auctionStartAt": "2026-08-03T10:00:00.000Z",
"prebidStartAt": null,
"prebidEndAt": null,
"auctionEndAt": "2026-08-03T10:30:00.000Z",
"startedAt": "2026-08-03T10:00:00.000Z",
"pausedAt": null,
"closingPhase": null,
"closingPhaseEndsAt": null,
"prebidAmountsRevealed": false,
"prebidAmountsRevealedAt": null,
"approvalRequired": false,
"cycleStatus": "ACTIVE",
"joinWindow": {
"status": "CLOSED",
"version": 1,
"joiningRule": "BEFORE_START",
"opensAt": "2026-08-03T09:50:00.000Z",
"openedAt": "2026-08-03T09:50:00.000Z",
"closesAt": "2026-08-03T10:00:00.000Z",
"closedAt": "2026-08-03T10:00:00.000Z"
}
}
}
}
],
"lastSequence": 12
}
}
}
}
Related: auction.connected, auction.bid.created, auction.state.changed.
The canonical live-state event is
auction.state.changed. Earlier granular events (auction.started,auction.paused,auction.resumed,auction.extended,auction.call_started,auction.closed,auction.winner_selected,auction.status_changed,auction.state_refreshed) are collapsed into it;data.payload.transitionidentifies the specific transition.
auction.state.changed¶
Description¶
Published whenever persisted auction lifecycle state changes. One durable event type carries scheduling, prebid, approval, live, close, cancellation, and winner transitions; data.payload.transition identifies the cause and data.payload.state is the complete post-transition lifecycle state.
transition | Published when |
|---|---|
schedule_changed | Auction timing/settings are scheduled or rescheduled. |
prebid_opened / prebid_closed | Manual or automatic prebid-window transition. |
prebids_revealed | Staff reveals sealed prebid amounts. |
approved | Approval-required auction moves to READY. |
started | Auction goes live manually or automatically. |
paused / resumed | Staff pauses or resumes live bidding. |
extended | A committed bid extends the duration-mode deadline. |
call_started | A call-mode closing phase begins. |
closed | Auction enters ENDED. |
cancelled | Auction is cancelled. |
winner_selected / winner_disqualified | Winner state changes and division counts are recalculated. |
Direction¶
Server → Client
Roles Allowed¶
All subscribers of the auction.
Permissions Required¶
None at the event level (delivered to room members).
Response Schema¶
| Field | Type | Description |
|---|---|---|
id | string | Durable event id; deduplicate replay/live overlap with this value. |
data.stateVersion | number | Same value as data.payload.state.stateVersion. |
data.serverSequence | number | Durable per-auction high-water sequence. |
data.payload.transition | string | Cause of the state change. |
data.payload.state | object | Complete post-transition lifecycle state. |
data.payload.winner | object | Optional public winner projection. |
data.payload.declaredWinnerCount | number | Optional, winner transitions only. |
data.payload.remainingWinnerSlots | number | Optional, winner transitions only. |
data.payload.replacementRequired | bool | Optional, disqualification only. |
payload.state always contains status, stateVersion, currentPhase, prebidPhase, auctionStartAt, prebidStartAt, prebidEndAt, auctionEndAt, startedAt, pausedAt, closingPhase, closingPhaseEndsAt, prebidAmountsRevealed, prebidAmountsRevealedAt, approvalRequired, cycleStatus, and the complete joinWindow object. Live and replay deliveries use this same payload. Replace the local lifecycle slice from payload.state; do not expect root-level startedAt, pausedAt, resumedAt, or deadline fields.
Example Response¶
{
"type": "auction.state.changed",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"stateVersion": 2,
"serverSequence": 14,
"payload": {
"transition": "started",
"state": {
"status": "LIVE",
"stateVersion": 2,
"currentPhase": "LIVE_BIDDING",
"prebidPhase": "CLOSED",
"auctionStartAt": "2026-08-03T10:00:00.000Z",
"prebidStartAt": null,
"prebidEndAt": null,
"auctionEndAt": "2026-08-03T10:30:00.000Z",
"startedAt": "2026-08-03T10:00:00.000Z",
"pausedAt": null,
"closingPhase": null,
"closingPhaseEndsAt": null,
"prebidAmountsRevealed": false,
"prebidAmountsRevealedAt": null,
"approvalRequired": false,
"cycleStatus": "ACTIVE",
"joinWindow": {
"status": "CLOSED",
"version": 1,
"joiningRule": "BEFORE_START",
"opensAt": "2026-08-03T09:50:00.000Z",
"openedAt": "2026-08-03T09:50:00.000Z",
"closesAt": "2026-08-03T10:00:00.000Z",
"closedAt": "2026-08-03T10:00:00.000Z"
}
}
}
}
}
Broadcast Behaviour¶
Broadcast to auction:<auctionId> room across servers.
Related Events¶
auction.connected, auction.state.snapshot, auction.bid.created.
Notes¶
- Implementation status: Implemented across schedule, prebid, approval, start/pause/resume/end, automatic lifecycle, bid extension, cancellation, winner declaration/lot, and winner disqualification paths.
- Consume the full state from
auction.state.snapshoton connect, then replace lifecycle fields from each newerpayload.state.
auction.bid.created¶
The auction.bid.created server event announces a new leading bid. The full payload is a bid activity object.
{
"type": "auction.bid.created",
"id": "bid_req_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"stateVersion": 7,
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"id": "bid_01HQ5BY3J7Z2V6Y0W5X0W5X0W5X",
"auctionId": "auc_01HQ5BWRJ3J5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"subscriberProgramId": "enr_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"subscriberId": "sub_01HQ5BWYP1Y5RX0W5X0W5X0W5X",
"bidderName": "Annie Mohan",
"bidderAvatarUrl": null,
"bidderCode": "*********3210",
"bidAmount": "25000.00",
"bidRank": 1,
"isWinningBid": true,
"status": "ACCEPTED",
"createdAt": "2026-08-03T10:00:00.000Z",
"serverSequence": 12
}
}
}
| Payload field | Type | Description |
|---|---|---|
id | string | Bid id. |
subscriberProgramId | string | Enrollment id of the bidder. |
subscriberId | string | User id of the bidder. |
bidderName / bidderAvatarUrl | string | null | Display. |
bidderCode | string | null | Masked mobile (e.g. *********3210). |
bidAmount | string | Decimal string amount. |
bidRank / isWinningBid | number / bool | Rank & winning flag. |
status | string | ACCEPTED or OUTBID. |
serverSequence | number | Monotonic ordering. |
- Target-scoped: for subscriber sockets, a bid event carrying
targetUserIdonly arrives when it matches the socket's user. (See [protocol.md]).
Handler behaviour Server → Client · Roles all subscribed · Permissions — · Broadcast to auction:<auctionId>. · Error none. Related auction.bid.place, auction.state.changed, auction.bid.updated.
Other server events (compact reference)¶
| Event | Meaning | Payload snippets |
|---|---|---|
auction.state.changed | Any lifecycle transition | transition + complete state (see auction.state.changed) |
auction.settings.updated | Auction settings changed | updated settings fields |
auction.bid.created | New leading bid | (see auction.bid.created) |
auction.bid.updated | Existing bid outbid | same item shape, status: OUTBID |
auction.bid.deleted | Bid deleted (staff) | deleted bid |
auction.prebid.created | New prebid placed | staff-only durable prebid projection |
auction.winner.changed | Winner console delta | staff-only durable selected/disqualified winner projection |
auction.prebid.deleted | Prebid deleted | id |
auction.bidder.disqualified | Bidder disqualified | participant + reason |
auction.announcement.created | New announcement posted | announcement object |
auction.announcement.updated | Announcement edited | announcement object |
auction.announcement.deleted | Announcement removed | { id, auctionId, cycleId } |
- Direction: Server → Client
- Roles: all subscribed
- Permissions: none at event level
- Broadcast: to
auction:<auctionId>room, across servers; subscriber filtering bytargetUserIdwhere present. - Error Responses: none (server-pushed).
- Validation: N/A.
Payload excerpt for auction.state.changed (auto-extension): The real state object also contains every lifecycle field listed in the canonical schema above.
{
"type": "auction.state.changed",
"data": {
"auctionId": "auc_11HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_11HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"stateVersion": 8,
"serverSequence": 31,
"payload": {
"transition": "extended",
"state": {
"status": "LIVE",
"stateVersion": 8,
"currentPhase": "LIVE_BIDDING",
"auctionEndAt": "2026-08-03T10:12:00.000Z",
"regularAuctionEndAt": "2026-08-03T10:11:00.000Z",
"extensionStartedAt": null
}
}
}
}
extended is emitted immediately when a qualifying bid moves auctionEndAt. At regularAuctionEndAt, the server emits one additional state event with transition: "extension_started" and a non-null extensionStartedAt. Reconnecting clients restore both fields from the snapshot instead of inferring the extension phase locally.
Example auction.bidder.disqualified:
{
"type": "auction.bidder.disqualified",
"data": {
"auctionId": "auc_11HQ0W5X1Y3P5RX0W0X0W1X0X5X",
"cycleId": "cyc_11HQ0W5X1Y3P5RX0W0X0W1X0X5X",
"stateVersion": 5,
"occurredAt": "2026-08-03T10:05:00.000Z",
"payload": { "subscriberId": "enr_...", "reason": "RULE_VIOLATION" }
}
}
auction.announcement.created / auction.announcement.updated / auction.announcement.deleted¶
These server-pushed events announce changes to auction-scoped announcements (persistent text messages posted by staff). They are published to the auction:<auctionId> room whenever the corresponding command succeeds.
auction.announcement.created¶
Description — A new announcement was created via the auction.announcement.create client command.
| Field | Type | Description |
|---|---|---|
payload.id | string | Announcement id. |
payload.auctionId | string | Auction id. |
payload.cycleId | string | Cycle id. |
payload.actorId | string | Staff user who created it. |
payload.message | string | Announcement text. |
payload.tone | string | INFO or WARNING. |
payload.createdAt | string | ISO-8601 creation time. |
{
"type": "auction.announcement.created",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:00:00.000Z",
"payload": {
"id": "ann_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"actorId": "com_01HQ5BWYP1Y5RX0W5X0W5X0W5X",
"message": "Heads up: the reserve is being lowered.",
"tone": "WARNING",
"createdAt": "2026-08-03T10:00:00.000Z"
}
}
}
auction.announcement.updated¶
Description — An existing announcement was edited via the REST updateAnnouncement command. Same payload shape as auction.announcement.created.
auction.announcement.deleted¶
Description — An announcement was deleted via the REST deleteAnnouncement command.
| Field | Type | Description |
|---|---|---|
payload.id | string | Deleted announcement id. |
payload.auctionId | string | Auction id. |
payload.cycleId | string | Cycle id. |
{
"type": "auction.announcement.deleted",
"data": {
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"occurredAt": "2026-08-03T10:05:00.000Z",
"payload": {
"id": "ann_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"auctionId": "auc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWNJ5Y1P5RX0W5X0W5X0W5X"
}
}
}
auction.prebid.created¶
Description — A staff-only, durable event emitted when a subscriber places a prebid. It is published to auction:staff:<auctionId> and included only in staff replay. Subscriber sockets do not receive it. The payload has a nullable amount so sealed values remain redacted.
The subscriber write/snapshot contract is in events/prebid.md.
| Field | Type | Description |
|---|---|---|
payload.id | string | Prebid id. |
payload.enrolledSubscriberId | string | Enrollment id of the bidder. |
payload.subscriberId | string | User id of the bidder. |
payload.bidderName | string | Staff display name. |
payload.amount | number | null | null while the prebid amount remains sealed. |
payload.createdAt | string | ISO-8601 creation time. |
payload.status | string | Current AuctionPrebidStatus value. |
payload.documentSubmissionId | string | null | Associated document submission, when required. |
{
"type": "auction.prebid.created",
"id": "bid_req_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"data": {
"auctionId": "auc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"serverSequence": 22,
"payload": {
"id": "pb_01HQ5BY3J7Z2V6Y0W5X0W5X0W5X",
"enrolledSubscriberId": "enr_01HQ5BY3K7Z2V6Y0W5X0W5X0W5X",
"subscriberId": "sub_01HQ5BWYP1Y5RX0W5X0W5X0W5X",
"bidderName": "Annie Mohan",
"amount": null,
"createdAt": "2026-08-03T09:30:00.000Z",
"status": "ACTIVE",
"documentSubmissionId": null
}
}
}
After staff reveals completed-auction prebids, clients receive auction.state.changed with payload.transition: "prebids_revealed" and should refresh the auction snapshot. Snapshot and REST payloads expose prebidAmountsRevealed: true and include prebid amounts from that point onward.
auction.winner.changed¶
Description — A staff-only, durable console event emitted alongside the public auction.state.changed winner transition. action is SELECTED or DISQUALIFIED.
The winner object includes public winner fields (id, subscriber and enrollment ids, display identity, nullable amount, division, status, type, selection source, bid/prebid ids, and selectedAt) plus staff-only email, mobile, selecting/replacing/disqualifying actor ids and timestamps, reason code, and note. A disqualification may also include suggestedReplacementCandidate, or explicit null when no suggestion exists.
{
"type": "auction.winner.changed",
"data": {
"auctionId": "auc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"serverSequence": 38,
"payload": {
"action": "DISQUALIFIED",
"winner": {
"id": "winner-id",
"subscriberId": "subscriber-id",
"enrolledSubscriberId": "enrollment-id",
"subscriberName": "Annie Mohan",
"subscriberAvatar": null,
"subscriberEmail": "annie@example.com",
"subscriberMobileNumber": "+919999999999",
"amount": 1500,
"division": 1,
"status": "DISQUALIFIED",
"type": "AUCTION",
"selectionSource": "LIVE_BID_REVIEW",
"auctionBidId": "bid-id",
"auctionPrebidId": null,
"selectedById": "staff-id",
"selectedAt": "2026-08-03T10:35:00.000Z",
"replacedById": null,
"replacedAt": null,
"disqualifiedById": "staff-id",
"disqualifiedAt": "2026-08-03T10:40:00.000Z",
"disqualificationReasonCode": "KYC_ISSUE",
"disqualificationNote": "Document mismatch"
},
"suggestedReplacementCandidate": null
}
}
}
Deduplicate using the event id; apply it in serverSequence order. The public winner UI should use the companion auction.state.changed event, whose winner projection omits staff-only contact and audit fields.
auction.settings.updated¶
Description — Auction configuration (e.g. reserve price, extension policy, bid increments) was changed by staff, typically via the program/cycle management API. Pushed to the auction:<auctionId> room so live clients can reconcile.
| Field | Type | Description |
|---|---|---|
payload | object | The changed settings fields (shape is implementation-defined). |
{
"type": "auction.settings.updated",
"data": {
"auctionId": "auc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"cycleId": "cyc_01HQ5BWRJ5Y1P5RX0W5X0W5X0W5X",
"stateVersion": 3,
"occurredAt": "2026-08-03T09:45:00.000Z",
"payload": {
"reservePrice": "20000.00",
"allowStaffOfflineBids": true
}
}
}
Notes
- These events are server-pushed; clients should treat them as informational and reconcile from
auction.state.snapshotwhen available. auction.prebid.createdis only emitted before the auction goes live; during the live phase useauction.bid.createdinstead.auction.announcement.updatedandauction.announcement.deletedare currently published from the REST command path, not from a socket command.
Related documents¶
- events/prebid.md — subscriber prebid WebSocket contract (events, snapshot fields, REST write path).
- events/presence.md — presence stream inside the auction room, including floor presence.
- events/company.md — staff (company) role usage.
- rate-limits.md —
auction.bid.place100/min. - errors.md — error codes.