Auction Lifecycle Control — Frontend Integration Guide¶
This guide is the complete, frontend-facing contract for starting, stopping (ending), pausing and resuming a live auction from the company console and the superadmin console.
Both consoles share exactly the same contract. The only difference is the REST mount and the role token used. The live actions themselves (start / pause / resume / end) are WebSocket staff commands — there is no REST endpoint that starts, pauses, resumes or ends an auction.
Audience: frontend engineers integrating the auction console.
Related docs:
1. TL;DR¶
| Action | Transport | Command / endpoint | Success event |
|---|---|---|---|
| Start | WebSocket | auction.status.update → { status: "START" } | auction.state.changed transition: "started" |
| Pause | WebSocket | auction.pause → { reason } | auction.state.changed transition: "paused" |
| Resume | WebSocket | auction.resume → { reason } | auction.state.changed transition: "resumed" |
| End | WebSocket | auction.end → { reason } | auction.state.changed transition: "closed" |
| Read | REST | GET /v2/companies\|admin/auctions/:auctionId | console overview payload |
| Audit | REST | GET /v2/companies\|admin/auctions/:auctionId/action-logs | action log list |
| Arm | REST | PATCH /v2/companies\|admin/auctions/:auctionId/schedule | schedule payload |
| Approve | REST | POST /v2/companies\|admin/auctions/:auctionId/approve | mutation envelope — only when approvalRequired is true |
Roles: COMPANY and SUPERADMIN. Both are "staff" in the auction room and require the auction:staff permission.
2. Lifecycle model¶
2.1 Status machine (auctionStatus)¶
SCHEDULED ──(approve)──▶ READY ──start──▶ LIVE ──pause──▶ PAUSED ──resume──▶ LIVE
│ │
└──────────end─────────────────┴──▶ ENDED
(approve) applies only when the resolved policy sets approvalRequired: true; otherwise the auction starts directly from SCHEDULED.
Current auctionStatus | Start | Pause | Resume | End |
|---|---|---|---|---|
SCHEDULED | ✅ | ✕ | ✕ | ✕ |
READY | ✅ | ✕ | ✕ | ✕ |
LIVE | ✕ | ✅ | ✕ | ✅ |
PAUSED | ✕ | ✕ | ✅ | ✅ |
ENDED | ✕ | ✕ | ✕ | ✕ |
CANCELLED | ✕ | ✕ | ✕ | ✕ |
End is allowed from both LIVE and PAUSED. A failed command returns an error frame with code: "CONFLICT" (or BAD_REQUEST) and a safe message — drive your button states from the current status so invalid actions are unreachable.
2.2 auctionStatus and prebidPhase¶
The console overview (GET .../auctions/:auctionId) exposes the status fields that drive the control buttons:
auctionStatus—PENDING/SCHEDULED/READY/LIVE/PAUSED/ENDED/COMPLETE/CANCELLED.prebidPhase—NONE/OPEN/CLOSED.LIVE/PAUSED/ENDEDareauctionStatusvalues, notprebidPhasevalues.- Migration note: the old derived "review status" and "availability phase" fields are removed, as is the "live decision" approval endpoint. Drive control logic from
auctionStatusplusprebidPhaseonly, and usePOST .../approvefor approval.
Recommended button visibility:
| Button | Visible when |
|---|---|
| Start | auctionStatus === 'SCHEDULED' (READY when approvalRequired is true) and prebidPhase === 'CLOSED' — i.e. after the prebid window closed and, if approval is required, after POST .../approve; never while prebid is OPEN (see Manual prebid controls) |
| Pause | auctionStatus === 'LIVE' |
| Resume | auctionStatus === 'PAUSED' |
| End | auctionStatus === 'LIVE' or auctionStatus === 'PAUSED' |
3. Setup: connect the console WebSocket¶
All four live actions travel over one socket. Open wss://api.example.com/ws (see WebSocket frontend integration for auth details — browsers send the token via Sec-WebSocket-Protocol).
Example (browser):
const token = '<access-token>';
const ws = new WebSocket(`wss://api.example.com/ws`, `websocket.v1, bearer.${token}`);
ws.onopen = () => {
ws.send(
JSON.stringify({
id: 'subscribe-001',
type: 'auction.subscribe',
data: { auctionId }
})
);
};
ws.onmessage = event => {
const frame = JSON.parse(event.data);
handleFrame(frame);
};
Staff members omit enrolledSubscriberId on subscribe; the server joins them to both auction:<auctionId> and auction:staff:<auctionId>. Fetch auctionId via REST before subscribing..
4. Start the auction¶
4.0 Approve the auction (only when approval required)¶
Approval is needed only when the resolved policy sets approvalRequired: true and auctionStatus === 'SCHEDULED'. When approvalRequired is false (the default) no approval is needed — start directly from SCHEDULED.
POST /v2/companies/auctions/:auctionId/approve (COMPANY token)
POST /v2/admin/auctions/:auctionId/approve (SUPERADMIN token)
Body: { reason? } (optional). On success the server sets auctionStatus = READY and writes an APPROVE action log. Response:
{
"data": {
"auctionId": "...",
"cycleId": "...",
"status": "READY",
"approvalRequired": true,
"auctionStartAt": "..."
}
}
The endpoint is valid only when approvalRequired === true AND auctionStatus === 'SCHEDULED'. Approving without the approval-required policy is rejected with 400: "This auction does not require approval. Approving an auction without the approval-required policy is not allowed."
4.1 Command¶
{
"id": "start-001",
"type": "auction.status.update",
"data": {
"auctionId": "auction-id",
"status": "START"
}
}
4.2 What happens¶
- Immediate frame:
command.acknowledged(no business data). - On success, the authoritative event arrives:
auction.state.changedwithdata.payload.transition: "started". - The server sets
auctionStatus = LIVE, persists the automatic end timestamp, writes aSTARTaction log, and enqueuesAUCTION_STARTEDnotifications. - A start is only accepted when
prebidPhase === 'CLOSED'— starting while the prebid window is open is rejected ("Close the prebid window before starting the live auction."). WhenapprovalRequiredis true the auction must already beREADY— starting fromSCHEDULEDis rejected ("Approve this auction before starting the live auction."). WhenapprovalRequiredis false (the default) no approval is needed and the auction starts directly fromSCHEDULED.
4.3 UI guidance¶
- Show a loading state from send until
transition: "started"(or anerrorframe). - Never flip the console to "live" on the
command.acknowledgedframe — the ack only means the command was accepted.
5. Pause the auction¶
{
"id": "pause-001",
"type": "auction.pause",
"data": {
"auctionId": "auction-id",
"reason": "network issue"
}
}
reasonis required, trimmed, 1–500 characters.- Requires
auctionStatus === 'LIVE'. - Success event:
auction.state.changedwithtransition: "paused"; readpausedAt,auctionEndAt,closingPhase, andclosingPhaseEndsAtfrom the completepayload.stateobject. - Bids submitted while paused are rejected (
AUCTION_NOT_LIVE); subscribers see the auction as paused.
6. Resume the auction¶
{
"id": "resume-001",
"type": "auction.resume",
"data": {
"auctionId": "auction-id",
"reason": "resolved"
}
}
reasonis required, trimmed, 1–500 characters.- Requires
auctionStatus === 'PAUSED'. - Success event:
auction.state.changedwithtransition: "resumed"; the completepayload.statehaspausedAt: nulland the shiftedauctionEndAt/closingPhaseEndsAtdeadline. The outer eventtimestampis the occurrence time; there is no rootpayload.resumedAtfield. - Bidding is re-enabled immediately on the server side; subscribers receive the same event.
7. End (stop) the auction¶
{
"id": "end-001",
"type": "auction.end",
"data": {
"auctionId": "auction-id",
"reason": "auction complete"
}
}
reasonis required, trimmed, 1–500 characters.- Requires
auctionStatus === 'LIVE'orauctionStatus === 'PAUSED'. - Success event:
auction.state.changedwithtransition: "closed". - Server sets
auctionStatus = ENDED, recalculates the next lifecycle transition, writes anENDaction log, and enqueuesAUCTION_ENDEDnotifications.
8. Reading state and auditing¶
Poll or reconcile the console with REST after each transition:
GET /v2/companies/auctions/:auctionId (COMPANY token)
GET /v2/admin/auctions/:auctionId (SUPERADMIN token)
The overview carries auctionStatus, prebidPhase, stateVersion, startedAt, pausedAt, endedAt, and the current leadingBid/bidCount/prebids/participants.
Audit trail — the same four actions are recorded here:
Relevant action values: APPROVE, START, AUTO_START, PAUSE, RESUME, END, AUTO_END.
9. Recommended client flow¶
1. Open /ws with an access token.
2. auction.subscribe { auctionId } (fetch via REST; row guaranteed before `READY`/`LIVE`)
3. Fetch GET .../auctions/:auctionId → seed control state.
4. On user action → send the WS command above.
5. On auction.state.changed → replace lifecycle fields from payload.state and
use payload.transition for transition-specific UI effects.
6. On error frame → surface the message, keep previous state.
7. On network loss → reconnect, resubscribe with lastSequence, re-fetch overview.
Event handling sketch¶
function handleFrame(frame: AuctionSocketFrame) {
if (frame.type === 'auction.state.changed') {
const t = frame.data?.payload?.transition;
const state = frame.data?.payload?.state;
if (state) replaceAuctionLifecycleState(state);
if (t === 'started') showAuctionStartedEffect();
if (t === 'closed') showAuctionEndedEffect();
}
if (frame.type === 'error') {
// frame.data.code in { BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, RATE_LIMITED, INTERNAL }
showToast(frame.data.message);
}
}
Reconnection¶
ws.onclose = () => {
const lastSequence = lastProcessedAuctionSequence; // from data.serverSequence
reconnect().then(() =>
ws.send(
JSON.stringify({
id: 'subscribe-002',
type: 'auction.subscribe',
data: { auctionId, lastSequence }
})
)
);
};
10. Error reference¶
| Code | Typical cause |
|---|---|
FORBIDDEN | Non-staff caller, or company does not own the auction. |
NOT_FOUND | Cycle/auction does not exist. |
CONFLICT | Invalid transition for current status (e.g. pause while not live). |
BAD_REQUEST | Missing/invalid fields (e.g. empty reason, missing auctionId). |
RATE_LIMITED | More than the per-event-type message limit. |
INTERNAL | Server error. |
Company users are scoped to auctions owned by their company; the server enforces ownership on every command. Superadmin tokens bypass the company scope.