diff --git a/src/main/client/src/feature/lobby/Lobby.jsx b/src/main/client/src/feature/lobby/Lobby.jsx
index ee1ebdc..7453c8b 100644
--- a/src/main/client/src/feature/lobby/Lobby.jsx
+++ b/src/main/client/src/feature/lobby/Lobby.jsx
@@ -1,6 +1,7 @@
 import {
   useRef,
   useState,
+  useEffect,
   useContext,
   useCallback,
 } from "react"
@@ -38,6 +39,7 @@ import {
 import {
   getZindex,
   setNewGameOpen,
+  setOpenGameId,
   handleLobbyClick,
   closeLobbyPopup,
   initialState,
@@ -59,26 +61,26 @@ export function Lobby() {
   let [lobbyState, setLobbyState] = useState(initialState())
   let zNewGame = getZindex(lobbyState, "newgame")
   let [detail, setDetail] = useState("open")
-  let stompClient = useContext(StompContext)
   let navigate = useNavigate()
   let auth = useAuthStore(state => state.auth)
   let newGameRef = useRef()
-  let onNewGame = useCallback((d) => doTry(async () => {
-    let response = await tfetch("/api/create", {
-      method: "POST",
-      headers: {
-        "Authorization": "Bearer " + auth.token,
-        "Content-Type": "application/json",
-      },
-      body: JSON.stringify(d),
+  let stompClient = useContext(StompContext)
+  let initialized = useRef()
+  useEffect(() => {
+    if (initialized.current) {
+      return
+    }
+    initialized.current = true
+    let sub = stompClient.subscribe("/topic/gamestart", (message) => {
+      let r = JSON.parse(message.body)
+      if (r.opponent === auth.name) {
+        navigate(base + "/game/" + r.id)
+      }
     })
-    let sub = stompClient.subscribe("/topic/game/" + response.id, (message) => {
-      let game = JSON.parse(message.body)
-      navigate(base + "/game/" + game.id)
+    return () => {
       sub.unsubscribe()
-    })
-    setLobbyState(closeLobbyPopup(lobbyState))
-  }), [auth.token, navigate, stompClient, lobbyState])
+    }
+  }, [auth, initialized, stompClient, navigate])
   let onStartEdit = useCallback((d) => doTry(async () => {
     let response = await tfetch("/api/start_edit", {
       method: "POST",
@@ -100,7 +102,6 @@ export function Lobby() {
           lobbyState={lobbyState}
           setLobbyState={setLobbyState}
           newGameRef={newGameRef}
-          onNewGame={onNewGame}
           onStartEdit={onStartEdit} />
         <button disabled={zNewGame !== 0}  className={twJoin(
             "ml-2 border-2 border-transparent px-4 py-2 rounded-lg",
@@ -131,10 +132,23 @@ export function Lobby() {
   )
 }
 
-function NewGameDialog({zNewGame, lobbyState, setLobbyState, onNewGame, onStartEdit, newGameRef}) {
+function NewGameDialog({zNewGame, lobbyState, setLobbyState, onStartEdit, newGameRef}) {
   let dimRef = useRef(9)
   let timeRef = useRef(10)
   let [edit, setEdit] = useState(false)
+  let auth = useAuthStore(state => state.auth)
+  let onNewGame = useCallback((d) => doTry(async () => {
+    let response = await tfetch("/api/create", {
+      method: "POST",
+      headers: {
+        "Authorization": "Bearer " + auth.token,
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(d),
+    })
+    let newState = closeLobbyPopup(lobbyState)
+    setLobbyState(setOpenGameId(newState, response.id))
+  }), [auth.token, lobbyState, setLobbyState])
   return (
     <form onSubmit={(e) => {
         e.preventDefault()
diff --git a/src/main/client/src/feature/lobby/OpenGames.jsx b/src/main/client/src/feature/lobby/OpenGames.jsx
index 9ab7fb7..c2f89d6 100644
--- a/src/main/client/src/feature/lobby/OpenGames.jsx
+++ b/src/main/client/src/feature/lobby/OpenGames.jsx
@@ -3,7 +3,6 @@ import {
   useState,
   useEffect,
   useContext,
-  useCallback,
 } from "react"
 import {
   FaAngleLeft,
@@ -15,9 +14,6 @@ import {
 import {
   twJoin,
 } from "tailwind-merge"
-import {
-  useNavigate,
-} from "react-router-dom"
 import {
   Form,
 } from "src/component/Form.jsx"
@@ -28,7 +24,6 @@ import {
   BabyStone,
 } from "src/component/BabyStone.jsx"
 import {
-  base,
   StompContext,
   tfetch,
   doTry,
@@ -37,6 +32,7 @@ import {
 import {
   getZindex,
   getAcceptData,
+  closeLobbyPopup,
   setAcceptDialogOpen,
 } from "./lobbyState.js"
 import {
@@ -47,7 +43,6 @@ export function OpenGames({lobbyState, setLobbyState}) {
   let [openGames, setOpenGames] = useState([])
   let acceptableGame = getAcceptData(lobbyState)
   let stompClient = useContext(StompContext)
-  let navigate = useNavigate()
   let auth = useAuthStore(state => state.auth)
   let initialized = useRef()
   let acceptDialogRef = useRef()
@@ -71,18 +66,7 @@ export function OpenGames({lobbyState, setLobbyState}) {
     return () => {
       sub1.unsubscribe()
     }
-  }, [auth, initialized, stompClient, navigate])
-  let onAccept = useCallback((d) => doTry(async () => {
-    await tfetch("/api/accept", {
-      method: "POST",
-      headers: {
-        "Authorization": "Bearer " + auth.token,
-        "Content-Type": "application/json",
-      },
-      body: JSON.stringify(d),
-    })
-    navigate(base + "/game/" + d.game.id)
-  }), [auth, navigate])
+  }, [auth, initialized, stompClient])
   return (
     <div>
       <div className="grid grid-cols-[max-content_max-content_max-content]">
@@ -96,10 +80,10 @@ export function OpenGames({lobbyState, setLobbyState}) {
             key={game.id} />
         ))}
       </div>
-      <AcceptDialog
+      <ChallengeDialog
         lobbyState={lobbyState}
+        setLobbyState={setLobbyState}
         acceptableGame={acceptableGame}
-        onAccept={onAccept}
         acceptDialogRef={acceptDialogRef} />
     </div>
   )
@@ -138,7 +122,7 @@ function OpenGame({game, onClick}) {
   )
 }
 
-function AcceptDialog({lobbyState, onAccept, acceptableGame, acceptDialogRef}) {
+function ChallengeDialog({lobbyState, setLobbyState, acceptableGame, acceptDialogRef}) {
   let [isFlip, setFlip] = useState(false)
   let [handi, setHandi] = useState(1)
   let auth = useAuthStore(state => state.auth)
@@ -146,10 +130,20 @@ function AcceptDialog({lobbyState, onAccept, acceptableGame, acceptDialogRef}) {
   return (
     <Form
       forwardedRef={acceptDialogRef}
-      onSubmit={() => onAccept({
-        game: acceptableGame?.game,
-        flip: isFlip,
-        handicap: handi === 1 ? 0 : handi,
+      onSubmit={() => doTry(async () => {
+        await tfetch("/api/challenge", {
+          method: "POST",
+          headers: {
+            "Authorization": "Bearer " + auth.token,
+            "Content-Type": "application/json",
+          },
+          body: JSON.stringify({
+            game: acceptableGame?.game,
+            flip: isFlip,
+            handicap: handi === 1 ? 0 : handi,
+          }),
+        })
+        setLobbyState(closeLobbyPopup(lobbyState))
       })}
       style={{
         zIndex: zAccept,
diff --git a/src/main/client/src/feature/lobby/Requests.jsx b/src/main/client/src/feature/lobby/Requests.jsx
index f1b0e39..e7aeea9 100644
--- a/src/main/client/src/feature/lobby/Requests.jsx
+++ b/src/main/client/src/feature/lobby/Requests.jsx
@@ -38,7 +38,7 @@ export function Requests({lobbyState}) {
           "Authorization": "Bearer " + auth.token,
         },
       })
-      setRequests(r.requests.filter(request => request.gameId === openGameId))
+      setRequests(r.requests)
     })
     let sub1 = stompClient.subscribe("/topic/lobby/requests", (message) => {
       let r = JSON.parse(message.body)
@@ -53,10 +53,9 @@ export function Requests({lobbyState}) {
   }
   return (
     <div>
-      <div className="grid grid-cols-[max-content_max-content_max-content_max-content]">
+      <div className="grid grid-cols-[max-content_max-content_max-content_max-content_max-content]">
         {requests.map((request) => (
           <Request
-            lobbyState={lobbyState}
             request={request}
             key={request.id} />
         ))}
@@ -65,45 +64,44 @@ export function Requests({lobbyState}) {
   )
 }
 
-function Request({lobbyState, request}) {
+function Request({request}) {
   let navigate = useNavigate()
   let auth = useAuthStore(state => state.auth)
-  let openGameId = lobbyState.openGameId
-  let classes = twJoin(
-    "contents",
-    "*:py-3",
-    "cursor-pointer *:hover:bg-sky-200 *:hover:text-black",
-  )
   return (
     <div
       onClick={() => {
         doTry(async () => {
-          let r = await tfetch("/api/lobby/start", {
+          await tfetch("/api/lobby/start", {
+            method: "POST",
             headers: {
-              "method": "POST",
               "Authorization": "Bearer " + auth.token,
+              "Content-Type": "application/json",
             },
-            body: JSON.stringify({
-              gameId: openGameId,
-              request: request.id,
-            }),
+            body: JSON.stringify(request),
           })
-          navigate(base + "/game/" + r.id)
+          navigate(base + "/game/" + request.game.id)
         })
       }}
-      className={classes}
-      key={request.id}>
+      className={twJoin(
+        "contents",
+        "*:py-3",
+        "cursor-pointer *:hover:bg-sky-200 *:hover:text-black",
+      )}
+      key={request.game.id}>
       <div className="pl-3 pr-1 rounded-l-lg">
-        {request.white}
+        {request.flip ? "B" : "W"}: {request.opponent}
+      </div>
+      <div className="px-1">
+        {request.flip ? "W" : "B"}: {request.game.user}
       </div>
       <div className="px-1">
-        {request.black}
+        {request.game.dim}x{request.game.dim}
       </div>
       <div className="px-1">
-        {request.dim}x{request.dim}
+        T: {request.game.timesetting}
       </div>
       <div className="pl-1 pr-3 rounded-r-lg">
-        H{request.handi}
+        H: {request.handicap}
       </div>
     </div>
   )
diff --git a/src/main/java/com/bernd/GameController.java b/src/main/java/com/bernd/GameController.java
index 8a70cee..f17f131 100644
--- a/src/main/java/com/bernd/GameController.java
+++ b/src/main/java/com/bernd/GameController.java
@@ -12,6 +12,10 @@
 import com.bernd.model.ViewGame;
 import com.bernd.util.RandomString;
 import com.bernd.util.SgfCreator;
+import java.security.Principal;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Map;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -25,9 +29,6 @@
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.server.ResponseStatusException;
 
-import java.security.Principal;
-import java.time.LocalDate;
-
 import static com.bernd.util.Auth.getPrincipal;
 import static com.bernd.util.Util.COLORS;
 
@@ -140,24 +141,43 @@ public OpenGame newGame(@RequestBody OpenGame game) {
     return result;
   }
 
-  @PostMapping(value = "/api/accept", consumes = "application/json")
-  public ResponseEntity<?> accept(@RequestBody AcceptRequest acceptRequest) {
+  @PostMapping(value = "/api/lobby/start", consumes = "application/json")
+  public ResponseEntity<?> start(@RequestBody AcceptRequest acceptRequest) {
     String principal = getPrincipal();
-    openGames.remove(principal);
-    OpenGame openGame = openGames.remove(acceptRequest.game().user());
-    Game fullGame = games.put(openGame.accept(principal, acceptRequest));
+    openGames.remove(acceptRequest.opponent());
+    OpenGame openGame = openGames.remove(principal);
+    Game fullGame = games.put(openGame.accept(acceptRequest));
     activeGames.put(ActiveGame.fromGame(fullGame));
     Chat chat = chats.get(openGame.id());
 
     ChatMessage startMessage = ChatMessage.createStartMessage(chat, fullGame);
     chat.messages().add(startMessage);
     operations.convertAndSend("/topic/chat/" + chat.id(), startMessage);
-    operations.convertAndSend("/topic/game/" + fullGame.id(), fullGame.toView());
+    operations.convertAndSend("/topic/gamestart", Map.of(
+        "opponent", acceptRequest.opponent(),
+        "id", openGame.id()));
     operations.convertAndSend("/topic/lobby/open_games", openGames.games());
     operations.convertAndSend("/topic/lobby/active_games", activeGames.games());
     return ResponseEntity.ok().build();
   }
 
+  @ResponseBody
+  @PostMapping(value = "/api/challenge", consumes = "application/json")
+  public Map<String, List<AcceptRequest>> challenge(@RequestBody AcceptRequest acceptRequest) {
+    String principal = getPrincipal();
+    openGames.remove(principal);
+    OpenGame openGame = openGames.addRequest(acceptRequest.game().user(), acceptRequest, principal);
+    operations.convertAndSend("/topic/lobby/requests", Map.of("requests", openGame.requests()));
+    return Map.of("requests", openGame.requests());
+  }
+
+  @ResponseBody
+  @GetMapping(value = "/api/lobby/requests")
+  public Map<String, List<AcceptRequest>> getRequests() {
+    String principal = getPrincipal();
+    return Map.of("requests", openGames.getRequests(principal));
+  }
+
   @GetMapping("/api/sgf/{id}/{black}_vs_{white}.sgf")
   public ResponseEntity<String> getSgf(
       @PathVariable String id) {
diff --git a/src/main/java/com/bernd/OpenGames.java b/src/main/java/com/bernd/OpenGames.java
index a663630..2ab4ce4 100644
--- a/src/main/java/com/bernd/OpenGames.java
+++ b/src/main/java/com/bernd/OpenGames.java
@@ -1,12 +1,12 @@
 package com.bernd;
 
+import com.bernd.model.AcceptRequest;
 import com.bernd.model.OpenGame;
 import com.bernd.model.OpenGameList;
-import org.springframework.stereotype.Component;
-
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import org.springframework.stereotype.Component;
 
 @Component
 public class OpenGames {
@@ -17,6 +17,16 @@ OpenGame put(OpenGame game) {
     return game;
   }
 
+  OpenGame addRequest(String name, AcceptRequest request, String opponent) {
+    OpenGame openGame = map.get(name);
+    if (openGame == null) {
+      return null;
+    }
+    OpenGame result = openGame.addRequest(request, opponent);
+    map.put(name, result);
+    return result;
+  }
+
   OpenGameList games() {
     return new OpenGameList(List.copyOf(map.values()));
   }
@@ -24,4 +34,12 @@ OpenGameList games() {
   OpenGame remove(String name) {
     return map.remove(name);
   }
+
+  List<AcceptRequest> getRequests(String name) {
+    OpenGame openGame = map.get(name);
+    if (openGame == null) {
+      return List.of();
+    }
+    return openGame.requests();
+  }
 }
diff --git a/src/main/java/com/bernd/model/OpenGame.java b/src/main/java/com/bernd/model/OpenGame.java
index ae111ba..885b63e 100644
--- a/src/main/java/com/bernd/model/OpenGame.java
+++ b/src/main/java/com/bernd/model/OpenGame.java
@@ -1,6 +1,7 @@
 package com.bernd.model;
 
 import com.bernd.game.MoveList;
+import java.util.ArrayList;
 import java.util.List;
 
 import static com.bernd.LobbyController.createEmptyBoard;
@@ -20,6 +21,13 @@ public OpenGame withUser(String user) {
     return new OpenGame(id, user, requests, dim, timesetting).sanitize();
   }
 
+  public OpenGame addRequest(AcceptRequest request, String opponent) {
+    List<AcceptRequest> updated = new ArrayList<>(requests.size() + 1);
+    updated.addAll(requests);
+    updated.add(request.withOpponent(opponent));
+    return new OpenGame(id, user, updated, dim, timesetting);
+  }
+
   private OpenGame sanitize() {
     if (requests == null) {
       return new OpenGame(id, user, List.of(), dim, timesetting);
@@ -27,9 +35,9 @@ private OpenGame sanitize() {
     return this;
   }
 
-  public Game accept(String opponent, AcceptRequest acceptRequest) {
-    String userBlack = acceptRequest.flip() ? opponent : user;
-    String userWhite = acceptRequest.flip() ? user : opponent;
+  public Game accept(AcceptRequest acceptRequest) {
+    String userBlack = acceptRequest.flip() ? acceptRequest.opponent() : user;
+    String userWhite = acceptRequest.flip() ? user : acceptRequest.opponent();
     return new Game(
         id,
         userBlack,