Skip to main content

Draft clicker

The draft clicker makes one pick in the ESPN draft room. It prefers handing the pick to the live draft sync session, because a second connection for the same team evicts the first.

What starts it?​

The draft-day optimizer calls the core API's POST /v1/trigger/draft-pick. The API checks that the pick slot is still open and unexpired, stores the action, and publishes to draft_targets.

What does the message contain?​

FieldRequiredNotes
tracking_idNoMessage identity.
team_idYesNumeric string.
player_idYesESPN id. A negative id is a D/ST.
nameYesDisplay name, for logs and delegation.
position, pro_teamSent by coreFor a name match against ESPN's player pool.
league_idNoPreferred over ESPN_LEAGUE_ID, so a practice room is not mistaken for the live league.
pick_numberNoForwarded to the rejection notice.
expires_atNoUnix seconds. Past it, the worker skips the message.
modeNolive or practice. A practice pick defaults to a real submit.
idempotency_key, traceparentNoForwarded on a rejection notice.

What does it read and write?​

StepSurface
Read the boardESPN REST mDraftDetail through readDraftPicks
Check ownershipRedis draft:{leagueId}:health lease
DelegateRedis stream draft_commands:{leagueId}, result on draft_commands:{leagueId}:results
SubmitSELECT <playerId> frame on the draft-room WebSocket
FallbackA separate Chromium click on the draft board

The WebSocket protocol is in the draft room notes.

How does it verify?​

The bot waits up to 5 s for ESPN's SELECTED frame for the same player and team. A matching frame is recorded as a draft_pick_click row in execution_log with status: "success". The core API reads that row back for the draft status.

What can go wrong?​

FailureResult
Missing or non-numeric team_id, player_id or namePermanentError
Player drafted by another teamPermanentError
ESPN sends an ERROR frame for the pickPermanentError. The bot also publishes a pick-rejection notice on Redis so core can try the next candidate within the same pick clock.
No SELECTED within 5 s on both the socket and the DOM fallbackPlain Error, requeued. The third consecutive timeout for the same target becomes a PermanentError, because the room is gone.
Cookies rejectedSessionExpiredError
League not found (HTTP 404)PermanentError
Redis delegation failsFalls back to a direct session

DRY_RUN defaults to dry run. A dry run still writes a skipped_dry_run row so the draft status shows it. A practice message defaults to live, and an explicit DRY_RUN always wins.