diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
 
 All notable changes to the LaunchDarkly Haskell Server-side SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org).
 
+## [2.0.0] - 2020-04-21
+### Changed:
+- The function `userSetKey` is now takes `Text` instead of `Maybe Text` for the key. This originally existed for compatibility with an internal test suite.
+
+### Removed:
+- The constructor `EvalErrorUserNotSpecified` has been removed from `EvalErrorKind`.
+
 ## [1.0.4] - 2020-04-06
 ### Fixed:
 - Widened time and lens dependency ranges. Thanks @nbouscal!
diff --git a/launchdarkly-server-sdk.cabal b/launchdarkly-server-sdk.cabal
--- a/launchdarkly-server-sdk.cabal
+++ b/launchdarkly-server-sdk.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 161f412cb18d22ca3dbf8912fdfd91f3f8a844d9b0ef1d9d61ab1105f61c243a
+-- hash: 19d9bbded7c024d267d0d6dc115cbc0e8fd60ae0e82b84bb73b68c68f60e3d73
 
 name:           launchdarkly-server-sdk
-version:        1.0.4
+version:        2.0.0
 synopsis:       Server-side SDK for integrating with LaunchDarkly
 description:    Please see the README on GitHub at <https://github.com/launchdarkly/haskell-server-sdk#readme>
 category:       Web
diff --git a/src/LaunchDarkly/Server/Client.hs b/src/LaunchDarkly/Server/Client.hs
--- a/src/LaunchDarkly/Server/Client.hs
+++ b/src/LaunchDarkly/Server/Client.hs
@@ -38,7 +38,6 @@
 import           Data.Aeson                            (Value(..))
 import           Data.Generics.Product                 (getField, setField)
 import           Data.Scientific                       (toRealFloat, fromFloatDigits)
-import           Data.Maybe                            (isNothing)
 import           Network.HTTP.Client                   (newManager)
 import           Network.HTTP.Client.TLS               (tlsManagerSettings)
 import           System.Clock                          (TimeSpec(..))
@@ -95,7 +94,7 @@
 -- will be returned. This method does not send analytics events back to
 -- LaunchDarkly.
 allFlags :: Client -> User -> IO (HashMap Text Value)
-allFlags (Client client) (User user) = if isNothing $ getField @"key" user then pure mempty else do
+allFlags (Client client) (User user) = do
     status <- getAllFlagsC $ getField @"store" client
     case status of
         Left _      -> pure HM.empty
diff --git a/src/LaunchDarkly/Server/Client/Internal.hs b/src/LaunchDarkly/Server/Client/Internal.hs
--- a/src/LaunchDarkly/Server/Client/Internal.hs
+++ b/src/LaunchDarkly/Server/Client/Internal.hs
@@ -25,7 +25,7 @@
 
 -- | The version string for this library.
 clientVersion :: Text
-clientVersion = "1.0.4"
+clientVersion = "2.0.0"
 
 -- | The status of the client initialization.
 data Status
diff --git a/src/LaunchDarkly/Server/Details.hs b/src/LaunchDarkly/Server/Details.hs
--- a/src/LaunchDarkly/Server/Details.hs
+++ b/src/LaunchDarkly/Server/Details.hs
@@ -82,9 +82,6 @@
     | EvalErrorWrongType
       -- ^ Indicates that the result value was not of the requested type, e.g.
       -- you called boolVariationDetail but the value was an integer.
-    | EvalErrorUserNotSpecified
-      -- ^ Indicates that the caller passed a user without a key for the user
-      -- parameter.
     | EvalErrorClientNotReady
       -- ^ Indicates that the caller tried to evaluate a flag before the client
       -- had successfully initialized.
@@ -97,6 +94,5 @@
         EvalErrorKindMalformedFlag -> "MALFORMED_FLAG"
         EvalErrorFlagNotFound      -> "FLAG_NOT_FOUND"
         EvalErrorWrongType         -> "WRONG_TYPE"
-        EvalErrorUserNotSpecified  -> "USER_NOT_SPECIFIED"
         EvalErrorClientNotReady    -> "CLIENT_NOT_READY"
         EvalErrorExternalStore _   -> "EXTERNAL_STORE_ERROR"
diff --git a/src/LaunchDarkly/Server/Evaluate.hs b/src/LaunchDarkly/Server/Evaluate.hs
--- a/src/LaunchDarkly/Server/Evaluate.hs
+++ b/src/LaunchDarkly/Server/Evaluate.hs
@@ -55,9 +55,7 @@
             let event = newUnknownFlagEvent key fallback (EvaluationReasonError EvalErrorFlagNotFound)
             pure (errorDetail EvalErrorFlagNotFound, True, pure event)
         Right (Just flag) -> do
-            (reason, events) <- case getField @"key" user of
-                Nothing -> pure (errorDetail EvalErrorUserNotSpecified, [])
-                Just _  -> evaluateDetail flag user $ getField @"store" client
+            (reason, events) <- evaluateDetail flag user $ getField @"store" client
             let reason' = setFallback reason fallback
             pure (reason', False, flip (:) events $ newSuccessfulEvalEvent flag (getField @"variationIndex" reason')
                 (getField @"value" reason') (Just fallback) (getField @"reason" reason') Nothing)
@@ -111,7 +109,7 @@
 
 evaluateInternal :: (Monad m, LaunchDarklyStoreRead store m) => Flag -> UserI -> store -> m (EvaluationDetail Value)
 evaluateInternal flag user store = result where
-    checkTarget target = if elem (getField @"key" user) (Just <$> getField @"values" target)
+    checkTarget target = if elem (getField @"key" user) (getField @"values" target)
         then Just $ getVariation flag (getField @"variation" target) EvaluationReasonTargetMatch else Nothing
     checkRule (ruleIndex, rule) = ifM (ruleMatchesUser rule user store)
         (pure $ Just $ getValueForVariationOrRollout flag (getField @"variationOrRollout" rule) user
@@ -214,9 +212,8 @@
 
 segmentContainsUser :: Segment -> UserI -> Bool
 segmentContainsUser segment user
-    | Nothing <- getField @"key" user = False
-    | elem (fromJust $ getField @"key" user) (getField @"included" segment) = True
-    | elem (fromJust $ getField @"key" user) (getField @"excluded" segment) = False
+    | elem (getField @"key" user) (getField @"included" segment) = True
+    | elem (getField @"key" user) (getField @"excluded" segment) = False
     | Just _ <- find
         (\r -> segmentRuleMatchesUser r user (getField @"key" segment) (getField @"salt" segment))
         (getField @"rules" segment) = True
diff --git a/src/LaunchDarkly/Server/Events.hs b/src/LaunchDarkly/Server/Events.hs
--- a/src/LaunchDarkly/Server/Events.hs
+++ b/src/LaunchDarkly/Server/Events.hs
@@ -107,7 +107,7 @@
     } deriving (Generic, Show, ToJSON)
 
 data IdentifyEvent = IdentifyEvent
-    { key  :: !(Maybe Text)
+    { key  :: !Text
     , user :: !Value
     } deriving (Generic, ToJSON, Show)
 
@@ -156,7 +156,7 @@
 addUserToEvent :: (HasField' "user" r (Maybe Value), HasField' "userKey" r (Maybe Text)) => ConfigI -> UserI -> r -> r
 addUserToEvent config user event = if getField @"inlineUsersInEvents" config
     then setField @"user" (pure $ userSerializeRedacted config user) event
-    else setField @"userKey" (getField @"key" user) event
+    else setField @"userKey" (pure $ getField @"key" user) event
 
 makeFeatureEvent :: ConfigI -> UserI -> Bool -> EvalEvent -> FeatureEvent
 makeFeatureEvent config user includeReason event = addUserToEvent config user $ FeatureEvent
@@ -306,7 +306,7 @@
 
 maybeIndexUser :: Natural -> ConfigI -> UserI -> EventState -> IO ()
 maybeIndexUser now config user state = modifyMVar_ (getField @"userKeyLRU" state) $ \cache ->
-    let key = fromMaybe "" $ getField @"key" user in case LRU.lookup key cache of
+    let key = getField @"key" user in case LRU.lookup key cache of
         (cache', Just _)  -> pure cache'
         (cache', Nothing) -> do
             queueEvent config state (EventTypeIndex $ BaseEvent now $ IndexEvent { user = userSerializeRedacted config user })
diff --git a/src/LaunchDarkly/Server/User.hs b/src/LaunchDarkly/Server/User.hs
--- a/src/LaunchDarkly/Server/User.hs
+++ b/src/LaunchDarkly/Server/User.hs
@@ -28,7 +28,7 @@
 -- | Creates a new user identified by the given key.
 makeUser :: Text -> User
 makeUser key = User $ UserI
-    { key                   = pure key
+    { key                   = key
     , secondary             = mempty
     , ip                    = mempty
     , country               = mempty
@@ -43,7 +43,7 @@
     }
 
 -- | Set the primary key for a user.
-userSetKey :: (Maybe Text) -> User -> User
+userSetKey :: Text -> User -> User
 userSetKey = mapUser . setField @"key"
 
 -- | Set the secondary key for a user.
diff --git a/src/LaunchDarkly/Server/User/Internal.hs b/src/LaunchDarkly/Server/User/Internal.hs
--- a/src/LaunchDarkly/Server/User/Internal.hs
+++ b/src/LaunchDarkly/Server/User/Internal.hs
@@ -6,7 +6,7 @@
     , userSerializeRedacted
     ) where
 
-import           Data.Aeson                          (FromJSON, ToJSON, Value(..), (.:?), withObject, object, parseJSON, toJSON)
+import           Data.Aeson                          (FromJSON, ToJSON, Value(..), (.:), (.:?), withObject, object, parseJSON, toJSON)
 import           Data.Foldable                       (fold, or)
 import           Data.Generics.Product               (getField)
 import qualified Data.HashMap.Strict as              HM
@@ -30,7 +30,7 @@
 newtype User = User { unwrapUser :: UserI }
 
 data UserI = UserI
-    { key                   :: !(Maybe Text)
+    { key                   :: !Text
     , secondary             :: !(Maybe Text)
     , ip                    :: !(Maybe Text)
     , country               :: !(Maybe Text)
@@ -52,7 +52,7 @@
 
 instance FromJSON UserI where
     parseJSON = withObject "User" $ \o -> UserI
-        <$> o .:? "key"
+        <$> o .:  "key"
         <*> o .:? "secondary"
         <*> o .:? "ip"
         <*> o .:? "country"
@@ -83,7 +83,7 @@
 
 valueOf :: UserI -> Text -> Maybe Value
 valueOf user attribute = case attribute of
-    "key"       -> String <$> getField @"key" user
+    "key"       -> pure $ String $ getField @"key" user
     "secondary" -> String <$> getField @"secondary" user
     "ip"        -> String <$> getField @"ip" user
     "country"   -> String <$> getField @"country" user
